aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Barta <sbarta@google.com>2012-03-01 12:35:35 -0800
committerScott Barta <sbarta@google.com>2012-03-01 12:40:08 -0800
commit59b2e6871c65f58fdad78cd7229c292f6a177578 (patch)
tree2d4e7bfc05b93f40b34675d77e403dd1c25efafd
parentf9b30489e75ac1eabc365064959804e99534f7ab (diff)
downloadjmonkeyengine-59b2e6871c65f58fdad78cd7229c292f6a177578.tar.gz
Adds the jMonkeyEngine library to the build.
Adds the jMonkeyEngine open source 3D game engine to the build. This is built as a static library and is only used by the Finsky client. Change-Id: I06a3f054df7b8a67757267d884854f70c5a16ca0
-rw-r--r--Android.mk20
-rw-r--r--MODULE_LICENSE_BSD0
-rw-r--r--NOTICE29033
-rw-r--r--README37
-rw-r--r--engine/src/android/com/jme3/app/AndroidHarness.java398
-rw-r--r--engine/src/android/com/jme3/app/R.java20
-rw-r--r--engine/src/android/com/jme3/asset/AndroidAssetManager.java115
-rw-r--r--engine/src/android/com/jme3/asset/AndroidImageInfo.java101
-rw-r--r--engine/src/android/com/jme3/asset/plugins/AndroidLocator.java90
-rw-r--r--engine/src/android/com/jme3/audio/android/AndroidAudioData.java62
-rw-r--r--engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java498
-rw-r--r--engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java19
-rw-r--r--engine/src/android/com/jme3/input/android/AndroidInput.java619
-rw-r--r--engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java19
-rw-r--r--engine/src/android/com/jme3/renderer/android/Android22Workaround.java14
-rw-r--r--engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java2951
-rw-r--r--engine/src/android/com/jme3/renderer/android/TextureUtil.java297
-rw-r--r--engine/src/android/com/jme3/system/android/AndroidConfigChooser.java362
-rw-r--r--engine/src/android/com/jme3/system/android/AndroidTimer.java96
-rw-r--r--engine/src/android/com/jme3/system/android/JmeAndroidSystem.java134
-rw-r--r--engine/src/android/com/jme3/system/android/OGLESContext.java445
-rw-r--r--engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java20
-rw-r--r--engine/src/android/com/jme3/util/AndroidLogHandler.java37
-rw-r--r--engine/src/android/com/jme3/util/FastInteger.java359
-rw-r--r--engine/src/android/com/jme3/util/RingBuffer.java75
-rw-r--r--engine/src/android/jme3test/android/AndroidManifest.xml29
-rw-r--r--engine/src/android/jme3test/android/DemoAndroidHarness.java54
-rw-r--r--engine/src/android/jme3test/android/DemoLaunchAdapter.java72
-rw-r--r--engine/src/android/jme3test/android/DemoLaunchEntry.java38
-rw-r--r--engine/src/android/jme3test/android/DemoMainActivity.java131
-rw-r--r--engine/src/android/jme3test/android/R.java46
-rw-r--r--engine/src/android/jme3test/android/SimpleSoundTest.java40
-rw-r--r--engine/src/android/jme3test/android/SimpleTexturedTest.java150
-rw-r--r--engine/src/android/jme3test/android/TestAmbient.java97
-rw-r--r--engine/src/android/jme3test/android/TestBumpModel.java95
-rw-r--r--engine/src/android/jme3test/android/TestMovingParticle.java102
-rw-r--r--engine/src/android/jme3test/android/TestNormalMapping.java99
-rw-r--r--engine/src/android/jme3test/android/TestSkyLoadingLagoon.java70
-rw-r--r--engine/src/android/jme3test/android/TestSkyLoadingPrimitives.java68
-rw-r--r--engine/src/android/jme3test/android/TestUnshadedModel.java44
-rw-r--r--engine/src/android/jme3tools/android/Fixed.java431
-rw-r--r--engine/src/android/res/layout/about.xml31
-rw-r--r--engine/src/android/res/layout/tests.xml25
-rw-r--r--engine/src/android/res/menu/options.xml12
-rw-r--r--engine/src/android/res/values/strings.xml6
-rw-r--r--engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag7
-rw-r--r--engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md16
-rw-r--r--engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert11
-rw-r--r--engine/src/blender/com/jme3/asset/BlenderKey.java730
-rw-r--r--engine/src/blender/com/jme3/asset/GeneratedTextureKey.java37
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java176
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java187
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java660
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java228
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java81
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java263
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java204
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java128
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java236
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java199
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java118
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/BlenderTrack.java147
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java147
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java43
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java43
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java120
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java203
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java162
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java122
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java137
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java43
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java37
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java44
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java114
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java180
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java92
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java98
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java131
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java44
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java42
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java302
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java136
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java589
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java76
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java383
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java208
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java157
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java316
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java199
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java180
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java316
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java120
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java26
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java483
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java588
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java105
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java535
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java393
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java247
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java174
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java52
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java195
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java91
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java101
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java415
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java467
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java196
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java135
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java875
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java416
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java168
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java131
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java135
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java191
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java148
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java137
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java106
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java125
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java171
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java216
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java481
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java294
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java408
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java226
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java195
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java51
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java162
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java96
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java81
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java221
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.datbin0 -> 19598 bytes
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/BulletAppState.java274
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/PhysicsTickListener.java54
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java25
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionListener.java47
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/collision/RagdollCollisionListener.java17
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java46
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/CharacterControl.java208
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/GhostControl.java178
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java867
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/PhysicsControl.java28
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/RigidBodyControl.java264
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/VehicleControl.java268
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java99
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollPreset.java106
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java268
-rw-r--r--engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java264
-rw-r--r--engine/src/bullet-native/android/Android.mk259
-rw-r--r--engine/src/bullet-native/android/Application.mk2
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.cpp450
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.h165
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp308
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.h173
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.cpp148
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.h87
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp59
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp68
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.cpp110
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.h45
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp107
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.h37
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp68
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp70
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp70
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.h29
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp59
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp69
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp70
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.h29
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp60
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp110
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.h45
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp57
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.cpp100
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.h37
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.cpp226
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.h101
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.cpp61
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.h29
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.cpp162
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.h69
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.cpp170
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.h69
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.cpp94
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.h61
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.cpp963
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.h469
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp365
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.h173
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp237
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.h109
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.cpp388
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.h215
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.cpp313
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.h167
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.cpp849
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.h415
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.cpp272
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.h143
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.cpp163
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.h69
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp138
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.h61
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.cpp152
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.h21
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.cpp59
-rw-r--r--engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.h21
-rw-r--r--engine/src/bullet-native/jmeBulletUtil.cpp327
-rw-r--r--engine/src/bullet-native/jmeBulletUtil.h61
-rw-r--r--engine/src/bullet-native/jmeClasses.cpp250
-rw-r--r--engine/src/bullet-native/jmeClasses.h99
-rw-r--r--engine/src/bullet-native/jmeMotionState.cpp89
-rw-r--r--engine/src/bullet-native/jmeMotionState.h57
-rw-r--r--engine/src/bullet-native/jmePhysicsSpace.cpp275
-rw-r--r--engine/src/bullet-native/jmePhysicsSpace.h76
-rw-r--r--engine/src/bullet/com/jme3/bullet/PhysicsSpace.java921
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java247
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java58
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionObject.java324
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/PhysicsRayTestResult.java82
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java91
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java91
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java129
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/CollisionShape.java130
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java158
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java81
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java121
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java168
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java145
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java98
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java161
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java66
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java100
-rw-r--r--engine/src/bullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java91
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/ConeJoint.java136
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/HingeJoint.java189
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/PhysicsJoint.java147
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/Point2PointJoint.java126
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/SixDofJoint.java230
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/SixDofSpringJoint.java92
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/SliderJoint.java538
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java169
-rw-r--r--engine/src/bullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java129
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java319
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java301
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java752
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java584
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java423
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java163
-rw-r--r--engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java45
-rw-r--r--engine/src/bullet/com/jme3/bullet/util/DebugMeshCallback.java61
-rw-r--r--engine/src/bullet/com/jme3/bullet/util/DebugShapeFactory.java123
-rw-r--r--engine/src/bullet/com/jme3/bullet/util/NativeMeshUtil.java77
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag24
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md21
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag47
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md36
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag50
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag25
-rw-r--r--engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md21
-rw-r--r--engine/src/core-data/Common/MatDefs/Gui/Gui.frag16
-rw-r--r--engine/src/core-data/Common/MatDefs/Gui/Gui.j3md26
-rw-r--r--engine/src/core-data/Common/MatDefs/Gui/Gui.vert29
-rw-r--r--engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag65
-rw-r--r--engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md31
-rw-r--r--engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag31
-rw-r--r--engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md23
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Deferred.frag146
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Deferred.j3md61
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Deferred.vert10
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/GBuf.frag86
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/GBuf.vert71
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Glow.frag32
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Lighting.frag278
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Lighting.j3md238
-rw-r--r--engine/src/core-data/Common/MatDefs/Light/Lighting.vert207
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag9
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md20
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert11
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Particle.frag22
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Particle.j3md89
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Particle.vert42
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag5
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md10
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert11
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag27
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md32
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert11
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Sky.frag11
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Sky.j3md27
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Sky.vert25
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md44
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag50
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md70
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert37
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md18
-rw-r--r--engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md38
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag12
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md26
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert31
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag119
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md63
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert37
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag139
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag15
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md19
-rw-r--r--engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert12
-rw-r--r--engine/src/core-data/Common/Materials/RedColor.j3m5
-rw-r--r--engine/src/core-data/Common/Materials/VertexColor.j3m5
-rw-r--r--engine/src/core-data/Common/Materials/WhiteColor.j3m5
-rw-r--r--engine/src/core-data/Common/ShaderLib/Bump.glsllib44
-rw-r--r--engine/src/core-data/Common/ShaderLib/Common.glsllib13
-rw-r--r--engine/src/core-data/Common/ShaderLib/Fog.glsllib41
-rw-r--r--engine/src/core-data/Common/ShaderLib/Hdr.glsllib65
-rw-r--r--engine/src/core-data/Common/ShaderLib/Lighting.glsllib48
-rw-r--r--engine/src/core-data/Common/ShaderLib/Math.glsllib4
-rw-r--r--engine/src/core-data/Common/ShaderLib/MultiSample.glsllib62
-rw-r--r--engine/src/core-data/Common/ShaderLib/Optics.glsllib32
-rw-r--r--engine/src/core-data/Common/ShaderLib/Parallax.glsllib78
-rw-r--r--engine/src/core-data/Common/ShaderLib/Shadow.glsllib105
-rw-r--r--engine/src/core-data/Common/ShaderLib/Skinning.glsllib36
-rw-r--r--engine/src/core-data/Common/ShaderLib/Splatting.glsllib10
-rw-r--r--engine/src/core-data/Common/ShaderLib/Tangent.glsllib11
-rw-r--r--engine/src/core-data/Common/ShaderLib/Texture.glsllib41
-rw-r--r--engine/src/core-data/Common/ShaderLib/Ubo.glsllib15
-rw-r--r--engine/src/core-data/Interface/Fonts/Console.fnt99
-rw-r--r--engine/src/core-data/Interface/Fonts/Console.pngbin0 -> 1519 bytes
-rw-r--r--engine/src/core-data/Interface/Fonts/Default.fnt229
-rw-r--r--engine/src/core-data/Interface/Fonts/Default.pngbin0 -> 9950 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/BloomExtract.j3md43
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md36
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.frag55
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.j3md48
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/CartoonEdge15.frag57
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/CrossHatch.frag51
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/CrossHatch.j3md41
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/CrossHatch15.frag53
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/DepthOfField.frag89
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/DepthOfField.j3md25
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/FXAA.frag88
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/FXAA.j3md20
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/FXAA.vert18
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Fade.frag11
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Fade.j3md34
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Fade15.frag11
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Fog.frag21
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Fog.j3md39
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Fog15.frag24
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.frag23
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.j3md39
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/GammaCorrection15.frag26
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/LightScattering.frag36
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/LightScattering.j3md41
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/LightScattering.vert14
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/LightScattering15.frag39
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/LightScattering15.vert14
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Overlay.frag9
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Overlay.j3md36
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Overlay15.frag11
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Post.vert10
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Post15.vert12
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Posterization.frag18
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Posterization.j3md32
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/Posterization15.frag20
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/bloomExtract.frag29
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/bloomExtract15.frag33
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/bloomFinal.frag12
-rw-r--r--engine/src/core-effects/Common/MatDefs/Post/bloomFinal15.frag15
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/Textures/random.pngbin0 -> 42993 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/normal.frag21
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/normal.vert16
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/ssao.frag104
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/ssao.j3md51
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/ssao15.frag96
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.frag159
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.j3md57
-rw-r--r--engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur15.frag160
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/SimpleWater.j3md34
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/caustics.jpgbin0 -> 57225 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/dudv_map.jpgbin0 -> 39633 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/foam.jpgbin0 -> 65823 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/foam2.jpgbin0 -> 80871 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/foam3.jpgbin0 -> 100447 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/heightmap.jpgbin0 -> 24995 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Textures/water_normalmap.ddsbin0 -> 32896 bytes
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Water.frag402
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Water.j3md90
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/Water15.frag419
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/simple_water.frag126
-rw-r--r--engine/src/core-effects/Common/MatDefs/Water/simple_water.vert87
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/BloomFilter.java309
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/CartoonEdgeFilter.java245
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/ColorOverlayFilter.java111
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/CrossHatchFilter.java305
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/DepthOfFieldFilter.java158
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/FXAAFilter.java95
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/FadeFilter.java177
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/FogFilter.java172
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/GammaCorrectionFilter.java78
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java243
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/PosterizationFilter.java147
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/RadialBlurFilter.java156
-rw-r--r--engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java80
-rw-r--r--engine/src/core-effects/com/jme3/post/ssao/SSAOFilter.java324
-rw-r--r--engine/src/core-effects/com/jme3/water/ReflectionProcessor.java125
-rw-r--r--engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java589
-rw-r--r--engine/src/core-effects/com/jme3/water/WaterFilter.java1050
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/ClasspathLocator.java117
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/FileLocator.java104
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java367
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~355
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/UrlAssetInfo.java65
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/UrlLocator.java84
-rw-r--r--engine/src/core-plugins/com/jme3/asset/plugins/ZipLocator.java87
-rw-r--r--engine/src/core-plugins/com/jme3/audio/plugins/WAVLoader.java191
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java101
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java46
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java401
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java60
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java361
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java1380
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java944
-rw-r--r--engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java486
-rw-r--r--engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java181
-rw-r--r--engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java530
-rw-r--r--engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java325
-rw-r--r--engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java593
-rw-r--r--engine/src/core-plugins/com/jme3/shader/plugins/GLSLLoader.java213
-rw-r--r--engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java827
-rw-r--r--engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java318
-rw-r--r--engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java332
-rw-r--r--engine/src/core-plugins/com/jme3/texture/plugins/ImageFlipper.java77
-rw-r--r--engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java152
-rw-r--r--engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java517
-rw-r--r--engine/src/core/checkers/quals/DefaultLocation.java25
-rw-r--r--engine/src/core/checkers/quals/DefaultQualifier.java44
-rw-r--r--engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java29
-rw-r--r--engine/src/core/checkers/quals/DefaultQualifiers.java35
-rw-r--r--engine/src/core/checkers/quals/Dependent.java38
-rw-r--r--engine/src/core/checkers/quals/SubtypeOf.java59
-rw-r--r--engine/src/core/checkers/quals/TypeQualifier.java16
-rw-r--r--engine/src/core/checkers/quals/Unqualified.java16
-rw-r--r--engine/src/core/checkers/quals/Unused.java43
-rw-r--r--engine/src/core/checkers/quals/package-info.java10
-rw-r--r--engine/src/core/com/jme3/animation/AnimChannel.java363
-rw-r--r--engine/src/core/com/jme3/animation/AnimControl.java377
-rw-r--r--engine/src/core/com/jme3/animation/AnimEventListener.java63
-rw-r--r--engine/src/core/com/jme3/animation/Animation.java205
-rw-r--r--engine/src/core/com/jme3/animation/AnimationFactory.java494
-rw-r--r--engine/src/core/com/jme3/animation/Bone.java628
-rw-r--r--engine/src/core/com/jme3/animation/BoneAnimation.java44
-rw-r--r--engine/src/core/com/jme3/animation/BoneTrack.java334
-rw-r--r--engine/src/core/com/jme3/animation/CompactArray.java270
-rw-r--r--engine/src/core/com/jme3/animation/CompactQuaternionArray.java100
-rw-r--r--engine/src/core/com/jme3/animation/CompactVector3Array.java99
-rw-r--r--engine/src/core/com/jme3/animation/LoopMode.java60
-rw-r--r--engine/src/core/com/jme3/animation/Pose.java126
-rw-r--r--engine/src/core/com/jme3/animation/PoseTrack.java186
-rw-r--r--engine/src/core/com/jme3/animation/Skeleton.java297
-rw-r--r--engine/src/core/com/jme3/animation/SkeletonControl.java549
-rw-r--r--engine/src/core/com/jme3/animation/SpatialAnimation.java11
-rw-r--r--engine/src/core/com/jme3/animation/SpatialTrack.java242
-rw-r--r--engine/src/core/com/jme3/animation/Track.java63
-rw-r--r--engine/src/core/com/jme3/animation/package.html78
-rw-r--r--engine/src/core/com/jme3/app/AppTask.java167
-rw-r--r--engine/src/core/com/jme3/app/Application.java642
-rw-r--r--engine/src/core/com/jme3/app/DebugKeysAppState.java117
-rw-r--r--engine/src/core/com/jme3/app/FlyCamAppState.java94
-rw-r--r--engine/src/core/com/jme3/app/SimpleApplication.java277
-rw-r--r--engine/src/core/com/jme3/app/StatsAppState.java208
-rw-r--r--engine/src/core/com/jme3/app/StatsView.java132
-rw-r--r--engine/src/core/com/jme3/app/package.html80
-rw-r--r--engine/src/core/com/jme3/app/state/AbstractAppState.java89
-rw-r--r--engine/src/core/com/jme3/app/state/AppState.java119
-rw-r--r--engine/src/core/com/jme3/app/state/AppStateManager.java288
-rw-r--r--engine/src/core/com/jme3/app/state/package.html15
-rw-r--r--engine/src/core/com/jme3/asset/Asset.java73
-rw-r--r--engine/src/core/com/jme3/asset/AssetCache.java131
-rw-r--r--engine/src/core/com/jme3/asset/AssetConfig.java138
-rw-r--r--engine/src/core/com/jme3/asset/AssetEventListener.java77
-rw-r--r--engine/src/core/com/jme3/asset/AssetInfo.java77
-rw-r--r--engine/src/core/com/jme3/asset/AssetKey.java203
-rw-r--r--engine/src/core/com/jme3/asset/AssetLoadException.java17
-rw-r--r--engine/src/core/com/jme3/asset/AssetLoader.java54
-rw-r--r--engine/src/core/com/jme3/asset/AssetLocator.java60
-rw-r--r--engine/src/core/com/jme3/asset/AssetManager.java265
-rw-r--r--engine/src/core/com/jme3/asset/AssetNotFoundException.java17
-rw-r--r--engine/src/core/com/jme3/asset/Desktop.cfg22
-rw-r--r--engine/src/core/com/jme3/asset/DesktopAssetManager.java421
-rw-r--r--engine/src/core/com/jme3/asset/ImplHandler.java209
-rw-r--r--engine/src/core/com/jme3/asset/MaterialKey.java29
-rw-r--r--engine/src/core/com/jme3/asset/ModelKey.java61
-rw-r--r--engine/src/core/com/jme3/asset/TextureKey.java190
-rw-r--r--engine/src/core/com/jme3/asset/ThreadingManager.java103
-rw-r--r--engine/src/core/com/jme3/asset/package.html37
-rw-r--r--engine/src/core/com/jme3/audio/AudioBuffer.java120
-rw-r--r--engine/src/core/com/jme3/audio/AudioContext.java51
-rw-r--r--engine/src/core/com/jme3/audio/AudioData.java110
-rw-r--r--engine/src/core/com/jme3/audio/AudioKey.java137
-rw-r--r--engine/src/core/com/jme3/audio/AudioNode.java810
-rw-r--r--engine/src/core/com/jme3/audio/AudioParam.java19
-rw-r--r--engine/src/core/com/jme3/audio/AudioRenderer.java83
-rw-r--r--engine/src/core/com/jme3/audio/AudioStream.java204
-rw-r--r--engine/src/core/com/jme3/audio/Environment.java256
-rw-r--r--engine/src/core/com/jme3/audio/Filter.java73
-rw-r--r--engine/src/core/com/jme3/audio/Listener.java114
-rw-r--r--engine/src/core/com/jme3/audio/ListenerParam.java40
-rw-r--r--engine/src/core/com/jme3/audio/LowPassFilter.java100
-rw-r--r--engine/src/core/com/jme3/audio/SeekableStream.java15
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingBox.java977
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingSphere.java858
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingVolume.java329
-rw-r--r--engine/src/core/com/jme3/bounding/Intersection.java284
-rw-r--r--engine/src/core/com/jme3/bounding/OrientedBoundingBox.java1522
-rw-r--r--engine/src/core/com/jme3/cinematic/Cinematic.java381
-rw-r--r--engine/src/core/com/jme3/cinematic/KeyFrame.java85
-rw-r--r--engine/src/core/com/jme3/cinematic/MotionPath.java371
-rw-r--r--engine/src/core/com/jme3/cinematic/MotionPathListener.java50
-rw-r--r--engine/src/core/com/jme3/cinematic/PlayState.java48
-rw-r--r--engine/src/core/com/jme3/cinematic/TimeLine.java120
-rw-r--r--engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java320
-rw-r--r--engine/src/core/com/jme3/cinematic/events/AnimationTrack.java175
-rw-r--r--engine/src/core/com/jme3/cinematic/events/CinematicEvent.java143
-rw-r--r--engine/src/core/com/jme3/cinematic/events/CinematicEventListener.java17
-rw-r--r--engine/src/core/com/jme3/cinematic/events/MotionTrack.java440
-rw-r--r--engine/src/core/com/jme3/cinematic/events/PositionTrack.java122
-rw-r--r--engine/src/core/com/jme3/cinematic/events/RotationTrack.java126
-rw-r--r--engine/src/core/com/jme3/cinematic/events/ScaleTrack.java121
-rw-r--r--engine/src/core/com/jme3/cinematic/events/SoundTrack.java183
-rw-r--r--engine/src/core/com/jme3/collision/Collidable.java53
-rw-r--r--engine/src/core/com/jme3/collision/CollisionResult.java138
-rw-r--r--engine/src/core/com/jme3/collision/CollisionResults.java136
-rw-r--r--engine/src/core/com/jme3/collision/MotionAllowedListener.java48
-rw-r--r--engine/src/core/com/jme3/collision/SweepSphere.java440
-rw-r--r--engine/src/core/com/jme3/collision/UnsupportedCollisionException.java60
-rw-r--r--engine/src/core/com/jme3/collision/bih/BIHNode.java430
-rw-r--r--engine/src/core/com/jme3/collision/bih/BIHTree.java482
-rw-r--r--engine/src/core/com/jme3/collision/bih/BIHTriangle.java111
-rw-r--r--engine/src/core/com/jme3/collision/bih/TriangleAxisComparator.java63
-rw-r--r--engine/src/core/com/jme3/effect/Particle.java94
-rw-r--r--engine/src/core/com/jme3/effect/ParticleComparator.java77
-rw-r--r--engine/src/core/com/jme3/effect/ParticleEmitter.java1206
-rw-r--r--engine/src/core/com/jme3/effect/ParticleMesh.java86
-rw-r--r--engine/src/core/com/jme3/effect/ParticlePointMesh.java166
-rw-r--r--engine/src/core/com/jme3/effect/ParticleTriMesh.java284
-rw-r--r--engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java92
-rw-r--r--engine/src/core/com/jme3/effect/influencers/EmptyParticleInfluencer.java55
-rw-r--r--engine/src/core/com/jme3/effect/influencers/NewtonianParticleInfluencer.java142
-rw-r--r--engine/src/core/com/jme3/effect/influencers/ParticleInfluencer.java61
-rw-r--r--engine/src/core/com/jme3/effect/package.html19
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterBoxShape.java118
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java63
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterMeshFaceShape.java97
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java158
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterPointShape.java96
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterShape.java64
-rw-r--r--engine/src/core/com/jme3/effect/shapes/EmitterSphereShape.java117
-rw-r--r--engine/src/core/com/jme3/export/FormatVersion.java22
-rw-r--r--engine/src/core/com/jme3/export/InputCapsule.java160
-rw-r--r--engine/src/core/com/jme3/export/JmeExporter.java74
-rw-r--r--engine/src/core/com/jme3/export/JmeImporter.java49
-rw-r--r--engine/src/core/com/jme3/export/OutputCapsule.java158
-rw-r--r--engine/src/core/com/jme3/export/ReadListener.java39
-rw-r--r--engine/src/core/com/jme3/export/Savable.java46
-rw-r--r--engine/src/core/com/jme3/export/SavableClassUtil.java205
-rw-r--r--engine/src/core/com/jme3/font/BitmapCharacter.java199
-rw-r--r--engine/src/core/com/jme3/font/BitmapCharacterSet.java223
-rw-r--r--engine/src/core/com/jme3/font/BitmapFont.java286
-rw-r--r--engine/src/core/com/jme3/font/BitmapText.java361
-rw-r--r--engine/src/core/com/jme3/font/BitmapTextPage.java197
-rw-r--r--engine/src/core/com/jme3/font/ColorTags.java91
-rw-r--r--engine/src/core/com/jme3/font/Kerning.java74
-rw-r--r--engine/src/core/com/jme3/font/LetterQuad.java496
-rw-r--r--engine/src/core/com/jme3/font/Letters.java331
-rw-r--r--engine/src/core/com/jme3/font/LineWrapMode.java11
-rw-r--r--engine/src/core/com/jme3/font/Rectangle.java65
-rw-r--r--engine/src/core/com/jme3/font/StringBlock.java200
-rw-r--r--engine/src/core/com/jme3/input/ChaseCamera.java875
-rw-r--r--engine/src/core/com/jme3/input/FlyByCamera.java364
-rw-r--r--engine/src/core/com/jme3/input/Input.java82
-rw-r--r--engine/src/core/com/jme3/input/InputManager.java881
-rw-r--r--engine/src/core/com/jme3/input/JoyInput.java66
-rw-r--r--engine/src/core/com/jme3/input/Joystick.java136
-rw-r--r--engine/src/core/com/jme3/input/KeyInput.java543
-rw-r--r--engine/src/core/com/jme3/input/KeyNames.java153
-rw-r--r--engine/src/core/com/jme3/input/MouseInput.java83
-rw-r--r--engine/src/core/com/jme3/input/RawInputListener.java99
-rw-r--r--engine/src/core/com/jme3/input/TouchInput.java92
-rw-r--r--engine/src/core/com/jme3/input/controls/ActionListener.java58
-rw-r--r--engine/src/core/com/jme3/input/controls/AnalogListener.java53
-rw-r--r--engine/src/core/com/jme3/input/controls/InputListener.java42
-rw-r--r--engine/src/core/com/jme3/input/controls/JoyAxisTrigger.java77
-rw-r--r--engine/src/core/com/jme3/input/controls/JoyButtonTrigger.java73
-rw-r--r--engine/src/core/com/jme3/input/controls/KeyTrigger.java72
-rw-r--r--engine/src/core/com/jme3/input/controls/MouseAxisTrigger.java90
-rw-r--r--engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java79
-rw-r--r--engine/src/core/com/jme3/input/controls/TouchListener.java49
-rw-r--r--engine/src/core/com/jme3/input/controls/TouchTrigger.java73
-rw-r--r--engine/src/core/com/jme3/input/controls/Trigger.java52
-rw-r--r--engine/src/core/com/jme3/input/controls/package.html19
-rw-r--r--engine/src/core/com/jme3/input/dummy/DummyInput.java78
-rw-r--r--engine/src/core/com/jme3/input/dummy/DummyKeyInput.java52
-rw-r--r--engine/src/core/com/jme3/input/dummy/DummyMouseInput.java54
-rw-r--r--engine/src/core/com/jme3/input/dummy/package.html14
-rw-r--r--engine/src/core/com/jme3/input/event/InputEvent.java84
-rw-r--r--engine/src/core/com/jme3/input/event/JoyAxisEvent.java85
-rw-r--r--engine/src/core/com/jme3/input/event/JoyButtonEvent.java89
-rw-r--r--engine/src/core/com/jme3/input/event/KeyInputEvent.java117
-rw-r--r--engine/src/core/com/jme3/input/event/MouseButtonEvent.java111
-rw-r--r--engine/src/core/com/jme3/input/event/MouseMotionEvent.java110
-rw-r--r--engine/src/core/com/jme3/input/event/TouchEvent.java209
-rw-r--r--engine/src/core/com/jme3/input/event/package.html13
-rw-r--r--engine/src/core/com/jme3/input/package.html38
-rw-r--r--engine/src/core/com/jme3/light/AmbientLight.java26
-rw-r--r--engine/src/core/com/jme3/light/DirectionalLight.java104
-rw-r--r--engine/src/core/com/jme3/light/Light.java210
-rw-r--r--engine/src/core/com/jme3/light/LightList.java336
-rw-r--r--engine/src/core/com/jme3/light/PointLight.java157
-rw-r--r--engine/src/core/com/jme3/light/SpotLight.java218
-rw-r--r--engine/src/core/com/jme3/light/package.html23
-rw-r--r--engine/src/core/com/jme3/material/FixedFuncBinding.java80
-rw-r--r--engine/src/core/com/jme3/material/MatParam.java353
-rw-r--r--engine/src/core/com/jme3/material/MatParamTexture.java67
-rw-r--r--engine/src/core/com/jme3/material/Material.java1152
-rw-r--r--engine/src/core/com/jme3/material/MaterialDef.java190
-rw-r--r--engine/src/core/com/jme3/material/MaterialList.java44
-rw-r--r--engine/src/core/com/jme3/material/RenderState.java1070
-rw-r--r--engine/src/core/com/jme3/material/Technique.java261
-rw-r--r--engine/src/core/com/jme3/material/TechniqueDef.java396
-rw-r--r--engine/src/core/com/jme3/material/package.html58
-rw-r--r--engine/src/core/com/jme3/math/AbstractTriangle.java49
-rw-r--r--engine/src/core/com/jme3/math/ColorRGBA.java549
-rw-r--r--engine/src/core/com/jme3/math/CurveAndSurfaceMath.java133
-rw-r--r--engine/src/core/com/jme3/math/Eigen3f.java421
-rw-r--r--engine/src/core/com/jme3/math/FastMath.java987
-rw-r--r--engine/src/core/com/jme3/math/Line.java239
-rw-r--r--engine/src/core/com/jme3/math/LineSegment.java631
-rw-r--r--engine/src/core/com/jme3/math/Matrix3f.java1387
-rw-r--r--engine/src/core/com/jme3/math/Matrix4f.java2305
-rw-r--r--engine/src/core/com/jme3/math/Plane.java284
-rw-r--r--engine/src/core/com/jme3/math/Quaternion.java1345
-rw-r--r--engine/src/core/com/jme3/math/Ray.java521
-rw-r--r--engine/src/core/com/jme3/math/Rectangle.java197
-rw-r--r--engine/src/core/com/jme3/math/Ring.java233
-rw-r--r--engine/src/core/com/jme3/math/Spline.java447
-rw-r--r--engine/src/core/com/jme3/math/Transform.java318
-rw-r--r--engine/src/core/com/jme3/math/Triangle.java302
-rw-r--r--engine/src/core/com/jme3/math/Vector2f.java757
-rw-r--r--engine/src/core/com/jme3/math/Vector3f.java1061
-rw-r--r--engine/src/core/com/jme3/math/Vector4f.java1003
-rw-r--r--engine/src/core/com/jme3/math/package.html53
-rw-r--r--engine/src/core/com/jme3/post/Filter.java433
-rw-r--r--engine/src/core/com/jme3/post/FilterPostProcessor.java500
-rw-r--r--engine/src/core/com/jme3/post/HDRRenderer.java419
-rw-r--r--engine/src/core/com/jme3/post/PreDepthProcessor.java100
-rw-r--r--engine/src/core/com/jme3/post/SceneProcessor.java94
-rw-r--r--engine/src/core/com/jme3/post/package.html24
-rw-r--r--engine/src/core/com/jme3/renderer/Camera.java1436
-rw-r--r--engine/src/core/com/jme3/renderer/Caps.java360
-rw-r--r--engine/src/core/com/jme3/renderer/GL1Renderer.java26
-rw-r--r--engine/src/core/com/jme3/renderer/IDList.java121
-rw-r--r--engine/src/core/com/jme3/renderer/RenderContext.java319
-rw-r--r--engine/src/core/com/jme3/renderer/RenderManager.java1170
-rw-r--r--engine/src/core/com/jme3/renderer/Renderer.java305
-rw-r--r--engine/src/core/com/jme3/renderer/RendererException.java49
-rw-r--r--engine/src/core/com/jme3/renderer/Statistics.java255
-rw-r--r--engine/src/core/com/jme3/renderer/ViewPort.java363
-rw-r--r--engine/src/core/com/jme3/renderer/package.html38
-rw-r--r--engine/src/core/com/jme3/renderer/queue/GeometryComparator.java53
-rw-r--r--engine/src/core/com/jme3/renderer/queue/GeometryList.java143
-rw-r--r--engine/src/core/com/jme3/renderer/queue/GuiComparator.java60
-rw-r--r--engine/src/core/com/jme3/renderer/queue/NullComparator.java51
-rw-r--r--engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java96
-rw-r--r--engine/src/core/com/jme3/renderer/queue/RenderQueue.java377
-rw-r--r--engine/src/core/com/jme3/renderer/queue/TransparentComparator.java102
-rw-r--r--engine/src/core/com/jme3/scene/AssetLinkNode.java185
-rw-r--r--engine/src/core/com/jme3/scene/BatchNode.java653
-rw-r--r--engine/src/core/com/jme3/scene/CameraNode.java93
-rw-r--r--engine/src/core/com/jme3/scene/CollisionData.java52
-rw-r--r--engine/src/core/com/jme3/scene/Geometry.java565
-rw-r--r--engine/src/core/com/jme3/scene/LightNode.java93
-rw-r--r--engine/src/core/com/jme3/scene/Mesh.java1315
-rw-r--r--engine/src/core/com/jme3/scene/Node.java643
-rw-r--r--engine/src/core/com/jme3/scene/SceneGraphVisitor.java16
-rw-r--r--engine/src/core/com/jme3/scene/SceneGraphVisitorAdapter.java35
-rw-r--r--engine/src/core/com/jme3/scene/SimpleBatchNode.java56
-rw-r--r--engine/src/core/com/jme3/scene/Spatial.java1478
-rw-r--r--engine/src/core/com/jme3/scene/UserData.java156
-rw-r--r--engine/src/core/com/jme3/scene/VertexBuffer.java1025
-rw-r--r--engine/src/core/com/jme3/scene/control/AbstractControl.java112
-rw-r--r--engine/src/core/com/jme3/scene/control/AreaUtils.java85
-rw-r--r--engine/src/core/com/jme3/scene/control/BillboardControl.java307
-rw-r--r--engine/src/core/com/jme3/scene/control/CameraControl.java159
-rw-r--r--engine/src/core/com/jme3/scene/control/Control.java90
-rw-r--r--engine/src/core/com/jme3/scene/control/LightControl.java189
-rw-r--r--engine/src/core/com/jme3/scene/control/LodControl.java204
-rw-r--r--engine/src/core/com/jme3/scene/control/UpdateControl.java96
-rw-r--r--engine/src/core/com/jme3/scene/control/package.html17
-rw-r--r--engine/src/core/com/jme3/scene/debug/Arrow.java142
-rw-r--r--engine/src/core/com/jme3/scene/debug/Grid.java105
-rw-r--r--engine/src/core/com/jme3/scene/debug/SkeletonDebugger.java80
-rw-r--r--engine/src/core/com/jme3/scene/debug/SkeletonPoints.java80
-rw-r--r--engine/src/core/com/jme3/scene/debug/SkeletonWire.java109
-rw-r--r--engine/src/core/com/jme3/scene/debug/WireBox.java108
-rw-r--r--engine/src/core/com/jme3/scene/debug/WireFrustum.java88
-rw-r--r--engine/src/core/com/jme3/scene/debug/WireSphere.java159
-rw-r--r--engine/src/core/com/jme3/scene/mesh/IndexBuffer.java96
-rw-r--r--engine/src/core/com/jme3/scene/mesh/IndexByteBuffer.java71
-rw-r--r--engine/src/core/com/jme3/scene/mesh/IndexIntBuffer.java70
-rw-r--r--engine/src/core/com/jme3/scene/mesh/IndexShortBuffer.java70
-rw-r--r--engine/src/core/com/jme3/scene/mesh/VirtualIndexBuffer.java106
-rw-r--r--engine/src/core/com/jme3/scene/mesh/WrappedIndexBuffer.java86
-rw-r--r--engine/src/core/com/jme3/scene/mesh/package.html25
-rw-r--r--engine/src/core/com/jme3/scene/package.html26
-rw-r--r--engine/src/core/com/jme3/scene/shape/AbstractBox.java211
-rw-r--r--engine/src/core/com/jme3/scene/shape/Box.java176
-rw-r--r--engine/src/core/com/jme3/scene/shape/Curve.java271
-rw-r--r--engine/src/core/com/jme3/scene/shape/Cylinder.java420
-rw-r--r--engine/src/core/com/jme3/scene/shape/Dome.java339
-rw-r--r--engine/src/core/com/jme3/scene/shape/Line.java123
-rw-r--r--engine/src/core/com/jme3/scene/shape/PQTorus.java239
-rw-r--r--engine/src/core/com/jme3/scene/shape/Quad.java130
-rw-r--r--engine/src/core/com/jme3/scene/shape/Sphere.java420
-rw-r--r--engine/src/core/com/jme3/scene/shape/StripBox.java191
-rw-r--r--engine/src/core/com/jme3/scene/shape/Surface.java283
-rw-r--r--engine/src/core/com/jme3/scene/shape/Torus.java255
-rw-r--r--engine/src/core/com/jme3/shader/Attribute.java42
-rw-r--r--engine/src/core/com/jme3/shader/DefineList.java180
-rw-r--r--engine/src/core/com/jme3/shader/Shader.java443
-rw-r--r--engine/src/core/com/jme3/shader/ShaderKey.java126
-rw-r--r--engine/src/core/com/jme3/shader/ShaderUtils.java50
-rw-r--r--engine/src/core/com/jme3/shader/ShaderVariable.java83
-rw-r--r--engine/src/core/com/jme3/shader/Uniform.java432
-rw-r--r--engine/src/core/com/jme3/shader/UniformBinding.java162
-rw-r--r--engine/src/core/com/jme3/shader/VarType.java81
-rw-r--r--engine/src/core/com/jme3/shadow/BasicShadowRenderer.java216
-rw-r--r--engine/src/core/com/jme3/shadow/PssmShadowRenderer.java554
-rw-r--r--engine/src/core/com/jme3/shadow/PssmShadowUtil.java81
-rw-r--r--engine/src/core/com/jme3/shadow/ShadowCamera.java75
-rw-r--r--engine/src/core/com/jme3/shadow/ShadowUtil.java486
-rw-r--r--engine/src/core/com/jme3/system/Annotations.java72
-rw-r--r--engine/src/core/com/jme3/system/AppSettings.java726
-rw-r--r--engine/src/core/com/jme3/system/JmeContext.java184
-rw-r--r--engine/src/core/com/jme3/system/JmeSystem.java136
-rw-r--r--engine/src/core/com/jme3/system/JmeSystemDelegate.java140
-rw-r--r--engine/src/core/com/jme3/system/JmeVersion.java5
-rw-r--r--engine/src/core/com/jme3/system/NanoTimer.java92
-rw-r--r--engine/src/core/com/jme3/system/NullContext.java230
-rw-r--r--engine/src/core/com/jme3/system/NullRenderer.java152
-rw-r--r--engine/src/core/com/jme3/system/Platform.java65
-rw-r--r--engine/src/core/com/jme3/system/SystemListener.java99
-rw-r--r--engine/src/core/com/jme3/system/Timer.java94
-rw-r--r--engine/src/core/com/jme3/texture/FrameBuffer.java460
-rw-r--r--engine/src/core/com/jme3/texture/Image.java790
-rw-r--r--engine/src/core/com/jme3/texture/Texture.java613
-rw-r--r--engine/src/core/com/jme3/texture/Texture2D.java221
-rw-r--r--engine/src/core/com/jme3/texture/Texture3D.java224
-rw-r--r--engine/src/core/com/jme3/texture/TextureArray.java113
-rw-r--r--engine/src/core/com/jme3/texture/TextureCubeMap.java206
-rw-r--r--engine/src/core/com/jme3/ui/Picture.java160
-rw-r--r--engine/src/core/com/jme3/util/BufferUtils.java1196
-rw-r--r--engine/src/core/com/jme3/util/IntMap.java308
-rw-r--r--engine/src/core/com/jme3/util/JmeFormatter.java93
-rw-r--r--engine/src/core/com/jme3/util/ListMap.java317
-rw-r--r--engine/src/core/com/jme3/util/LittleEndien.java160
-rw-r--r--engine/src/core/com/jme3/util/NativeObject.java172
-rw-r--r--engine/src/core/com/jme3/util/NativeObjectManager.java148
-rw-r--r--engine/src/core/com/jme3/util/PlaceholderAssets.java72
-rw-r--r--engine/src/core/com/jme3/util/SafeArrayList.java402
-rw-r--r--engine/src/core/com/jme3/util/SkyFactory.java214
-rw-r--r--engine/src/core/com/jme3/util/SortUtil.java352
-rw-r--r--engine/src/core/com/jme3/util/TangentBinormalGenerator.java739
-rw-r--r--engine/src/core/com/jme3/util/TempVars.java221
-rw-r--r--engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java92
-rw-r--r--engine/src/core/com/jme3/util/blockparser/Statement.java61
-rw-r--r--engine/src/core/com/jme3/util/xml/SAXUtil.java140
-rw-r--r--engine/src/desktop/com/jme3/app/AppletHarness.java185
-rw-r--r--engine/src/desktop/com/jme3/app/Monkey.pngbin0 -> 114815 bytes
-rw-r--r--engine/src/desktop/com/jme3/app/SettingsDialog.java677
-rw-r--r--engine/src/desktop/com/jme3/app/state/MjpegFileWriter.java490
-rw-r--r--engine/src/desktop/com/jme3/app/state/ScreenshotAppState.java94
-rw-r--r--engine/src/desktop/com/jme3/app/state/VideoRecorderAppState.java241
-rw-r--r--engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java606
-rw-r--r--engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java313
-rw-r--r--engine/src/desktop/com/jme3/system/JmeCanvasContext.java39
-rw-r--r--engine/src/desktop/com/jme3/system/JmeDesktopSystem.java281
-rw-r--r--engine/src/desktop/com/jme3/system/Natives.java306
-rw-r--r--engine/src/desktop/com/jme3/system/awt/AwtPanel.java293
-rw-r--r--engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java202
-rw-r--r--engine/src/desktop/com/jme3/system/awt/PaintMode.java7
-rw-r--r--engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java211
-rw-r--r--engine/src/desktop/com/jme3/util/Screenshots.java78
-rw-r--r--engine/src/desktop/jme3tools/converters/ImageToAwt.java479
-rw-r--r--engine/src/desktop/jme3tools/converters/MipMapGenerator.java127
-rw-r--r--engine/src/desktop/jme3tools/navigation/Coordinate.java247
-rw-r--r--engine/src/desktop/jme3tools/navigation/GCSailing.java33
-rw-r--r--engine/src/desktop/jme3tools/navigation/InvalidPositionException.java14
-rw-r--r--engine/src/desktop/jme3tools/navigation/MapModel2D.java368
-rw-r--r--engine/src/desktop/jme3tools/navigation/MapModel3D.java389
-rw-r--r--engine/src/desktop/jme3tools/navigation/NavCalculator.java591
-rw-r--r--engine/src/desktop/jme3tools/navigation/NumUtil.java30
-rw-r--r--engine/src/desktop/jme3tools/navigation/Position.java228
-rw-r--r--engine/src/desktop/jme3tools/navigation/RLSailing.java32
-rw-r--r--engine/src/desktop/jme3tools/navigation/StringUtil.java260
-rw-r--r--engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java853
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java197
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java59
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java287
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java91
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java91
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java87
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java126
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java111
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java150
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java77
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java117
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java133
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java133
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java79
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java126
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java59
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java85
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java85
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java138
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java156
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java136
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java111
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java228
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java430
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java129
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java100
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java290
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java284
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java703
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java557
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java402
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java206
-rw-r--r--engine/src/jbullet/com/jme3/bullet/util/Converter.java282
-rw-r--r--engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java249
-rw-r--r--engine/src/jogg/com/jme3/audio/plugins/CachedOggStream.java142
-rw-r--r--engine/src/jogg/com/jme3/audio/plugins/OGGLoader.java310
-rw-r--r--engine/src/jogg/com/jme3/audio/plugins/UncachedOggStream.java140
-rw-r--r--engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java1056
-rw-r--r--engine/src/lwjgl/com/jme3/input/lwjgl/JInputJoyInput.java194
-rw-r--r--engine/src/lwjgl/com/jme3/input/lwjgl/LwjglKeyInput.java112
-rw-r--r--engine/src/lwjgl/com/jme3/input/lwjgl/LwjglMouseInput.java153
-rw-r--r--engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java1169
-rw-r--r--engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java2468
-rw-r--r--engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java522
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java266
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglCanvas.java482
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglContext.java217
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglDisplay.java240
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java198
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglSmoothingTimer.java209
-rw-r--r--engine/src/lwjgl/com/jme3/system/lwjgl/LwjglTimer.java139
-rw-r--r--engine/src/networking/com/jme3/network/AbstractMessage.java75
-rw-r--r--engine/src/networking/com/jme3/network/Client.java143
-rw-r--r--engine/src/networking/com/jme3/network/ClientStateListener.java68
-rw-r--r--engine/src/networking/com/jme3/network/ConnectionListener.java56
-rw-r--r--engine/src/networking/com/jme3/network/ErrorListener.java45
-rw-r--r--engine/src/networking/com/jme3/network/Filter.java51
-rw-r--r--engine/src/networking/com/jme3/network/Filters.java159
-rw-r--r--engine/src/networking/com/jme3/network/HostedConnection.java90
-rw-r--r--engine/src/networking/com/jme3/network/Message.java57
-rw-r--r--engine/src/networking/com/jme3/network/MessageConnection.java56
-rw-r--r--engine/src/networking/com/jme3/network/MessageListener.java54
-rw-r--r--engine/src/networking/com/jme3/network/Network.java192
-rw-r--r--engine/src/networking/com/jme3/network/NetworkClient.java67
-rw-r--r--engine/src/networking/com/jme3/network/Server.java186
-rw-r--r--engine/src/networking/com/jme3/network/base/ConnectorAdapter.java218
-rw-r--r--engine/src/networking/com/jme3/network/base/ConnectorFactory.java48
-rw-r--r--engine/src/networking/com/jme3/network/base/DefaultClient.java428
-rw-r--r--engine/src/networking/com/jme3/network/base/DefaultServer.java591
-rw-r--r--engine/src/networking/com/jme3/network/base/KernelAdapter.java296
-rw-r--r--engine/src/networking/com/jme3/network/base/KernelFactory.java51
-rw-r--r--engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java120
-rw-r--r--engine/src/networking/com/jme3/network/base/MessageProtocol.java191
-rw-r--r--engine/src/networking/com/jme3/network/base/NioKernelFactory.java53
-rw-r--r--engine/src/networking/com/jme3/network/base/TcpConnectorFactory.java60
-rw-r--r--engine/src/networking/com/jme3/network/base/package.html12
-rw-r--r--engine/src/networking/com/jme3/network/kernel/AbstractKernel.java122
-rw-r--r--engine/src/networking/com/jme3/network/kernel/Connector.java83
-rw-r--r--engine/src/networking/com/jme3/network/kernel/ConnectorException.java54
-rw-r--r--engine/src/networking/com/jme3/network/kernel/Endpoint.java88
-rw-r--r--engine/src/networking/com/jme3/network/kernel/EndpointEvent.java87
-rw-r--r--engine/src/networking/com/jme3/network/kernel/Envelope.java79
-rw-r--r--engine/src/networking/com/jme3/network/kernel/Kernel.java94
-rw-r--r--engine/src/networking/com/jme3/network/kernel/KernelException.java54
-rw-r--r--engine/src/networking/com/jme3/network/kernel/NamedThreadFactory.java83
-rw-r--r--engine/src/networking/com/jme3/network/kernel/package.html34
-rw-r--r--engine/src/networking/com/jme3/network/kernel/tcp/NioEndpoint.java181
-rw-r--r--engine/src/networking/com/jme3/network/kernel/tcp/SelectorKernel.java472
-rw-r--r--engine/src/networking/com/jme3/network/kernel/tcp/SocketConnector.java150
-rw-r--r--engine/src/networking/com/jme3/network/kernel/udp/UdpConnector.java146
-rw-r--r--engine/src/networking/com/jme3/network/kernel/udp/UdpEndpoint.java140
-rw-r--r--engine/src/networking/com/jme3/network/kernel/udp/UdpKernel.java294
-rw-r--r--engine/src/networking/com/jme3/network/message/ChannelInfoMessage.java74
-rw-r--r--engine/src/networking/com/jme3/network/message/ClientRegistrationMessage.java76
-rw-r--r--engine/src/networking/com/jme3/network/message/CompressedMessage.java62
-rw-r--r--engine/src/networking/com/jme3/network/message/DisconnectMessage.java68
-rw-r--r--engine/src/networking/com/jme3/network/message/GZIPCompressedMessage.java53
-rw-r--r--engine/src/networking/com/jme3/network/message/ZIPCompressedMessage.java71
-rw-r--r--engine/src/networking/com/jme3/network/package.html14
-rw-r--r--engine/src/networking/com/jme3/network/rmi/LocalObject.java64
-rw-r--r--engine/src/networking/com/jme3/network/rmi/MethodDef.java58
-rw-r--r--engine/src/networking/com/jme3/network/rmi/ObjectDef.java69
-rw-r--r--engine/src/networking/com/jme3/network/rmi/ObjectStore.java344
-rw-r--r--engine/src/networking/com/jme3/network/rmi/RemoteMethodCallMessage.java89
-rw-r--r--engine/src/networking/com/jme3/network/rmi/RemoteMethodReturnMessage.java66
-rw-r--r--engine/src/networking/com/jme3/network/rmi/RemoteObject.java137
-rw-r--r--engine/src/networking/com/jme3/network/rmi/RemoteObjectDefMessage.java62
-rw-r--r--engine/src/networking/com/jme3/network/rmi/RmiSerializer.java267
-rw-r--r--engine/src/networking/com/jme3/network/serializing/Serializable.java49
-rw-r--r--engine/src/networking/com/jme3/network/serializing/Serializer.java423
-rw-r--r--engine/src/networking/com/jme3/network/serializing/SerializerException.java55
-rw-r--r--engine/src/networking/com/jme3/network/serializing/SerializerRegistration.java82
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/ArraySerializer.java155
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/BooleanSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/ByteSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/CharSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/CollectionSerializer.java114
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/DateSerializer.java55
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/DoubleSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/EnumSerializer.java67
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java191
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/FloatSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/GZIPSerializer.java98
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/IntSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/LongSerializer.java54
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/MapSerializer.java190
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/SavableSerializer.java120
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/SerializableSerializer.java56
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/ShortSerializer.java53
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/StringSerializer.java99
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/Vector3Serializer.java28
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/ZIPSerializer.java109
-rw-r--r--engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag13
-rw-r--r--engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md21
-rw-r--r--engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert16
-rw-r--r--engine/src/niftygui/com/jme3/cinematic/events/GuiTrack.java119
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java233
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/NiftyJmeDisplay.java181
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java393
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/RenderFontJme.java121
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/RenderImageJme.java89
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/SoundDeviceJme.java69
-rw-r--r--engine/src/niftygui/com/jme3/niftygui/SoundHandleJme.java111
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java48
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java473
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java239
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java892
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java85
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java468
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java302
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java86
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java138
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java85
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java73
-rw-r--r--engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html40
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.frag76
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.j3md41
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.vert22
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/Terrain.frag63
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/Terrain.j3md33
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/Terrain.vert23
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag625
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.j3md254
-rw-r--r--engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert107
-rw-r--r--engine/src/terrain/com/jme3/terrain/GeoMap.java365
-rw-r--r--engine/src/terrain/com/jme3/terrain/ProgressMonitor.java68
-rw-r--r--engine/src/terrain/com/jme3/terrain/Terrain.java209
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java1110
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java122
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/NormalRecalcControl.java106
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java514
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java47
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java21
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java210
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java983
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java1862
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java183
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java92
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java87
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java153
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java182
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java65
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java50
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java88
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java81
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java54
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java175
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java116
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java75
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java243
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamYUpGridTracer.java193
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPickData.java79
-rw-r--r--engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPicker.java56
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java485
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/CombinerHeightMap.java269
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/FaultHeightMap.java326
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/FluidSimHeightMap.java308
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/HeightMap.java158
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java23
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/HillHeightMap.java261
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMap.java177
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java79
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java30
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/MidpointDisplacementHeightMap.java273
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/Namer.java21
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/ParticleDepositionHeightMap.java397
-rw-r--r--engine/src/terrain/com/jme3/terrain/heightmap/RawHeightMap.java248
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/Basis.java77
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/Color.java134
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/Filter.java44
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java288
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java111
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java127
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java94
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java64
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java100
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java154
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java102
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java113
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java98
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java80
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java101
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java57
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java142
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java78
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java36
-rw-r--r--engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java34
-rw-r--r--engine/src/test/jme3test/TestChooser.java500
-rw-r--r--engine/src/test/jme3test/animation/SubtitleTrack.java37
-rw-r--r--engine/src/test/jme3test/animation/TestCameraMotionPath.java206
-rw-r--r--engine/src/test/jme3test/animation/TestCinematic.java336
-rw-r--r--engine/src/test/jme3test/animation/TestMotionPath.java196
-rw-r--r--engine/src/test/jme3test/app/TestApplication.java75
-rw-r--r--engine/src/test/jme3test/app/TestBareBonesApp.java90
-rw-r--r--engine/src/test/jme3test/app/TestChangeAppIcon.java75
-rw-r--r--engine/src/test/jme3test/app/TestContextRestart.java59
-rw-r--r--engine/src/test/jme3test/app/TestIDList.java164
-rw-r--r--engine/src/test/jme3test/app/TestReleaseDirectMemory.java70
-rw-r--r--engine/src/test/jme3test/app/TestTempVars.java114
-rw-r--r--engine/src/test/jme3test/app/state/RootNodeState.java54
-rw-r--r--engine/src/test/jme3test/app/state/TestAppStates.java100
-rw-r--r--engine/src/test/jme3test/asset/TestAbsoluteLocators.java71
-rw-r--r--engine/src/test/jme3test/asset/TestAssetCache.java171
-rw-r--r--engine/src/test/jme3test/asset/TestManyLocators.java89
-rw-r--r--engine/src/test/jme3test/asset/TestOnlineJar.java80
-rw-r--r--engine/src/test/jme3test/asset/TestUrlLoading.java80
-rw-r--r--engine/src/test/jme3test/audio/TestAmbient.java83
-rw-r--r--engine/src/test/jme3test/audio/TestDoppler.java104
-rw-r--r--engine/src/test/jme3test/audio/TestMusicPlayer.form117
-rw-r--r--engine/src/test/jme3test/audio/TestMusicPlayer.java298
-rw-r--r--engine/src/test/jme3test/audio/TestMusicStreaming.java56
-rw-r--r--engine/src/test/jme3test/audio/TestOgg.java68
-rw-r--r--engine/src/test/jme3test/audio/TestReverb.java79
-rw-r--r--engine/src/test/jme3test/audio/TestWav.java60
-rw-r--r--engine/src/test/jme3test/awt/AppHarness.java150
-rw-r--r--engine/src/test/jme3test/awt/TestApplet.java145
-rw-r--r--engine/src/test/jme3test/awt/TestAwtPanels.java88
-rw-r--r--engine/src/test/jme3test/awt/TestCanvas.java270
-rw-r--r--engine/src/test/jme3test/awt/TestSafeCanvas.java69
-rw-r--r--engine/src/test/jme3test/batching/TestBatchNode.java97
-rw-r--r--engine/src/test/jme3test/batching/TestBatchNodeCluster.java345
-rw-r--r--engine/src/test/jme3test/batching/TestBatchNodeTower.java251
-rw-r--r--engine/src/test/jme3test/blender/TestBlenderLoader.java83
-rw-r--r--engine/src/test/jme3test/bounding/TestRayCollision.java65
-rw-r--r--engine/src/test/jme3test/bullet/BombControl.java189
-rw-r--r--engine/src/test/jme3test/bullet/PhysicsHoverControl.java240
-rw-r--r--engine/src/test/jme3test/bullet/PhysicsTestHelper.java205
-rw-r--r--engine/src/test/jme3test/bullet/TestAttachDriver.java294
-rw-r--r--engine/src/test/jme3test/bullet/TestAttachGhostObject.java130
-rw-r--r--engine/src/test/jme3test/bullet/TestBoneRagdoll.java357
-rw-r--r--engine/src/test/jme3test/bullet/TestBrickTower.java236
-rw-r--r--engine/src/test/jme3test/bullet/TestBrickWall.java210
-rw-r--r--engine/src/test/jme3test/bullet/TestCcd.java152
-rw-r--r--engine/src/test/jme3test/bullet/TestCollisionGroups.java107
-rw-r--r--engine/src/test/jme3test/bullet/TestCollisionListener.java98
-rw-r--r--engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java137
-rw-r--r--engine/src/test/jme3test/bullet/TestFancyCar.java265
-rw-r--r--engine/src/test/jme3test/bullet/TestGhostObject.java117
-rw-r--r--engine/src/test/jme3test/bullet/TestHoveringTank.java299
-rw-r--r--engine/src/test/jme3test/bullet/TestKinematicAddToPhysicsSpaceIssue.java80
-rw-r--r--engine/src/test/jme3test/bullet/TestLocalPhysics.java122
-rw-r--r--engine/src/test/jme3test/bullet/TestPhysicsCar.java224
-rw-r--r--engine/src/test/jme3test/bullet/TestPhysicsCharacter.java207
-rw-r--r--engine/src/test/jme3test/bullet/TestPhysicsHingeJoint.java109
-rw-r--r--engine/src/test/jme3test/bullet/TestPhysicsReadWrite.java153
-rw-r--r--engine/src/test/jme3test/bullet/TestQ3.java179
-rw-r--r--engine/src/test/jme3test/bullet/TestRagDoll.java124
-rw-r--r--engine/src/test/jme3test/bullet/TestRagdollCharacter.java230
-rw-r--r--engine/src/test/jme3test/bullet/TestSimplePhysics.java111
-rw-r--r--engine/src/test/jme3test/bullet/TestWalkingChar.java430
-rw-r--r--engine/src/test/jme3test/collision/RayTrace.java102
-rw-r--r--engine/src/test/jme3test/collision/TestMousePick.java153
-rw-r--r--engine/src/test/jme3test/collision/TestRayCasting.java95
-rw-r--r--engine/src/test/jme3test/collision/TestTriangleCollision.java126
-rw-r--r--engine/src/test/jme3test/conversion/TestMipMapGen.java93
-rw-r--r--engine/src/test/jme3test/conversion/TestTriangleStrip.java75
-rw-r--r--engine/src/test/jme3test/effect/TestEverything.java207
-rw-r--r--engine/src/test/jme3test/effect/TestExplosionEffect.java277
-rw-r--r--engine/src/test/jme3test/effect/TestMovingParticle.java94
-rw-r--r--engine/src/test/jme3test/effect/TestParticleExportingCloning.java94
-rw-r--r--engine/src/test/jme3test/effect/TestPointSprite.java90
-rw-r--r--engine/src/test/jme3test/export/TestAssetLinkNode.java132
-rw-r--r--engine/src/test/jme3test/export/TestOgreConvert.java84
-rw-r--r--engine/src/test/jme3test/games/CubeField.java413
-rw-r--r--engine/src/test/jme3test/gui/TestBitmapFont.java134
-rw-r--r--engine/src/test/jme3test/gui/TestBitmapText3D.java70
-rw-r--r--engine/src/test/jme3test/gui/TestOrtho.java57
-rw-r--r--engine/src/test/jme3test/gui/TestSoftwareMouse.java123
-rw-r--r--engine/src/test/jme3test/gui/TestZOrder.java63
-rw-r--r--engine/src/test/jme3test/helloworld/HelloAnimation.java118
-rw-r--r--engine/src/test/jme3test/helloworld/HelloAssets.java91
-rw-r--r--engine/src/test/jme3test/helloworld/HelloAudio.java84
-rw-r--r--engine/src/test/jme3test/helloworld/HelloCollision.java178
-rw-r--r--engine/src/test/jme3test/helloworld/HelloEffects.java112
-rw-r--r--engine/src/test/jme3test/helloworld/HelloInput.java110
-rw-r--r--engine/src/test/jme3test/helloworld/HelloJME3.java62
-rw-r--r--engine/src/test/jme3test/helloworld/HelloLoop.java72
-rw-r--r--engine/src/test/jme3test/helloworld/HelloMaterial.java113
-rw-r--r--engine/src/test/jme3test/helloworld/HelloNode.java83
-rw-r--r--engine/src/test/jme3test/helloworld/HelloPhysics.java228
-rw-r--r--engine/src/test/jme3test/helloworld/HelloPicking.java180
-rw-r--r--engine/src/test/jme3test/helloworld/HelloTerrain.java117
-rw-r--r--engine/src/test/jme3test/helloworld/HelloTerrainCollision.java224
-rw-r--r--engine/src/test/jme3test/input/TestCameraNode.java153
-rw-r--r--engine/src/test/jme3test/input/TestChaseCamera.java160
-rw-r--r--engine/src/test/jme3test/input/TestControls.java73
-rw-r--r--engine/src/test/jme3test/input/TestJoystick.java51
-rw-r--r--engine/src/test/jme3test/input/combomoves/ComboMove.java143
-rw-r--r--engine/src/test/jme3test/input/combomoves/ComboMoveExecution.java126
-rw-r--r--engine/src/test/jme3test/input/combomoves/TestComboMoves.java217
-rw-r--r--engine/src/test/jme3test/light/TestEnvironmentMapping.java61
-rw-r--r--engine/src/test/jme3test/light/TestLightNode.java111
-rw-r--r--engine/src/test/jme3test/light/TestLightRadius.java109
-rw-r--r--engine/src/test/jme3test/light/TestManyLights.java54
-rw-r--r--engine/src/test/jme3test/light/TestPssmShadow.java176
-rw-r--r--engine/src/test/jme3test/light/TestShadow.java121
-rw-r--r--engine/src/test/jme3test/light/TestSimpleLighting.java112
-rw-r--r--engine/src/test/jme3test/light/TestSpotLight.java155
-rw-r--r--engine/src/test/jme3test/light/TestSpotLightTerrain.java217
-rw-r--r--engine/src/test/jme3test/light/TestTangentGen.java139
-rw-r--r--engine/src/test/jme3test/light/TestTangentGenBadModels.java136
-rw-r--r--engine/src/test/jme3test/light/TestTangentGenBadUV.java109
-rw-r--r--engine/src/test/jme3test/light/TestTransparentShadow.java140
-rw-r--r--engine/src/test/jme3test/material/TestBumpModel.java103
-rw-r--r--engine/src/test/jme3test/material/TestColoredTexture.java83
-rw-r--r--engine/src/test/jme3test/material/TestNormalMapping.java93
-rw-r--r--engine/src/test/jme3test/material/TestParallax.java172
-rw-r--r--engine/src/test/jme3test/material/TestSimpleBumps.java93
-rw-r--r--engine/src/test/jme3test/material/TestUnshadedModel.java44
-rw-r--r--engine/src/test/jme3test/math/TestHalfFloat.java55
-rw-r--r--engine/src/test/jme3test/model/TestHoverTank.java96
-rw-r--r--engine/src/test/jme3test/model/TestMonkeyHead.java100
-rw-r--r--engine/src/test/jme3test/model/TestObjLoading.java59
-rw-r--r--engine/src/test/jme3test/model/TestOgreLoading.java111
-rw-r--r--engine/src/test/jme3test/model/anim/TestAnimBlendBug.java131
-rw-r--r--engine/src/test/jme3test/model/anim/TestAnimationFactory.java85
-rw-r--r--engine/src/test/jme3test/model/anim/TestBlenderAnim.java93
-rw-r--r--engine/src/test/jme3test/model/anim/TestBlenderObjectAnim.java93
-rw-r--r--engine/src/test/jme3test/model/anim/TestCustomAnim.java144
-rw-r--r--engine/src/test/jme3test/model/anim/TestOgreAnim.java119
-rw-r--r--engine/src/test/jme3test/model/anim/TestOgreComplexAnim.java142
-rw-r--r--engine/src/test/jme3test/model/anim/TestSpatialAnim.java87
-rw-r--r--engine/src/test/jme3test/model/shape/TestBillboard.java111
-rw-r--r--engine/src/test/jme3test/model/shape/TestBox.java58
-rw-r--r--engine/src/test/jme3test/model/shape/TestCustomMesh.java153
-rw-r--r--engine/src/test/jme3test/model/shape/TestCylinder.java66
-rw-r--r--engine/src/test/jme3test/model/shape/TestDebugShapes.java95
-rw-r--r--engine/src/test/jme3test/model/shape/TestSphere.java65
-rw-r--r--engine/src/test/jme3test/network/MovingAverage.java64
-rw-r--r--engine/src/test/jme3test/network/TestChatClient.java156
-rw-r--r--engine/src/test/jme3test/network/TestChatServer.java133
-rw-r--r--engine/src/test/jme3test/network/TestLatency.java123
-rw-r--r--engine/src/test/jme3test/network/TestMessages.java88
-rw-r--r--engine/src/test/jme3test/network/TestNetworkStress.java66
-rw-r--r--engine/src/test/jme3test/network/TestRemoteCall.java117
-rw-r--r--engine/src/test/jme3test/network/TestSerialization.java158
-rw-r--r--engine/src/test/jme3test/network/TestThroughput.java118
-rw-r--r--engine/src/test/jme3test/niftygui/TestNiftyExamples.java65
-rw-r--r--engine/src/test/jme3test/niftygui/TestNiftyGui.java95
-rw-r--r--engine/src/test/jme3test/niftygui/TestNiftyToMesh.java89
-rw-r--r--engine/src/test/jme3test/post/BloomUI.java109
-rw-r--r--engine/src/test/jme3test/post/LightScatteringUI.java136
-rw-r--r--engine/src/test/jme3test/post/SSAOUI.java140
-rw-r--r--engine/src/test/jme3test/post/TestBloom.java161
-rw-r--r--engine/src/test/jme3test/post/TestCartoonEdge.java129
-rw-r--r--engine/src/test/jme3test/post/TestCrossHatch.java155
-rw-r--r--engine/src/test/jme3test/post/TestDepthOfField.java200
-rw-r--r--engine/src/test/jme3test/post/TestFBOPassthrough.java117
-rw-r--r--engine/src/test/jme3test/post/TestFog.java162
-rw-r--r--engine/src/test/jme3test/post/TestHDR.java101
-rw-r--r--engine/src/test/jme3test/post/TestLightScattering.java121
-rw-r--r--engine/src/test/jme3test/post/TestMultiRenderTarget.java219
-rw-r--r--engine/src/test/jme3test/post/TestMultiViewsFilters.java194
-rw-r--r--engine/src/test/jme3test/post/TestMultiplesFilters.java153
-rw-r--r--engine/src/test/jme3test/post/TestPostFilters.java179
-rw-r--r--engine/src/test/jme3test/post/TestPosterization.java155
-rw-r--r--engine/src/test/jme3test/post/TestRenderToMemory.java259
-rw-r--r--engine/src/test/jme3test/post/TestRenderToTexture.java148
-rw-r--r--engine/src/test/jme3test/post/TestSSAO.java96
-rw-r--r--engine/src/test/jme3test/post/TestTransparentCartoonEdge.java97
-rw-r--r--engine/src/test/jme3test/post/TestTransparentSSAO.java74
-rw-r--r--engine/src/test/jme3test/renderer/TestMultiViews.java109
-rw-r--r--engine/src/test/jme3test/renderer/TestParallelProjection.java83
-rw-r--r--engine/src/test/jme3test/scene/TestSceneLoading.java94
-rw-r--r--engine/src/test/jme3test/scene/TestUserData.java57
-rw-r--r--engine/src/test/jme3test/stress/TestBatchLod.java89
-rw-r--r--engine/src/test/jme3test/stress/TestLeakingGL.java91
-rw-r--r--engine/src/test/jme3test/stress/TestLodStress.java90
-rw-r--r--engine/src/test/jme3test/terrain/TerrainFractalGridTest.java147
-rw-r--r--engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java359
-rw-r--r--engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java179
-rw-r--r--engine/src/test/jme3test/terrain/TerrainGridTest.java239
-rw-r--r--engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java236
-rw-r--r--engine/src/test/jme3test/terrain/TerrainTest.java220
-rw-r--r--engine/src/test/jme3test/terrain/TerrainTestAdvanced.java298
-rw-r--r--engine/src/test/jme3test/terrain/TerrainTestCollision.java307
-rw-r--r--engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java439
-rw-r--r--engine/src/test/jme3test/terrain/TerrainTestReadWrite.java329
-rw-r--r--engine/src/test/jme3test/texture/TestSkyLoading.java59
-rw-r--r--engine/src/test/jme3test/texture/TestTexture3D.java103
-rw-r--r--engine/src/test/jme3test/texture/TestTexture3DLoading.java54
-rw-r--r--engine/src/test/jme3test/texture/TestTextureArray.java86
-rw-r--r--engine/src/test/jme3test/texture/UnshadedArray.frag50
-rw-r--r--engine/src/test/jme3test/texture/UnshadedArray.j3md70
-rw-r--r--engine/src/test/jme3test/texture/UnshadedArray.vert38
-rw-r--r--engine/src/test/jme3test/texture/tex3D.frag7
-rw-r--r--engine/src/test/jme3test/texture/tex3D.j3md16
-rw-r--r--engine/src/test/jme3test/texture/tex3D.vert11
-rw-r--r--engine/src/test/jme3test/texture/tex3DThumb.frag14
-rw-r--r--engine/src/test/jme3test/texture/tex3DThumb.j3md18
-rw-r--r--engine/src/test/jme3test/texture/tex3DThumb.vert11
-rw-r--r--engine/src/test/jme3test/tools/TestOctree.java165
-rw-r--r--engine/src/test/jme3test/tools/TestSaveGame.java84
-rw-r--r--engine/src/test/jme3test/tools/TestTextureAtlas.java90
-rw-r--r--engine/src/test/jme3test/water/TestPostWater.java298
-rw-r--r--engine/src/test/jme3test/water/TestPostWaterLake.java122
-rw-r--r--engine/src/test/jme3test/water/TestSceneWater.java142
-rw-r--r--engine/src/test/jme3test/water/TestSimpleWater.java166
-rw-r--r--engine/src/test/jme3test/water/WaterUI.java122
-rw-r--r--engine/src/tools/jme3tools/converters/Converter.java39
-rw-r--r--engine/src/tools/jme3tools/converters/FolderConverter.java80
-rw-r--r--engine/src/tools/jme3tools/converters/RGB565.java69
-rw-r--r--engine/src/tools/jme3tools/converters/model/FloatToFixed.java349
-rw-r--r--engine/src/tools/jme3tools/converters/model/ModelConverter.java183
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/EdgeInfo.java53
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/EdgeInfoVec.java50
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/FaceInfo.java59
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/FaceInfoVec.java54
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/IntVec.java71
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/PrimitiveGroup.java107
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/StripInfo.java355
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/StripInfoVec.java51
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/StripStartInfo.java49
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/Stripifier.java1365
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/TriStrip.java311
-rw-r--r--engine/src/tools/jme3tools/converters/model/strip/VertexCache.java100
-rw-r--r--engine/src/tools/jme3tools/optimize/FastOctnode.java169
-rw-r--r--engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java418
-rw-r--r--engine/src/tools/jme3tools/optimize/OCTTriangle.java80
-rw-r--r--engine/src/tools/jme3tools/optimize/Octnode.java317
-rw-r--r--engine/src/tools/jme3tools/optimize/Octree.java163
-rw-r--r--engine/src/tools/jme3tools/optimize/TestCollector.java54
-rw-r--r--engine/src/tools/jme3tools/optimize/TextureAtlas.java686
-rw-r--r--engine/src/tools/jme3tools/optimize/TriangleCollector.java248
-rw-r--r--engine/src/tools/jme3tools/optimize/pvsnotes40
-rw-r--r--engine/src/tools/jme3tools/savegame/SaveGame.java118
-rw-r--r--engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java1511
-rw-r--r--engine/src/xml/com/jme3/export/xml/DOMOutputCapsule.java825
-rw-r--r--engine/src/xml/com/jme3/export/xml/DOMSerializer.java237
-rw-r--r--engine/src/xml/com/jme3/export/xml/XMLExporter.java92
-rw-r--r--engine/src/xml/com/jme3/export/xml/XMLImporter.java116
1291 files changed, 265371 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..4b02ec4
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+ engine/src/android \
+ engine/src/core \
+ engine/src/core-plugins \
+ engine/src/ogre)
+
+LOCAL_MODULE := jmonkeyengine
+LOCAL_SDK_VERSION := 9
+LOCAL_MODULE_TAGS := optional
+
+# jMonkeyEngine needs these resources, but they will eventually get
+# stripped out even if they're compiled into the jar. You will need
+# to duplicate them into your project's assets. See the README for
+# more info.
+# LOCAL_JAVA_RESOURCE_DIRS := engine/src/core-data
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..34a719f
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,29033 @@
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html <==
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag <==
+==> engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md <==
+==> engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert <==
+==> engine/src/blender/.DS_Store <==
+==> engine/src/blender/com/jme3/.DS_Store <==
+==> engine/src/blender/com/jme3/asset/BlenderKey.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/asset/GeneratedTextureKey.java <==
+/**
+ * This key is mostly used to distinguish between textures that are loaded from
+ * the given assets and those being generated automatically. Every generated
+ * texture will have this kind of key attached.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java <==
+/**
+ * Class that holds information about the mesh.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java <==
+/*
+ *
+ * $Id: noise.c 14611 2008-04-29 08:24:33Z campbellbarton $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java <==
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> DXT1
+ * <li> DXT3
+ * <li> DXT5
+ * Not yet supported (but will be):
+ * <li> DXT1A:
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java <==
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> RGBA8
+ * <li> ABGR8
+ * <li> BGR8
+ * <li> RGB8
+ * Not yet supported (but will be):
+ * <li> ARGB4444:
+ * <li> RGB10:
+ * <li> RGB111110F:
+ * <li> RGB16:
+ * <li> RGB16F:
+ * <li> RGB16F_to_RGB111110F:
+ * <li> RGB16F_to_RGB9E5:
+ * <li> RGB32F:
+ * <li> RGB565:
+ * <li> RGB5A1:
+ * <li> RGB9E5:
+ * <li> RGBA16:
+ * <li> RGBA16F
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java <==
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> Luminance8
+ * <li> Luminance8Alpha8
+ * Not yet supported (but will be):
+ * <li> Luminance16:
+ * <li> Luminance16Alpha16:
+ * <li> Luminance16F:
+ * <li> Luminance16FAlpha16F:
+ * <li> Luminance32F:
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java <==
+/**
+ * An interface for texture blending classes (the classes that mix the texture
+ * pixels with the material colors).
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java <==
+/**
+ * An abstract class that contains the basic methods used by the classes that
+ * will derive from it.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */abstract class AbstractTextureBlender implements TextureBlender {
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java <==
+/**
+ * This class creates the texture blending class depending on the texture type.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java <==
+/**
+ * This class helps with projection calculations.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class UVProjectionGenerator {
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java <==
+/**
+ * The class that stores the pixel values of a texture.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat <==
+==> engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java <==
+/**
+ * A class that is used in mesh calculations.
+ * @author Marcin Roguski
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java <==
+/**
+ * A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize
+ * floating point operations errors.
+ * @author Marcin Roguski
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java <==
+/**
+ * A class that is used to load cameras into the scene.
+ * @author Marcin Roguski
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java <==
+/**
+ * This class helps to compute values from interpolation curves for features
+ * like animation or constraint influence. The curves are 3rd degree bezier
+ * curves.
+ *
+ * @author Marcin Roguski
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java <==
+/**
+ * This class is used to calculate bezier curves value for the given frames. The
+ * Ipo (interpolation object) consists of several b-spline curves (connected 3rd
+ * degree bezier curves) of a different type.
+ *
+ * @author Marcin Roguski
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java <==
+/**
+ * This class holds the basic data that describes a bone.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java <==
+/**
+ * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java <==
+/**
+ * The blender object's custom properties.
+ * This class is valid for all versions of blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java <==
+/**
+ * This modifier allows to array modifier to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class MirrorModifier extends Modifier {
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java <==
+/**
+ * This modifier allows to array modifier to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ArrayModifier extends Modifier {
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java <==
+/**
+ * This modifier allows to add animation to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ObjectAnimationModifier extends Modifier {
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java <==
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java <==
+/**
+ * This class represents an object's modifier. The modifier object can be varied
+ * and the user needs to know what is the type of it for the specified type
+ * name. For example "ArmatureModifierData" type specified in blender is
+ * represented by AnimData object from jMonkeyEngine.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java <==
+/**
+ * This modifier allows to add particles to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ParticlesModifier extends Modifier {
+==> engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java <==
+==> engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java <==
+/**
+ * An interface used in calculating alpha mask during particles' texture calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ interface IAlphaMask {
+==> engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java <==
+/**
+ * This class holds the data about the material.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java <==
+/**
+ * This class represents a single field in the structure. It can be either a primitive type or a table or a reference to
+ * another structure.
+ * @author Marcin Roguski
+ */
+/*package*/
+==> engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * This exception is thrown when blend file data is somehow invalid.
+==> engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java <==
+/**
+ * This class represents 'ChildOf' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintChildOf extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java <==
+/**
+ * The implementation of a constraint.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java <==
+/**
+ * This class represents 'Transform' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintTransform extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java <==
+/**
+ * This class represents 'Clamp to' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintClampTo extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java <==
+/**
+ * This class represents 'Rigid body joint' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintRigidBodyJoint extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java <==
+/**
+ * This class represents either owner or target of the constraint. It has the
+ * common methods that take the evalueation space of the feature.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class Feature {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java <==
+/**
+ * This class represents 'Min max' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintMinMax extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java <==
+/**
+ * This class represents 'Follow path' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintFollowPath extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java <==
+/**
+ * This class represents 'Null' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintNull extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java <==
+/**
+ * This class represents 'Dist limit' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintDistLimit extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java <==
+/**
+ * This class represents 'Python' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintPython extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java <==
+/**
+ * This class represents 'Loc limit' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintLocLimit extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java <==
+/**
+ * This class represents 'Action' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintAction extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/BlenderTrack.java <==
+/**
+ * This class holds either the bone track or spatial track. Is made to improve
+ * code readability.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */final class BlenderTrack implements Track {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java <==
+/**
+ * This class represents 'Action' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintLockTrack extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java <==
+/**
+ * This class represents 'Size like' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintSizeLike extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java <==
+/**
+ * The pivot constraint. Available for blender 2.50+.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintPivot extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java <==
+/**
+ * This class represents 'Size limit' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintSizeLimit extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java <==
+/**
+ * This class represents 'Shrink wrap' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintShrinkWrap extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java <==
+/**
+ * This class should be used for constraint calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java <==
+/**
+ * The damp track constraint. Available for blender 2.50+.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintDampTrack extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java <==
+/**
+ * The spline inverse kinematic constraint. Available for blender 2.50+.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintSplineInverseKinematic extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java <==
+/**
+ * This class represents 'Loc like' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintLocLike extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java <==
+/**
+ * This class represents 'Stretch to' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintStretchTo extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java <==
+/**
+ * This class represents 'Inverse kinematics' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintInverseKinematics extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java <==
+/**
+ * This class represents 'Rot like' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintRotLike extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java <==
+/**
+ * This class represents 'Rot limit' constraint type in blender.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ConstraintRotLimit extends Constraint {
+==> engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/blender/com/.DS_Store <==
+==> engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java <==
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A simple point, line, triangle or quad collisionShape based on one to four points-
+ * @author normenhansen
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster
+ * than using a regular mesh.
+ * There are a couple tricks though:
+ * -No rotation or translation is supported.
+ * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being
+ * equal on either side. If not, the whole collision box is shifted vertically and things don't collide
+ * as they should.
+ *
+ * @author Brent Owens
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/util/Converter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ *
+==> engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/system/Natives.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/system/JmeDesktopSystem.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/system/JmeCanvasContext.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/system/awt/AwtPanel.java <==
+==> engine/src/desktop/com/jme3/system/awt/PaintMode.java <==
+==> engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java <==
+==> engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/util/Screenshots.java <==
+==> engine/src/desktop/com/jme3/app/Monkey.png <==
+==> engine/src/desktop/com/jme3/app/AppletHarness.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/app/SettingsDialog.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/app/state/MjpegFileWriter.java <==
+/**
+ * Released under BSD License
+ * @author monceaux, normenhansen
+ */
+==> engine/src/desktop/com/jme3/app/state/VideoRecorderAppState.java <==
+/**
+ * A Video recording AppState that records the screen output into an AVI file with
+ * M-JPEG content. The file should be playable on any OS in any video player.<br/>
+ * The video recording starts when the state is attached and stops when it is detached
+ * or the application is quit. You can set the fileName of the file to be written when the
+ * state is detached, else the old file will be overwritten. If you specify no file
+ * the AppState will attempt to write a file into the user home directory, made unique
+ * by a timestamp.
+ * @author normenhansen, Robert McIntyre
+ */
+==> engine/src/desktop/com/jme3/app/state/ScreenshotAppState.java <==
+==> engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/jme3tools/converters/MipMapGenerator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/jme3tools/converters/ImageToAwt.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/desktop/jme3tools/navigation/RLSailing.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A utility class to package up a rhumb line sailing
+ *
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/MapModel3D.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A representation of the actual map in terms of lat/long and x,y,z co-ordinates.
+ * The Map class contains various helper methods such as methods for determining
+ * the world unit positions for lat/long coordinates and vice versa. This map projection
+ * does not handle screen/pixel coordinates.
+ *
+ * @author Benjamin Jakobus (thanks to Cormac Gebruers)
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/NavCalculator.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A utlity class for performing position calculations
+ *
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/InvalidPositionException.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/desktop/jme3tools/navigation/Coordinate.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Coordinate class. Used to store a coordinate in [DD]D MM.M format.
+ *
+ * @author Benjamin Jakobus (based on JMarine by Benjamin Jakobus and Cormac Gebruers)
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/NumUtil.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Provides various helper methods for number conversions (such as degree to radian
+ * conversion, decimal degree to radians etc)
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/MapModel2D.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A representation of the actual map in terms of lat/long and x,y co-ordinates.
+ * The Map class contains various helper methods such as methods for determining
+ * the pixel positions for lat/long co-ordinates and vice versa.
+ *
+ * @author Cormac Gebruers
+ * @author Benjamin Jakobus
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/Position.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * This class represents the position of an entity in the world.
+ *
+ * @author Benjamin Jakobus (based on JMarine by Cormac Gebruers and Benjamin Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/GCSailing.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A utility class to package up a great circle sailing.
+ *
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+==> engine/src/desktop/jme3tools/navigation/StringUtil.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A collection of String utilities.
+ *
+ * @author Benjamin Jakobus
+ * @version 1.0
+ */
+==> engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.vert <==
+==> engine/src/terrain/Common/MatDefs/Terrain/Terrain.j3md <==
+==> engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.frag <==
+==> engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag <==
+==> engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert <==
+==> engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.j3md <==
+==> engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.j3md <==
+==> engine/src/terrain/Common/MatDefs/Terrain/Terrain.frag <==
+==> engine/src/terrain/Common/MatDefs/Terrain/Terrain.vert <==
+==> engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Anthyon
+ */
+/**
+ * @Deprecated in favor of TerrainGridTileLoader
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A heightmap that is built off an image.
+ * If you want to be able to supply different Image types to
+ * ImageBaseHeightMapGrid, you need to implement this interface,
+ * and have that class extend Abstract heightmap.
+ *
+ * @author bowens
+ * @deprecated
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/CombinerHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/FaultHeightMap.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/MidpointDisplacementHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/Namer.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Loads Terrain grid tiles with image heightmaps.
+ * By default it expects a 16-bit grayscale image as the heightmap, but
+ * you can also call setImageType(BufferedImage.TYPE_) to set it to be a different
+ * image type. If you do this, you must also set a custom ImageHeightmap that will
+ * understand and be able to parse the image. By default if you pass in an image of type
+ * BufferedImage.TYPE_3BYTE_BGR, it will use the ImageBasedHeightMap for you.
+ *
+ * @author Anthyon, Brent Owens
+ */
+/**
+ * @Deprecated in favor of ImageTileLoader
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/RawHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/HillHeightMap.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/FluidSimHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/heightmap/HeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/terrain/com/jme3/terrain/heightmap/ParticleDepositionHeightMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/GeoMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/Terrain.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/ProgressMonitor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Monitor the progress of an expensive terrain operation.
+==> engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/Color.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+/**
+ * Helper class for working with colors and gradients
+ *
+ * @author Anthyon
+==> engine/src/terrain/com/jme3/terrain/noise/Filter.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/Basis.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+/**
+ * A simple aggregator basis. Takes two basis functions and a rate and return
+==> engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+/**
+==> engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+/**
+ * Interface for a general fractal basis.
+==> engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java <==
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/NormalRecalcControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java <==
+/**
+ * Computes the entropy value δ (delta) for a given terrain block and
+ * LOD level.
+ * See the geomipmapping paper section
+ * "2.3.1 Choosing the appropriate GeoMipMap level"
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java <==
+/**
+ * An LRU cache, based on <code>LinkedHashMap</code>.
+ *
+ * <p>
+ * This cache has a fixed maximum number of elements (<code>cacheSize</code>).
+ * If the cache is full and another entry is added, the LRU (least recently
+ * used) entry is dropped.
+ *
+ * <p>
+ * This class is thread-safe. All methods of this class are synchronized.
+ *
+==> engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Anthyon, normenhansen
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Anthyon, normenhansen
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPickData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamYUpGridTracer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPicker.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglCanvas.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglSmoothingTimer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglDisplay.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglContext.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglTimer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java <==
+==> engine/src/lwjgl/com/jme3/input/lwjgl/LwjglKeyInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/input/lwjgl/LwjglMouseInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/lwjgl/com/jme3/input/lwjgl/JInputJoyInput.java <==
+==> engine/src/networking/com/jme3/network/HostedConnection.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/package.html <==
+==> engine/src/networking/com/jme3/network/Network.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/ErrorListener.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/Endpoint.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/package.html <==
+==> engine/src/networking/com/jme3/network/kernel/Kernel.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/udp/UdpEndpoint.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/udp/UdpKernel.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/udp/UdpConnector.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/AbstractKernel.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/EndpointEvent.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/ConnectorException.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/KernelException.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/tcp/NioEndpoint.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/tcp/SocketConnector.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/tcp/SelectorKernel.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/Connector.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/NamedThreadFactory.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/kernel/Envelope.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/networking/com/jme3/network/rmi/RmiSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/LocalObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/ObjectStore.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/RemoteMethodCallMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/RemoteObjectDefMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/MethodDef.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/RemoteObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/RemoteMethodReturnMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/rmi/ObjectDef.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/Message.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/Filter.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/MessageConnection.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/ArraySerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine, Java Game Networking
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/ByteSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/DoubleSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/Vector3Serializer.java <==
+/**
+ * @author Kirill Vainer
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/SerializableSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/BooleanSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/SavableSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/StringSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/LongSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/CharSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/EnumSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/IntSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/MapSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/GZIPSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/FloatSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/CollectionSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/DateSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/ShortSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/ZIPSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine, Java Game Networking
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/SerializerRegistration.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/networking/com/jme3/network/serializing/SerializerException.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/Serializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/serializing/Serializable.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/Client.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/NetworkClient.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/MessageListener.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/package.html <==
+==> engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/TcpConnectorFactory.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/KernelFactory.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/DefaultServer.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/MessageProtocol.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/ConnectorFactory.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/KernelAdapter.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/DefaultClient.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/ConnectorAdapter.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/base/NioKernelFactory.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/message/DisconnectMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/message/GZIPCompressedMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/message/ZIPCompressedMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/message/ChannelInfoMessage.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/message/ClientRegistrationMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/message/CompressedMessage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/Server.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/AbstractMessage.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/ConnectionListener.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/Filters.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/networking/com/jme3/network/ClientStateListener.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/checkers/quals/Dependent.java <==
+/**
+ * Refines the qualified type of the annotated field or variable based on the
+ * qualified type of the receiver. The annotation declares a relationship
+ * between multiple type qualifier hierarchies.
+ *
+ * <p><b>Example:</b>
+ * Consider a field, {@code lock}, that is only initialized if the
+ * enclosing object (the receiver), is marked as {@code ThreadSafe}.
+ * Such a field can be declared as:
+ *
+ * <pre><code>
+ * private @Nullable @Dependent(result=NonNull.class, when=ThreadSafe.class)
+ * Lock lock;
+ * </code></pre>
+ */
+==> engine/src/core/checkers/quals/SubtypeOf.java <==
+/**
+ * A meta-annotation to specify all the qualifiers that the given qualifier
+ * is a subtype of. This provides a declarative way to specify the type
+ * qualifier hierarchy. (Alternatively, the hierarchy can be defined
+ * procedurally by subclassing {@link checkers.types.QualifierHierarchy} or
+ * {@link checkers.types.TypeHierarchy}.)
+ *
+ * <p>
+ * Example:
+ * <pre> @SubtypeOf( { Nullable.class } )
+ * public @interface NonNull { }
+ * </pre>
+ *
+ * <p>
+ *
+ * If a qualified type is a subtype of the same type without any qualifier,
+ * then use <code>Unqualified.class</code> in place of a type qualifier
+ * class. For example, to express that <code>@Encrypted <em>C</em></code>
+ * is a subtype of <code><em>C</em></code> (for every class
+ * <code><em>C</em></code>), and likewise for <code>@Interned</code>, write:
+ *
+ * <pre> @SubtypeOf(Unqualified.class)
+ * public @interface Encrypted { }
+ *
+ * &#64;SubtypeOf(Unqualified.class)
+ * public @interface Interned { }
+ * </pre>
+ *
+ * <p>
+ *
+ * For the root type qualifier in the qualifier hierarchy (i.e., the
+==> engine/src/core/checkers/quals/DefaultLocation.java <==
+/**
+ * Specifies the locations to which a {@link DefaultQualifier} annotation applies.
+ *
+ * @see DefaultQualifier
+ */
+==> engine/src/core/checkers/quals/DefaultQualifier.java <==
+/**
+ * Applied to a declaration of a package, type, method, variable, etc.,
+ * specifies that the given annotation should be the default. The default is
+ * applied to all types within the declaration for which no other
+ * annotation is explicitly written.
+ * If multiple DefaultQualifier annotations are in scope, the innermost one
+ * takes precedence.
+ * DefaultQualifier takes precedence over {@link DefaultQualifierInHierarchy}.
+ * <p>
+ *
+ * If you wish to write multiple @DefaultQualifier annotations (for
+ * unrelated type systems, or with different {@code locations} fields) at
+ * the same location, use {@link DefaultQualifiers}.
+ *
+ * @see DefaultLocation
+ */
+==> engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java <==
+/**
+ * Indicates that the annotated qualifier is the default qualifier in the
+ * qualifier hierarchy: it applies if the programmer writes no explicit
+ * qualifier.
+ * <p>
+ *
+ * The {@link DefaultQualifier} annotation, which targets Java code elements,
+ * takes precedence over {@code DefaultQualifierInHierarchy}.
+ * <p>
+ *
+ * Each type qualifier hierarchy may have at most one qualifier marked as
+ * {@code DefaultQualifierInHierarchy}.
+ *
+ * @see checkers.quals.DefaultQualifier
+ */
+==> engine/src/core/checkers/quals/Unused.java <==
+/**
+ * Declares that the field may not be accessed if the receiver is of the
+ * specified qualifier type (or any supertype).
+ *
+ * This property is verified by the checker that type-checks the {@code
+ * when} element value qualifier.
+ *
+ * <p><b>Example</b>
+ * Consider a class, {@code Table}, with a locking field, {@code lock}. The
+ * lock is used when a {@code Table} instance is shared across threads. When
+ * running in a local thread, the {@code lock} field ought not to be used.
+ *
+ * You can declare this behavior in the following way:
+ *
+ * <pre><code>
+ * class Table {
+ * private @Unused(when=LocalToThread.class) final Lock lock;
+ * ...
+ * }
+ * </code></pre>
+ *
+ * The checker for {@code @LocalToThread} would issue an error for the following code:
+ *
+ * <pre> @LocalToThread Table table = ...;
+ * ... table.lock ...;
+ * </pre>
+ *
+ */
+==> engine/src/core/checkers/quals/package-info.java <==
+/**
+ * Contains the basic annotations to be used by all type systems
+ * and meta-annotations to qualify annotations (qualifiers).
+ *
+ * They may serve as documentation for the type qualifiers, and aid the
+ * Checker Framework to infer the relations between the type qualifiers.
+ *
+ * @checker.framework.manual #writing-a-checker Writing a checker
+ */
+==> engine/src/core/checkers/quals/DefaultQualifiers.java <==
+/**
+ * Specifies the annotations to be included in a type without having to provide
+ * them explicitly.
+ *
+ * This annotation permits specifying multiple default qualifiers for more
+ * than one type system. It is necessary because Java forbids multiple
+ * annotations of the same name at a single location.
+ *
+ * Example:
+ * <!-- &nbsp; is a hack that prevents @ from being the first charater on the line, which confuses Javadoc -->
+ * <code><pre>
+ * &nbsp; @DefaultQualifiers({
+ * &nbsp; @DefaultQualifier("NonNull"),
+ * &nbsp; @DefaultQualifier(value = "Interned", locations = ALL_EXCEPT_LOCALS),
+ * &nbsp; @DefaultQualifier("Tainted")
+ * &nbsp; })
+ * </pre></code>
+ *
+ * @see DefaultQualifier
+ */
+==> engine/src/core/checkers/quals/TypeQualifier.java <==
+/**
+ * A meta-annotation indicating that the annotated annotation is a type
+ * qualifier.
+ *
+ * Examples of such qualifiers: {@code @ReadOnly}, {@code @NonNull}
+ */
+==> engine/src/core/checkers/quals/Unqualified.java <==
+/**
+ * A special annotation intended solely for representing an unqualified type in
+ * the qualifier hierarchy, as an argument to {@link SubtypeOf#value()},
+ * in the type qualifiers declarations.
+ *
+ * <p>
+ * Programmers cannot write this in source code.
+ */
+==> engine/src/core/com/jme3/animation/LoopMode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/animation/SkeletonControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * The Skeleton control deforms a model according to a skeleton,
+ * It handles the computation of the deformation matrices and performs
+ * the transformations on the mesh
+ *
+ * @author Rémy Bouquet Based on AnimControl by Kirill Vainer
+ */
+==> engine/src/core/com/jme3/animation/package.html <==
+==> engine/src/core/com/jme3/animation/PoseTrack.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/BoneAnimation.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
+==> engine/src/core/com/jme3/animation/Pose.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/AnimControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/Bone.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/CompactArray.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/CompactVector3Array.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/SpatialTrack.java <==
+/**
+ * This class represents the track for spatial animation.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/animation/Track.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/Animation.java <==
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/BoneTrack.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/Skeleton.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/AnimationFactory.java <==
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/CompactQuaternionArray.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/animation/SpatialAnimation.java <==
+/**
+ * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
+ */
+==> engine/src/core/com/jme3/animation/AnimEventListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/animation/AnimChannel.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/JmeSystemDelegate.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/SystemListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/system/JmeContext.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/NullContext.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/JmeVersion.java <==
+==> engine/src/core/com/jme3/system/JmeSystem.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/NanoTimer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/system/NullRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/Timer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * <code>Timer</code> is the base class for a high resolution timer. It is
+==> engine/src/core/com/jme3/system/Platform.java <==
+==> engine/src/core/com/jme3/system/AppSettings.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/system/Annotations.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioKey.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioParam.java <==
+==> engine/src/core/com/jme3/audio/SeekableStream.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/core/com/jme3/audio/LowPassFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioStream.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/audio/ListenerParam.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/Filter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioContext.java <==
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/audio/Environment.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/Listener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/audio/AudioNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shadow/PssmShadowUtil.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shadow/PssmShadowRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * <p/>
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shadow/ShadowCamera.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shadow/BasicShadowRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shadow/ShadowUtil.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/Renderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/RendererException.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/renderer/package.html <==
+==> engine/src/core/com/jme3/renderer/queue/GuiComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/queue/NullComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/queue/GeometryComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/queue/TransparentComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/queue/GeometryList.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/queue/RenderQueue.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/RenderContext.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/GL1Renderer.java <==
+/**
+ * Renderer sub-interface that is used for non-shader based renderers.
+ * <p>
+ * The <code>GL1Renderer</code> provides a single call,
+ * {@link #setFixedFuncBinding(com.jme3.material.FixedFuncBinding, java.lang.Object) }
+ * which allows to set fixed functionality state.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/renderer/RenderManager.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/ViewPort.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/Statistics.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/IDList.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/Camera.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/renderer/Caps.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Line.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/AbstractTriangle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/LineSegment.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Spline.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/core/com/jme3/math/package.html <==
+==> engine/src/core/com/jme3/math/Matrix3f.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Eigen3f.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/CurveAndSurfaceMath.java <==
+/**
+ * This class offers methods to help with curves and surfaces calculations.
+ * @author Marcin Roguski (Kealthas)
+ */
+==> engine/src/core/com/jme3/math/Transform.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Quaternion.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Triangle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Matrix4f.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/FastMath.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Vector4f.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Plane.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Rectangle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/ColorRGBA.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/math/Ray.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Vector2f.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Ring.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/math/Vector3f.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/UnsupportedCollisionException.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/collision/CollisionResult.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/bih/TriangleAxisComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/bih/BIHNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/bih/BIHTree.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/bih/BIHTriangle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/MotionAllowedListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/Collidable.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/collision/SweepSphere.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/collision/CollisionResults.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/LineWrapMode.java <==
+/**
+ * Line-wrap type for BitmapText
+ * @author YongHoon
+ */
+==> engine/src/core/com/jme3/font/BitmapCharacter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/Letters.java <==
+/**
+ * Manage and align LetterQuads
+ * @author YongHoon
+ */
+==> engine/src/core/com/jme3/font/Kerning.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/LetterQuad.java <==
+/**
+ * LetterQuad contains the position, color, uv texture information for a character in text.
+ * @author YongHoon
+ */
+==> engine/src/core/com/jme3/font/StringBlock.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/BitmapText.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/BitmapCharacterSet.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/Rectangle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/font/BitmapFont.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/font/ColorTags.java <==
+/**
+ * Contains the color information tagged in a text string
+ * Format: \#rgb#
+ * \#rgba#
+ * \#rrggbb#
+ * \#rrggbbaa#
+ * @author YongHoon
+ */
+==> engine/src/core/com/jme3/font/BitmapTextPage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/texture/Image.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/texture/Texture.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/texture/FrameBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/texture/TextureArray.java <==
+/**
+ * This class implements a Texture array
+ * warning, this feature is only supported on opengl 3.0 version.
+ * To check if a hardware supports TextureArray check :
+ * renderManager.getRenderer().getCaps().contains(Caps.TextureArray)
+ * @author phate666
+ */
+==> engine/src/core/com/jme3/texture/Texture3D.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/texture/Texture2D.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/texture/TextureCubeMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/BufferUtils.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/SkyFactory.java <==
+/**
+ * <code>SkyFactory</code> is used to create jME {@link Spatial}s that can
+ * be attached to the scene to display a sky image in the background.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/util/IntMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/NativeObjectManager.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/TangentBinormalGenerator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/SafeArrayList.java <==
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/JmeFormatter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/PlaceholderAssets.java <==
+==> engine/src/core/com/jme3/util/xml/SAXUtil.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/NativeObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/util/LittleEndien.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/SortUtil.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/ListMap.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java <==
+==> engine/src/core/com/jme3/util/blockparser/Statement.java <==
+==> engine/src/core/com/jme3/util/TempVars.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/Technique.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/package.html <==
+==> engine/src/core/com/jme3/material/TechniqueDef.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/Material.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * <p/>
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/MaterialList.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/FixedFuncBinding.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/material/RenderState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/MatParam.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/MaterialDef.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/material/MatParamTexture.java <==
+==> engine/src/core/com/jme3/export/ReadListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/export/JmeImporter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/export/FormatVersion.java <==
+/**
+ * Specifies the version of the format for jME3 object (j3o) files.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/export/SavableClassUtil.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/export/OutputCapsule.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/export/JmeExporter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/export/InputCapsule.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/export/Savable.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/package.html <==
+==> engine/src/core/com/jme3/asset/Asset.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/asset/AssetEventListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/asset/AssetCache.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/AssetLoadException.java <==
+/**
+ * <code>AssetLoadException</code> is thrown when the {@link AssetManager}
+ * is able to find the requested asset, but there was a problem while loading
+ * it.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/asset/DesktopAssetManager.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/AssetNotFoundException.java <==
+/**
+ * <code>AssetNotFoundException</code> is thrown when the {@link AssetManager}
+ * is unable to locate the requested asset using any of the registered
+ * {@link AssetLocator}s.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/asset/AssetManager.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/AssetConfig.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/AssetLocator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/asset/TextureKey.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/AssetLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/ThreadingManager.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/AssetInfo.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/ModelKey.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/Desktop.cfg <==
+==> engine/src/core/com/jme3/asset/MaterialKey.java <==
+/**
+ * Used for loading {@link Material materials} only (not material definitions).
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/asset/AssetKey.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/asset/ImplHandler.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/package.html <==
+==> engine/src/core/com/jme3/app/StatsView.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/state/package.html <==
+==> engine/src/core/com/jme3/app/state/AppState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/state/AbstractAppState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/state/AppStateManager.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/SimpleApplication.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/StatsAppState.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/FlyCamAppState.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/Application.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/DebugKeysAppState.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/app/AppTask.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/Particle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/package.html <==
+==> engine/src/core/com/jme3/effect/ParticleComparator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/ParticleMesh.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/ParticlePointMesh.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/influencers/NewtonianParticleInfluencer.java <==
+/**
+ * This influencer calculates initial velocity with the use of the emitter's shape.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/influencers/ParticleInfluencer.java <==
+/**
+ * An interface that defines the methods to affect initial velocity of the particles.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/influencers/EmptyParticleInfluencer.java <==
+/**
+ * This influencer does not influence particle at all.
+ * It makes particles not to move.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java <==
+/**
+ * This emitter influences the particles so that they move all in the same direction.
+ * The direction may vary a little if the velocity variation is non zero.
+ * This influencer is default for the particle emitter.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterBoxShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java <==
+/**
+ * This emiter shape emits the particles from the given shape's vertices
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterPointShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterMeshFaceShape.java <==
+/**
+ * This emiter shape emits the particles from the given shape's faces.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java <==
+/**
+ * This emiter shape emits the particles from the given shape's interior constrained by its convex hull
+ * (a geometry that tightly wraps the mesh). So in case of multiple meshes some vertices may appear
+ * in a space between them.
+ * @author Marcin Roguski (Kaelthas)
+ */
+==> engine/src/core/com/jme3/effect/shapes/EmitterSphereShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/ParticleEmitter.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/effect/ParticleTriMesh.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/UniformBinding.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/Attribute.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/shader/Shader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/Uniform.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/DefineList.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/ShaderUtils.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/ShaderKey.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/ShaderVariable.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/shader/VarType.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/mesh/WrappedIndexBuffer.java <==
+/**
+ * <code>WrappedIndexBuffer</code> converts vertex indices from a non list based
+ * mesh mode such as {@link Mode#TriangleStrip} or {@link Mode#LineLoop}
+ * into a list based mode such as {@link Mode#Triangles} or {@link Mode#Lines}.
+ * As it is often more convenient to read vertex data in list format
+ * than in a non-list format, using this class is recommended to avoid
+ * convoluting classes used to process mesh data from an external source.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/scene/mesh/package.html <==
+==> engine/src/core/com/jme3/scene/mesh/IndexByteBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/mesh/IndexBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/mesh/IndexIntBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/mesh/IndexShortBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/mesh/VirtualIndexBuffer.java <==
+/**
+ * IndexBuffer implementation that generates vertex indices sequentially
+ * based on a specific Mesh {@link Mode}.
+ * The generated indices are as if the mesh is in the given mode
+ * but contains no index buffer, thus this implementation will
+ * return the indices if the index buffer was there and contained sequential
+ * triangles.
+ * Example:
+ * <ul>
+ * <li>{@link Mode#Triangles}: 0, 1, 2 | 3, 4, 5 | 6, 7, 8 | ...</li>
+ * <li>{@link Mode#TriangleStrip}: 0, 1, 2 | 2, 1, 3 | 2, 3, 4 | ...</li>
+ * <li>{@link Mode#TriangleFan}: 0, 1, 2 | 0, 2, 3 | 0, 3, 4 | ...</li>
+ * </ul>
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/scene/package.html <==
+==> engine/src/core/com/jme3/scene/SceneGraphVisitorAdapter.java <==
+/**
+ * <code>SceneGraphVisitorAdapter</code> is used to traverse the scene
+ * graph tree. The adapter version of the interface simply separates
+ * between the {@link Geometry geometries} and the {@link Node nodes} by
+ * supplying visit methods that take them.
+ * Use by calling {@link Spatial#depthFirstTraversal(com.jme3.scene.SceneGraphVisitor) }
+ * or {@link Spatial#breadthFirstTraversal(com.jme3.scene.SceneGraphVisitor)}.
+ */
+==> engine/src/core/com/jme3/scene/Mesh.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/SimpleBatchNode.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * SimpleBatchNode comes with some restrictions, but can yield better performances.
+ * Geometries to be batched has to be attached directly to the BatchNode
+ * You can't attach a Node to a SimpleBatchNode
+ * SimpleBatchNode is recommended when you have a large number of geometries using the same material that does not require a complex scene graph structure.
+ * @see BatchNode
+ * @author Nehon
+ */
+==> engine/src/core/com/jme3/scene/BatchNode.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/UserData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/CameraNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/AssetLinkNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/VertexBuffer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/Geometry.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/SkeletonDebugger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/WireBox.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/WireSphere.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/Arrow.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/SkeletonPoints.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/SkeletonWire.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/Grid.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/debug/WireFrustum.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Line.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Sphere.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Cylinder.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Curve.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/PQTorus.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Box.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Quad.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Torus.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/StripBox.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Dome.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/shape/Surface.java <==
+/**
+ * This class represents a surface described by knots, weights and control points.
+ * Currently the following types are supported:
+ * a) NURBS
+ * @author Marcin Roguski (Kealthas)
+ */
+==> engine/src/core/com/jme3/scene/shape/AbstractBox.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/Spatial.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/Node.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/LightNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/SceneGraphVisitor.java <==
+/**
+ * <code>SceneGraphVisitorAdapter</code> is used to traverse the scene
+ * graph tree.
+ * Use by calling {@link Spatial#depthFirstTraversal(com.jme3.scene.SceneGraphVisitor) }
+ * or {@link Spatial#breadthFirstTraversal(com.jme3.scene.SceneGraphVisitor)}.
+ */
+==> engine/src/core/com/jme3/scene/CollisionData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/package.html <==
+==> engine/src/core/com/jme3/scene/control/LodControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/AbstractControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/BillboardControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/LightControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/AreaUtils.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/CameraControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/UpdateControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/scene/control/Control.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/KeyInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/TouchInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/Joystick.java <==
+/**
+ * A joystick represents a single joystick that is installed in the system.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/controls/Trigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/controls/AnalogListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/controls/package.html <==
+==> engine/src/core/com/jme3/input/controls/ActionListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/controls/JoyButtonTrigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/controls/KeyTrigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/controls/MouseAxisTrigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/controls/TouchTrigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/controls/InputListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/controls/TouchListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/controls/JoyAxisTrigger.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/package.html <==
+==> engine/src/core/com/jme3/input/event/package.html <==
+==> engine/src/core/com/jme3/input/event/MouseButtonEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/event/JoyButtonEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/event/JoyAxisEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/event/KeyInputEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/event/TouchEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * <code>TouchEvent</code> represents a single event from multi-touch input devices
+==> engine/src/core/com/jme3/input/event/InputEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/event/MouseMotionEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/KeyNames.java <==
+==> engine/src/core/com/jme3/input/Input.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/RawInputListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/dummy/package.html <==
+==> engine/src/core/com/jme3/input/dummy/DummyInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/dummy/DummyKeyInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/dummy/DummyMouseInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/InputManager.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/MouseInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/FlyByCamera.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/input/JoyInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/input/ChaseCamera.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/bounding/OrientedBoundingBox.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/bounding/Intersection.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/bounding/BoundingSphere.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/bounding/BoundingVolume.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/bounding/BoundingBox.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/PlayState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/core/com/jme3/cinematic/MotionPathListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/MotionPath.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/KeyFrame.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/Cinematic.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/events/RotationTrack.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ * @deprecated use spatial animation instead.
+ */
+==> engine/src/core/com/jme3/cinematic/events/CinematicEvent.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/events/SoundTrack.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/events/CinematicEventListener.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/core/com/jme3/cinematic/events/ScaleTrack.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ * @deprecated use spatial animation instead.
+ */
+==> engine/src/core/com/jme3/cinematic/events/PositionTrack.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ * @deprecated use spatial animation instead.
+ */
+==> engine/src/core/com/jme3/cinematic/events/MotionTrack.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/events/AnimationTrack.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/cinematic/TimeLine.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/post/package.html <==
+==> engine/src/core/com/jme3/post/Filter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/post/SceneProcessor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/post/PreDepthProcessor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/post/FilterPostProcessor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/post/HDRRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/light/SpotLight.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/light/package.html <==
+==> engine/src/core/com/jme3/light/AmbientLight.java <==
+/**
+ * An ambient light adds a constant color to the scene.
+ * <p>
+ * Ambient lights are unaffected by the surface normal, and are constant
+ * regardless of the model's location. The material's ambient color is
+ * multiplied by the ambient light color to get the final ambient color of
+ * an object.
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core/com/jme3/light/PointLight.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/light/Light.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/light/LightList.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/light/DirectionalLight.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core/com/jme3/ui/Picture.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_SixDofJoint */
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: getRotationalLimitMotor
+ * Signature: (JI)J
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: getTranslationalLimitMotor
+ * Signature: (J)J
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setLinearUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+==> engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_PhysicsSpace */
+/* Inaccessible static: pQueueTL */
+/* Inaccessible static: physicsSpaceTL */
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: createPhysicsSpace
+ * Signature: (FFFFFFIZ)J
+ */
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: stepSimulation
+ * Signature: (JFIF)V
+ */
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+==> engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_motors_RotationalLimitMotor */
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getLoLimit
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setLoLimit
+ * Signature: (JF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getHiLimit
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+==> engine/src/bullet-native/jmeClasses.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp <==
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulseLateral1
+ * Signature: (J)F
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_util_DebugShapeFactory */
+/*
+ * Class: com_jme3_bullet_util_DebugShapeFactory
+ * Method: getVertices
+ * Signature: (JLcom/jme3/bullet/util/DebugMeshCallback;)V
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_objects_PhysicsRigidBody */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_objects_PhysicsCharacter */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_PlaneCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_PlaneCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;F)J
+ */
+==> engine/src/bullet-native/jmePhysicsSpace.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_GImpactCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape
+ * Method: createShape
+ * Signature: (J)J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_HullCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_HullCollisionShape
+ * Method: createShape
+ * Signature: (Ljava/nio/ByteBuffer;)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_CapsuleCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CapsuleCollisionShape
+ * Method: createShape
+ * Signature: (IFF)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_objects_PhysicsGhostObject */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_SphereCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_SphereCollisionShape
+ * Method: createShape
+ * Signature: (F)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_SimplexCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;)J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+==> engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.cpp <==
+/**
+ * Author: Normen Hansen
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: enableString
+ * Signature: (JIZ)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setStiffness
+ * Signature: (JIF)V
+ */
+/*
+==> engine/src/bullet-native/jmeMotionState.h <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+==> engine/src/bullet-native/android/Android.mk <==
+==> engine/src/bullet-native/android/Application.mk <==
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_objects_VehicleWheel */
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getWheelLocation
+ * Signature: (JILcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getWheelRotation
+ * Signature: (JILcom/jme3/math/Matrix3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: applyInfo
+ * Signature: (JIFFFFFFFFZF)V
+ */
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_BoxCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_BoxCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;)J
+ */
+==> engine/src/bullet-native/jmeMotionState.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_HingeJoint */
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: enableMotor
+ * Signature: (JZFF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getEnableAngularMotor
+ * Signature: (J)Z
+ */
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getMotorTargetVelocity
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_ConeCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_ConeCollisionShape
+ * Method: createShape
+ * Signature: (IFF)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_PhysicsJoint */
+/*
+ * Class: com_jme3_bullet_joints_PhysicsJoint
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_joints_PhysicsJoint
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_CylinderCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CylinderCollisionShape
+ * Method: createShape
+ * Signature: (ILcom/jme3/math/Vector3f;)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_HeightfieldCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_HeightfieldCollisionShape
+ * Method: createShape
+ * Signature: (IILjava/nio/ByteBuffer;FFFIZ)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_util_NativeMeshUtil */
+/*
+ * Class: com_jme3_bullet_util_NativeMeshUtil
+ * Method: createTriangleIndexVertexArray
+ * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;IIII)J
+ */
+==> engine/src/bullet-native/jmeClasses.h <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+==> engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_PhysicsCollisionEvent */
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulseLateral1
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_ConeJoint */
+/*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: setLimit
+ * Signature: (JFFF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: setAngularOnly
+ * Signature: (JZ)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_PhysicsCollisionObject */
+==> engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen, CJ Hare
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/jmePhysicsSpace.h <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/jmeBulletUtil.h <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_CompoundCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: createShape
+ * Signature: ()J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: addChildShape
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;)J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: removeChildShape
+ * Signature: (JJ)J
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_SixDofSpringJoint */
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: enableSpring
+ * Signature: (JIZ)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setStiffness
+ * Signature: (JIF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setDamping
+ * Signature: (JIF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+==> engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_objects_infos_RigidBodyMotionState */
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: createMotionState
+ * Signature: ()J
+ */
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: applyTransform
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Quaternion;)Z
+ */
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_Point2PointJoint */
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setImpulseClamp
+ * Signature: (JF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setTau
+ * Signature: (JF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+==> engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_objects_PhysicsVehicle */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_CollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: getMargin
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: setLocalScaling
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: setMargin
+ * Signature: (JF)V
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+==> engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_motors_TranslationalLimitMotor */
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+==> engine/src/bullet-native/jmeBulletUtil.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.cpp <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Author: Normen Hansen
+ */
+==> engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_collision_shapes_MeshCollisionShape */
+/*
+ * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape
+ * Method: createShape
+ * Signature: (J)J
+ */
+/*
+ * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+==> engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.h <==
+/* DO NOT EDIT THIS FILE - it is machine generated */
+/* Header for class com_jme3_bullet_joints_SliderJoint */
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getLowerLinLimit
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setLowerLinLimit
+ * Signature: (JF)V
+ */
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getUpperLinLimit
+ * Signature: (J)F
+ */
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+==> engine/src/android/res/values/strings.xml <==
+==> engine/src/android/res/menu/options.xml <==
+==> engine/src/android/res/layout/tests.xml <==
+==> engine/src/android/res/layout/about.xml <==
+==> engine/src/android/com/jme3/system/android/JmeAndroidSystem.java <==
+==> engine/src/android/com/jme3/system/android/AndroidTimer.java <==
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/com/jme3/system/android/OGLESContext.java <==
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/com/jme3/system/android/AndroidConfigChooser.java <==
+/**
+ * AndroidConfigChooser is used to determine the best suited EGL Config
+ * @author larynx
+ *
+ */
+==> engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java <==
+==> engine/src/android/com/jme3/audio/android/AndroidAudioData.java <==
+==> engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/com/jme3/renderer/android/Android22Workaround.java <==
+==> engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/com/jme3/renderer/android/TextureUtil.java <==
+==> engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java <==
+==> engine/src/android/com/jme3/util/RingBuffer.java <==
+/**
+ * Ring buffer (fixed size queue) implementation using a circular array (array with wrap-around).
+ */
+==> engine/src/android/com/jme3/util/FastInteger.java <==
+/**
+ * The wrapper for the primitive type {@code int}.
+ * <p>
+ * As with the specification, this implementation relies on code laid out in <a
+ * href="http://www.hackersdelight.org/">Henry S. Warren, Jr.'s Hacker's
+ * Delight, (Addison Wesley, 2002)</a> as well as <a
+ * href="http://aggregate.org/MAGIC/">The Aggregate's Magic Algorithms</a>.
+ *
+ * @see java.lang.Number
+ * @since 1.1
+ */
+==> engine/src/android/com/jme3/util/AndroidLogHandler.java <==
+==> engine/src/android/com/jme3/asset/AndroidImageInfo.java <==
+/**
+==> engine/src/android/com/jme3/asset/AndroidAssetManager.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/com/jme3/asset/plugins/AndroidLocator.java <==
+==> engine/src/android/com/jme3/app/R.java <==
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+==> engine/src/android/com/jme3/app/AndroidHarness.java <==
+/**
+ * <code>AndroidHarness</code> wraps a jme application object and runs it on
+ * Android
+==> engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java <==
+/**
+ * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens
+ * For use with class AndroidInput
+ * @author larynx
+ *
+ */
+==> engine/src/android/com/jme3/input/android/AndroidInput.java <==
+/**
+ * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
+ * @author larynx
+ *
+ */
+==> engine/src/android/jme3test/android/R.java <==
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+==> engine/src/android/jme3test/android/SimpleSoundTest.java <==
+==> engine/src/android/jme3test/android/DemoMainActivity.java <==
+==> engine/src/android/jme3test/android/DemoLaunchEntry.java <==
+/**
+ * Name (=appClass) and Description of one demo launch inside the main apk
+ * @author larynx
+ *
+ */
+==> engine/src/android/jme3test/android/TestAmbient.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/jme3test/android/TestMovingParticle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/jme3test/android/AndroidManifest.xml <==
+==> engine/src/android/jme3test/android/DemoAndroidHarness.java <==
+==> engine/src/android/jme3test/android/TestNormalMapping.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/jme3test/android/TestBumpModel.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/jme3test/android/TestUnshadedModel.java <==
+==> engine/src/android/jme3test/android/DemoLaunchAdapter.java <==
+/**
+ * The view adapter which gets a list of LaunchEntries and displaqs them
+ * @author larynx
+ *
+ */
+==> engine/src/android/jme3test/android/TestSkyLoadingLagoon.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/jme3test/android/TestSkyLoadingPrimitives.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/android/jme3test/android/SimpleTexturedTest.java <==
+/*
+ * Android 2.2+ SimpleTextured test.
+ *
+ * created: Mon Nov 8 00:08:22 EST 2010
+ */
+==> engine/src/android/jme3tools/android/Fixed.java <==
+/**
+ * Fixed point maths class. This can be tailored for specific needs by
+ * changing the bits allocated to the 'fraction' part (see <code>FIXED_POINT
+ * </code>, which would also require <code>SIN_PRECALC</code> and <code>
+ * COS_PRECALC</code> updating).
+ *
+ * <p><a href="http://blog.numfum.com/2007/09/java-fixed-point-maths.html">
+ * http://blog.numfum.com/2007/09/java-fixed-point-maths.html</a></p>
+ *
+ * @version 1.0
+ * @author CW
+ *
+ * @deprecated Most devices with OpenGL ES 2.0 have an FPU. Please use
+ * floats instead of this class for decimal math.
+ */
+==> engine/src/core-plugins/com/jme3/audio/plugins/WAVLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/texture/plugins/ImageFlipper.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/asset/plugins/ClasspathLocator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/asset/plugins/UrlLocator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/asset/plugins/FileLocator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/asset/plugins/ZipLocator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/asset/plugins/UrlAssetInfo.java <==
+/**
+ * Handles loading of assets from a URL
+ *
+ * @author Kirill Vainer
+ */
+==> engine/src/core-plugins/com/jme3/shader/plugins/GLSLLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/.DS_Store <==
+==> engine/src/xml/com/jme3/export/xml/XMLExporter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/xml/com/jme3/export/xml/DOMSerializer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/xml/com/jme3/export/xml/DOMOutputCapsule.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/xml/com/jme3/export/xml/XMLImporter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jogg/com/jme3/audio/plugins/OGGLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jogg/com/jme3/audio/plugins/CachedOggStream.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/jogg/com/jme3/audio/plugins/UncachedOggStream.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-data/Common/ShaderLib/Fog.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Common.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Bump.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Texture.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Shadow.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Tangent.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/MultiSample.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Math.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Hdr.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Optics.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Parallax.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Lighting.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Splatting.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Skinning.glsllib <==
+==> engine/src/core-data/Common/ShaderLib/Ubo.glsllib <==
+==> engine/src/core-data/Common/Materials/WhiteColor.j3m <==
+==> engine/src/core-data/Common/Materials/VertexColor.j3m <==
+==> engine/src/core-data/Common/Materials/RedColor.j3m <==
+==> engine/src/core-data/Common/MatDefs/Gui/Gui.j3md <==
+==> engine/src/core-data/Common/MatDefs/Gui/Gui.frag <==
+==> engine/src/core-data/Common/MatDefs/Gui/Gui.vert <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag <==
+==> engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert <==
+==> engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md <==
+==> engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag <==
+==> engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md <==
+==> engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md <==
+==> engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag <==
+==> engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag <==
+==> engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag <==
+==> engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag <==
+==> engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md <==
+==> engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag <==
+==> engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md <==
+==> engine/src/core-data/Common/MatDefs/Light/Deferred.frag <==
+==> engine/src/core-data/Common/MatDefs/Light/Lighting.frag <==
+==> engine/src/core-data/Common/MatDefs/Light/Deferred.vert <==
+==> engine/src/core-data/Common/MatDefs/Light/GBuf.frag <==
+==> engine/src/core-data/Common/MatDefs/Light/Lighting.vert <==
+==> engine/src/core-data/Common/MatDefs/Light/Glow.frag <==
+==> engine/src/core-data/Common/MatDefs/Light/GBuf.vert <==
+==> engine/src/core-data/Common/MatDefs/Light/Deferred.j3md <==
+==> engine/src/core-data/Common/MatDefs/Light/Lighting.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/Sky.vert <==
+==> engine/src/core-data/Common/MatDefs/Misc/Particle.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/Particle.vert <==
+==> engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/Sky.frag <==
+==> engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag <==
+==> engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert <==
+==> engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag <==
+==> engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert <==
+==> engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag <==
+==> engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/Particle.frag <==
+==> engine/src/core-data/Common/MatDefs/Misc/Sky.j3md <==
+==> engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag <==
+==> engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert <==
+==> engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert <==
+==> engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md <==
+==> engine/src/core-data/Interface/Fonts/Console.fnt <==
+==> engine/src/core-data/Interface/Fonts/Console.png <==
+==> engine/src/core-data/Interface/Fonts/Default.png <==
+==> engine/src/core-data/Interface/Fonts/Default.fnt <==
+==> engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java <==
+==> engine/src/tools/jme3tools/optimize/Octnode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/OCTTriangle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/FastOctnode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/TestCollector.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/TextureAtlas.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/TriangleCollector.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/Octree.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/optimize/pvsnotes <==
+==> engine/src/tools/jme3tools/savegame/SaveGame.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Tool for saving Savables as SaveGame entries in a system-dependent way.
+ * @author normenhansen
+ */
+==> engine/src/tools/jme3tools/converters/Converter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/RGB565.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/tools/jme3tools/converters/FolderConverter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/ModelConverter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/StripStartInfo.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/StripInfoVec.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/TriStrip.java <==
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/IntVec.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/PrimitiveGroup.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/tools/jme3tools/converters/model/strip/StripInfo.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/tools/jme3tools/converters/model/strip/VertexCache.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/FaceInfo.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/EdgeInfo.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+==> engine/src/tools/jme3tools/converters/model/strip/Stripifier.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/FaceInfoVec.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/strip/EdgeInfoVec.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/tools/jme3tools/converters/model/FloatToFixed.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-common/com/jme3/bullet/BulletAppState.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * <code>BulletAppState</code> allows using bullet physics in an Application.
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/PhysicsTickListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Implement this interface to be called from the physics thread on a physics update.
+==> engine/src/bullet-common/com/jme3/bullet/collision/RagdollCollisionListener.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/bullet-common/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Interface for Objects that want to be informed about collision events in the physics space
+==> engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/VehicleControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/PhysicsControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollPreset.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/GhostControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A GhostControl moves with the spatial it is attached to and can be used to check
+ * overlaps with other physics objects (e.g. aggro radius).
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/CharacterControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet-common/com/jme3/bullet/control/RigidBodyControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/heightmap.jpg <==
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/water_normalmap.dds <==
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/caustics.jpg <==
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/foam2.jpg <==
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/foam.jpg <==
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/dudv_map.jpg <==
+==> engine/src/core-effects/Common/MatDefs/Water/Textures/foam3.jpg <==
+==> engine/src/core-effects/Common/MatDefs/Water/Water.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Water/SimpleWater.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Water/simple_water.vert <==
+/*
+==> engine/src/core-effects/Common/MatDefs/Water/Water.frag <==
+==> engine/src/core-effects/Common/MatDefs/Water/Water15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Water/simple_water.frag <==
+/*
+==> engine/src/core-effects/Common/MatDefs/SSAO/Textures/random.png <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur15.frag <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/ssao15.frag <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/normal.vert <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.j3md <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/ssao.j3md <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.frag <==
+/*
+==> engine/src/core-effects/Common/MatDefs/SSAO/normal.frag <==
+==> engine/src/core-effects/Common/MatDefs/SSAO/ssao.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/bloomFinal15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/LightScattering15.vert <==
+==> engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/Post.vert <==
+==> engine/src/core-effects/Common/MatDefs/Post/bloomExtract.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/LightScattering.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/GammaCorrection15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/FXAA.vert <==
+==> engine/src/core-effects/Common/MatDefs/Post/BloomExtract.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/Fog.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/bloomExtract15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/LightScattering.vert <==
+==> engine/src/core-effects/Common/MatDefs/Post/DepthOfField.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/Overlay.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Fog.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/Overlay15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Overlay.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/Posterization.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/LightScattering.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Fade15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Fog15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/DepthOfField.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/CrossHatch15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Posterization15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Posterization.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/FXAA.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/LightScattering15.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Post15.vert <==
+==> engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/CrossHatch.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/bloomFinal.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/Fade.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/FXAA.j3md <==
+==> engine/src/core-effects/Common/MatDefs/Post/CrossHatch.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/Fade.frag <==
+==> engine/src/core-effects/Common/MatDefs/Post/CartoonEdge15.frag <==
+==> engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/water/ReflectionProcessor.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Reflection Processor
+ * Used to render the reflected scene in an off view port
+ */
+==> engine/src/core-effects/com/jme3/water/WaterFilter.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/ssao/SSAOFilter.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/GammaCorrectionFilter.java <==
+/**
+ *
+ * @author Phate666
+ * @version 1.0 initial version
+ * @version 1.1 added luma
+ */
+==> engine/src/core-effects/com/jme3/post/filters/ColorOverlayFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/CartoonEdgeFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/FadeFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/RadialBlurFilter.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/FogFilter.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/PosterizationFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A filter to handle translucent objects when rendering a scene with filters that uses depth like WaterFilter and SSAOFilter
+ * just create a TranslucentBucketFilter and add it to the Filter list of a FilterPostPorcessor
+ * @author Nehon
+ */
+==> engine/src/core-effects/com/jme3/post/filters/BloomFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/CrossHatchFilter.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/DepthOfFieldFilter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/core-effects/com/jme3/post/filters/FXAAFilter.java <==
+/**
+ * <a href="http://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/" rel="nofollow">http://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-<span class="domtooltips" title="OpenGL (Open Graphics Library) is a standard specification defining a cross-language, cross-platform API for writing applications that produce 2D and 3D computer graphics." id="domtooltipsspan11">opengl</span>-test-radeon-geforce/3/</a>
+ * <a href="http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf" rel="nofollow">http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf</a>
+ *
+ * @author Phate666 (adapted to jme3)
+ *
+ */
+==> engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md <==
+==> engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert <==
+==> engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag <==
+==> engine/src/niftygui/com/jme3/niftygui/NiftyJmeDisplay.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/niftygui/SoundDeviceJme.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/niftygui/SoundHandleJme.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/niftygui/RenderImageJme.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/niftygui/RenderFontJme.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/niftygui/com/jme3/cinematic/events/GuiTrack.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ *
+==> engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/PhysicsSpace.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/PhysicsRayTestResult.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/CollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java <==
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * A simple point, line, triangle or quad collisionShape based on one to four points-
+ * @author normenhansen
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster
+ * than using a regular mesh.
+ * There are a couple tricks though:
+ * -No rotation or translation is supported.
+ * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being
+ * equal on either side. If not, the whole collision box is shifted vertically and things don't collide
+ * as they should.
+ *
+ * @author Brent Owens
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/bullet/com/jme3/bullet/util/DebugShapeFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/util/NativeMeshUtil.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/util/DebugMeshCallback.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/SixDofSpringJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/Point2PointJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/SixDofJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/ConeJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/HingeJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/SliderJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ *
+==> engine/src/bullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/bullet/com/jme3/bullet/joints/PhysicsJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/animation/SubtitleTrack.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/test/jme3test/animation/TestCinematic.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/animation/TestMotionPath.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/animation/TestCameraMotionPath.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloTerrain.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloLoop.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloAudio.java <==
+/** Sample 11 - playing 3D audio. */
+==> engine/src/test/jme3test/helloworld/HelloInput.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloEffects.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloTerrainCollision.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloPicking.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloJME3.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloAssets.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloPhysics.java <==
+/**
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloAnimation.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloCollision.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloMaterial.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/helloworld/HelloNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/blender/TestBlenderLoader.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestWav.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestAmbient.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestDoppler.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestOgg.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestMusicStreaming.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestMusicPlayer.form <==
+==> engine/src/test/jme3test/audio/TestMusicPlayer.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/audio/TestReverb.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/terrain/TerrainTestCollision.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/terrain/TerrainTestReadWrite.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/terrain/TerrainFractalGridTest.java <==
+==> engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java <==
+==> engine/src/test/jme3test/terrain/TerrainGridTest.java <==
+==> engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java <==
+==> engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java <==
+==> engine/src/test/jme3test/terrain/TerrainTestAdvanced.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/terrain/TerrainTest.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/renderer/TestMultiViews.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/renderer/TestParallelProjection.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/math/TestHalfFloat.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/collision/TestTriangleCollision.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/collision/TestRayCasting.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/collision/TestMousePick.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/collision/RayTrace.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/TestChooser.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/water/TestPostWater.java <==
+==> engine/src/test/jme3test/water/TestSceneWater.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/water/TestPostWaterLake.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/water/WaterUI.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/water/TestSimpleWater.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/texture/tex3DThumb.vert <==
+==> engine/src/test/jme3test/texture/TestTextureArray.java <==
+==> engine/src/test/jme3test/texture/TestSkyLoading.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/texture/UnshadedArray.frag <==
+==> engine/src/test/jme3test/texture/TestTexture3D.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+==> engine/src/test/jme3test/texture/tex3D.frag <==
+==> engine/src/test/jme3test/texture/tex3D.j3md <==
+==> engine/src/test/jme3test/texture/UnshadedArray.vert <==
+==> engine/src/test/jme3test/texture/tex3DThumb.j3md <==
+==> engine/src/test/jme3test/texture/tex3D.vert <==
+==> engine/src/test/jme3test/texture/tex3DThumb.frag <==
+==> engine/src/test/jme3test/texture/UnshadedArray.j3md <==
+==> engine/src/test/jme3test/texture/TestTexture3DLoading.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+==> engine/src/test/jme3test/material/TestSimpleBumps.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/material/TestNormalMapping.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/material/TestParallax.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/material/TestBumpModel.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/material/TestUnshadedModel.java <==
+==> engine/src/test/jme3test/material/TestColoredTexture.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/export/TestOgreConvert.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/export/TestAssetLinkNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/stress/TestLeakingGL.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/stress/TestBatchLod.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/stress/TestLodStress.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/asset/TestManyLocators.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/asset/TestAssetCache.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/asset/TestUrlLoading.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/asset/TestOnlineJar.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/asset/TestAbsoluteLocators.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestIDList.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestTempVars.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/state/RootNodeState.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/state/TestAppStates.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestBareBonesApp.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestChangeAppIcon.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestApplication.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestReleaseDirectMemory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/app/TestContextRestart.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/awt/TestSafeCanvas.java <==
+==> engine/src/test/jme3test/awt/TestAwtPanels.java <==
+==> engine/src/test/jme3test/awt/TestCanvas.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/awt/TestApplet.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/awt/AppHarness.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/batching/TestBatchNode.java <==
+/*
+ * To change this template, choose Tools | Templates and open the template in
+ * the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/test/jme3test/batching/TestBatchNodeTower.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+==> engine/src/test/jme3test/batching/TestBatchNodeCluster.java <==
+/*
+ * To change this template, choose Tools | Templates and open the template in
+ * the editor.
+ */
+==> engine/src/test/jme3test/tools/TestTextureAtlas.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/tools/TestSaveGame.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/tools/TestOctree.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/conversion/TestMipMapGen.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/conversion/TestTriangleStrip.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/niftygui/TestNiftyGui.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/niftygui/TestNiftyToMesh.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/niftygui/TestNiftyExamples.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/effect/TestEverything.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/effect/TestParticleExportingCloning.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/effect/TestMovingParticle.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/effect/TestPointSprite.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/effect/TestExplosionEffect.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestFancyCar.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestQ3.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestBrickWall.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestSimplePhysics.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestPhysicsCharacter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved. <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer. <p/> * Redistributions
+ * in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. <p/> * Neither the name of
+ * 'jMonkeyEngine' nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written
+ * permission. <p/> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+ * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestCcd.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestAttachDriver.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestAttachGhostObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/PhysicsTestHelper.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/test/jme3test/bullet/TestCollisionGroups.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestBoneRagdoll.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestHoveringTank.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/BombControl.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/test/jme3test/bullet/PhysicsHoverControl.java <==
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestRagDoll.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author normenhansen
+ */
+==> engine/src/test/jme3test/bullet/TestBrickTower.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+==> engine/src/test/jme3test/bullet/TestPhysicsHingeJoint.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestPhysicsReadWrite.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestGhostObject.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestWalkingChar.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestPhysicsCar.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestRagdollCharacter.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestLocalPhysics.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/bullet/TestKinematicAddToPhysicsSpaceIssue.java <==
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author Nehon
+ */
+==> engine/src/test/jme3test/bullet/TestCollisionListener.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/scene/TestSceneLoading.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/scene/TestUserData.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/TestControls.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/combomoves/ComboMove.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/combomoves/ComboMoveExecution.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/combomoves/TestComboMoves.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/TestCameraNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * <p/>
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/TestChaseCamera.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/input/TestJoystick.java <==
+==> engine/src/test/jme3test/bounding/TestRayCollision.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/TestMonkeyHead.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/TestOgreLoading.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/anim/TestOgreAnim.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/anim/TestOgreComplexAnim.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/anim/TestCustomAnim.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/anim/TestAnimBlendBug.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/anim/TestAnimationFactory.java <==
+==> engine/src/test/jme3test/model/anim/TestSpatialAnim.java <==
+==> engine/src/test/jme3test/model/anim/TestBlenderAnim.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/anim/TestBlenderObjectAnim.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/shape/TestCylinder.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/shape/TestDebugShapes.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/shape/TestBillboard.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/shape/TestCustomMesh.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/shape/TestBox.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/shape/TestSphere.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/TestHoverTank.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/model/TestObjLoading.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestBloom.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestRenderToTexture.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestCrossHatch.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestDepthOfField.java <==
+/**
+ * test
+ * @author Nehon
+ */
+==> engine/src/test/jme3test/post/SSAOUI.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestRenderToMemory.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestCartoonEdge.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestPostFilters.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestSSAO.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestFog.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestPosterization.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestTransparentCartoonEdge.java <==
+==> engine/src/test/jme3test/post/TestLightScattering.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/LightScatteringUI.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestFBOPassthrough.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestTransparentSSAO.java <==
+==> engine/src/test/jme3test/post/BloomUI.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestMultiRenderTarget.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestHDR.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestMultiViewsFilters.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/post/TestMultiplesFilters.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestLightNode.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestSpotLightTerrain.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestTangentGenBadUV.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestTransparentShadow.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestTangentGenBadModels.java <==
+/**
+ *
+ * @author Kirusha
+ */
+==> engine/src/test/jme3test/light/TestShadow.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestTangentGen.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestSimpleLighting.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestLightRadius.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestSpotLight.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestPssmShadow.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/light/TestEnvironmentMapping.java <==
+/**
+ * test
+ * @author nehon
+ */
+==> engine/src/test/jme3test/light/TestManyLights.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/gui/TestSoftwareMouse.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/gui/TestZOrder.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/gui/TestBitmapText3D.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/gui/TestOrtho.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/gui/TestBitmapFont.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestRemoteCall.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestLatency.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestChatClient.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestNetworkStress.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestChatServer.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/MovingAverage.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestThroughput.java <==
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestMessages.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/network/TestSerialization.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+==> engine/src/test/jme3test/games/CubeField.java <==
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/README b/README
new file mode 100644
index 0000000..e14b34f
--- /dev/null
+++ b/README
@@ -0,0 +1,37 @@
+jMonkeyEngine3 on Android README
+
+This is the source tree for the engine portion of jMonkeyEngine, from a
+Subversion snapshot taken on 2012.02.24. It includes the engine/src directory
+from their source repository, but not the rest as it is quite large.
+
+The Android makefile does not build the entire library, but only these
+directories:
+
+engine/src/android
+engine/src/core
+engine/src/core-plugins
+engine/src/ogre
+
+which should be enough to use the core parts of the library.
+
+jMonkeyEngine relies on material and shader resources that are intended to be
+saved in the Java classpath and picked up by the classloader. On Android, they
+are saved in the APK and AssetManager is used to load them in. This works if
+you build the APK in something like Eclipse and simply include a source jar,
+but that will not work in the Android.mk build environment. Even if the
+resources are included in the static library jar build here, they will be
+stripped out when dex compiles the libraries into the APK. If you want to use
+this library, you will need to duplicate the resources you need into the assets
+directory of your project. As long as the path under assets/ is the same, the
+Android-specific asset loader will find them, e.g. if asked to load
+"Common/MatDefs/Misc/Unshaded.j3md", it will find it under
+"assets/Common/MatDefs/Misc/Unshaded.j3md".
+
+Links:
+
+http://jmonkeyengine.org/
+http://code.google.com/p/jmonkeyengine/checkout
+
+THe NOTICES file was generated with this command:
+
+find engine/src -type f | xargs head -n 35 | grep "^[ /=][*=]" > NOTICE
diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java
new file mode 100644
index 0000000..6631003
--- /dev/null
+++ b/engine/src/android/com/jme3/app/AndroidHarness.java
@@ -0,0 +1,398 @@
+package com.jme3.app;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.pm.ActivityInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+import android.view.*;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.android.AndroidAudioRenderer;
+import com.jme3.input.android.AndroidInput;
+import com.jme3.input.controls.TouchListener;
+import com.jme3.input.event.TouchEvent;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeSystem;
+import com.jme3.system.android.AndroidConfigChooser.ConfigType;
+import com.jme3.system.android.JmeAndroidSystem;
+import com.jme3.system.android.OGLESContext;
+import com.jme3.util.JmeFormatter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>AndroidHarness</code> wraps a jme application object and runs it on
+ * Android
+ *
+ * @author Kirill
+ * @author larynx
+ */
+public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener {
+
+ protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
+ /**
+ * The application class to start
+ */
+ protected String appClass = "jme3test.android.Test";
+ /**
+ * The jme3 application object
+ */
+ protected Application app = null;
+ /**
+ * ConfigType.FASTEST is RGB565, GLSurfaceView default ConfigType.BEST is
+ * RGBA8888 or better if supported by the hardware
+ */
+ protected ConfigType eglConfigType = ConfigType.FASTEST;
+ /**
+ * If true all valid and not valid egl configs are logged
+ */
+ protected boolean eglConfigVerboseLogging = false;
+ /**
+ * If true MouseEvents are generated from TouchEvents
+ */
+ protected boolean mouseEventsEnabled = true;
+ /**
+ * Flip X axis
+ */
+ protected boolean mouseEventsInvertX = true;
+ /**
+ * Flip Y axis
+ */
+ protected boolean mouseEventsInvertY = true;
+ /**
+ * Title of the exit dialog, default is "Do you want to exit?"
+ */
+ protected String exitDialogTitle = "Do you want to exit?";
+ /**
+ * Message of the exit dialog, default is "Use your home key to bring this
+ * app into the background or exit to terminate it."
+ */
+ protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
+ /**
+ * Set the screen window mode. If screenFullSize is true, then the
+ * notification bar and title bar are removed and the screen covers the
+ * entire display.   If screenFullSize is false, then the notification bar
+ * remains visible if screenShowTitle is true while screenFullScreen is
+ * false, then the title bar is also displayed under the notification bar.
+ */
+ protected boolean screenFullScreen = true;
+ /**
+ * if screenShowTitle is true while screenFullScreen is false, then the
+ * title bar is also displayed under the notification bar
+ */
+ protected boolean screenShowTitle = true;
+ /**
+ * Splash Screen picture Resource ID. If a Splash Screen is desired, set
+ * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
+ * splashPicID = 0, then no splash screen will be displayed.
+ */
+ protected int splashPicID = 0;
+ /**
+ * Set the screen orientation, default is SENSOR
+ * ActivityInfo.SCREEN_ORIENTATION_* constants package
+ * android.content.pm.ActivityInfo
+ *
+ * SCREEN_ORIENTATION_UNSPECIFIED SCREEN_ORIENTATION_LANDSCAPE
+ * SCREEN_ORIENTATION_PORTRAIT SCREEN_ORIENTATION_USER
+ * SCREEN_ORIENTATION_BEHIND SCREEN_ORIENTATION_SENSOR (default)
+ * SCREEN_ORIENTATION_NOSENSOR
+ */
+ protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
+ protected OGLESContext ctx;
+ protected GLSurfaceView view = null;
+ protected boolean isGLThreadPaused = true;
+ private ImageView splashImageView = null;
+ private FrameLayout frameLayout = null;
+ final private String ESCAPE_EVENT = "TouchEscape";
+
+ static {
+ try {
+ System.loadLibrary("bulletjme");
+ } catch (UnsatisfiedLinkError e) {
+ }
+ JmeSystem.setSystemDelegate(new JmeAndroidSystem());
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Logger log = logger;
+ boolean bIsLogFormatSet = false;
+ do {
+ if (log.getHandlers().length == 0) {
+ log = logger.getParent();
+ if (log != null) {
+ for (Handler h : log.getHandlers()) {
+ //h.setFormatter(new SimpleFormatter());
+ h.setFormatter(new JmeFormatter());
+ bIsLogFormatSet = true;
+ }
+ }
+ }
+ } while (log != null && !bIsLogFormatSet);
+
+ JmeAndroidSystem.setResources(getResources());
+ JmeAndroidSystem.setActivity(this);
+
+ if (screenFullScreen) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ } else {
+ if (!screenShowTitle) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+ }
+
+ setRequestedOrientation(screenOrientation);
+
+ // Create Settings
+ AppSettings settings = new AppSettings(true);
+
+ // Create the input class
+ AndroidInput input = new AndroidInput(this);
+ input.setMouseEventsInvertX(mouseEventsInvertX);
+ input.setMouseEventsInvertY(mouseEventsInvertY);
+ input.setMouseEventsEnabled(mouseEventsEnabled);
+
+ // Create application instance
+ try {
+ if (app == null) {
+ @SuppressWarnings("unchecked")
+ Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }
+
+ app.setSettings(settings);
+ app.start();
+ ctx = (OGLESContext) app.getContext();
+ view = ctx.createView(input, eglConfigType, eglConfigVerboseLogging);
+
+ // Set the screen reolution
+ WindowManager wind = this.getWindowManager();
+ Display disp = wind.getDefaultDisplay();
+ ctx.getSettings().setResolution(disp.getWidth(), disp.getHeight());
+
+ AppSettings s = ctx.getSettings();
+ logger.log(Level.INFO, "Settings: Width {0} Height {1}", new Object[]{s.getWidth(), s.getHeight()});
+
+ layoutDisplay();
+ } catch (Exception ex) {
+ handleError("Class " + appClass + " init failed", ex);
+ setContentView(new TextView(this));
+ }
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+ if (app != null) {
+ app.restart();
+ }
+
+ logger.info("onRestart");
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ logger.info("onStart");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (view != null) {
+ view.onResume();
+ }
+
+ //resume the audio
+ AudioRenderer result = app.getAudioRenderer();
+ if (result != null) {
+ if (result instanceof AndroidAudioRenderer) {
+ AndroidAudioRenderer renderer = (AndroidAudioRenderer) result;
+ renderer.resumeAll();
+ }
+ }
+
+ isGLThreadPaused = false;
+ logger.info("onResume");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (view != null) {
+ view.onPause();
+ }
+
+ //pause the audio
+ AudioRenderer result = app.getAudioRenderer();
+ if (result != null) {
+ logger.info("pause: " + result.getClass().getSimpleName());
+ if (result instanceof AndroidAudioRenderer) {
+ AndroidAudioRenderer renderer = (AndroidAudioRenderer) result;
+ renderer.pauseAll();
+ }
+ }
+
+ isGLThreadPaused = true;
+ logger.info("onPause");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ logger.info("onStop");
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (app != null) {
+ app.stop(!isGLThreadPaused);
+ }
+
+ logger.info("onDestroy");
+ super.onDestroy();
+ }
+
+ public Application getJmeApplication() {
+ return app;
+ }
+
+ /**
+ * Called when an error has occurred. By default, will show an error message
+ * to the user and print the exception/error to the log.
+ */
+ public void handleError(final String errorMsg, final Throwable t) {
+ String stackTrace = "";
+ String title = "Error";
+
+ if (t != null) {
+ // Convert exception to string
+ StringWriter sw = new StringWriter(100);
+ t.printStackTrace(new PrintWriter(sw));
+ stackTrace = sw.toString();
+ title = t.toString();
+ }
+
+ final String finalTitle = title;
+ final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
+ + "\n" + stackTrace;
+
+ logger.log(Level.SEVERE, finalMsg);
+
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
+ .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create();
+ dialog.show();
+ }
+ });
+ }
+
+ /**
+ * Called by the android alert dialog, terminate the activity and OpenGL
+ * rendering
+ *
+ * @param dialog
+ * @param whichButton
+ */
+ public void onClick(DialogInterface dialog, int whichButton) {
+ if (whichButton != -2) {
+ if (app != null) {
+ app.stop(true);
+ }
+ this.finish();
+ }
+ }
+
+ /**
+ * Gets called by the InputManager on all touch/drag/scale events
+ */
+ @Override
+ public void onTouch(String name, TouchEvent evt, float tpf) {
+ if (name.equals(ESCAPE_EVENT)) {
+ switch (evt.getType()) {
+ case KEY_UP:
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
+ .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create();
+ dialog.show();
+ }
+ });
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public void layoutDisplay() {
+ logger.log(Level.INFO, "Splash Screen Picture Resource ID: {0}", splashPicID);
+ if (splashPicID != 0) {
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT,
+ Gravity.CENTER);
+
+ frameLayout = new FrameLayout(this);
+ splashImageView = new ImageView(this);
+
+ Drawable drawable = this.getResources().getDrawable(splashPicID);
+ if (drawable instanceof NinePatchDrawable) {
+ splashImageView.setBackgroundDrawable(drawable);
+ } else {
+ splashImageView.setImageResource(splashPicID);
+ }
+
+ frameLayout.addView(view);
+ frameLayout.addView(splashImageView, lp);
+
+ setContentView(frameLayout);
+ logger.log(Level.INFO, "Splash Screen Created");
+ } else {
+ logger.log(Level.INFO, "Splash Screen Skipped.");
+ setContentView(view);
+ }
+ }
+
+ public void removeSplashScreen() {
+ logger.log(Level.INFO, "Splash Screen Picture Resource ID: {0}", splashPicID);
+ if (splashPicID != 0) {
+ if (frameLayout != null) {
+ if (splashImageView != null) {
+ this.runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ splashImageView.setVisibility(View.INVISIBLE);
+ frameLayout.removeView(splashImageView);
+ }
+ });
+ } else {
+ logger.log(Level.INFO, "splashImageView is null");
+ }
+ } else {
+ logger.log(Level.INFO, "frameLayout is null");
+ }
+ }
+ }
+}
diff --git a/engine/src/android/com/jme3/app/R.java b/engine/src/android/com/jme3/app/R.java
new file mode 100644
index 0000000..4db44e3
--- /dev/null
+++ b/engine/src/android/com/jme3/app/R.java
@@ -0,0 +1,20 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.jme3.app;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class layout {
+ public static final int main=0x7f020000;
+ }
+ public static final class string {
+ public static final int app_name=0x7f030000;
+ public static final int jme3_appclass=0x7f030001;
+ }
+}
diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
new file mode 100644
index 0000000..c6f0b17
--- /dev/null
+++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.asset;
+
+import com.jme3.asset.plugins.AndroidLocator;
+import com.jme3.asset.plugins.ClasspathLocator;
+import com.jme3.audio.plugins.AndroidAudioLoader;
+import com.jme3.texture.Texture;
+import com.jme3.texture.plugins.AndroidImageLoader;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ * <code>AndroidAssetManager</code> is an implementation of DesktopAssetManager for Android
+ *
+ * @author larynx
+ */
+public class AndroidAssetManager extends DesktopAssetManager {
+
+ private static final Logger logger = Logger.getLogger(AndroidAssetManager.class.getName());
+
+ public AndroidAssetManager() {
+ this(null);
+ }
+
+ @Deprecated
+ public AndroidAssetManager(boolean loadDefaults) {
+ //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));
+ this(null);
+ }
+
+ /**
+ * AndroidAssetManager constructor
+ * If URL == null then a default list of locators and loaders for android is set
+ * @param configFile
+ */
+ public AndroidAssetManager(URL configFile) {
+ System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
+
+ // Set Default Android config
+ this.registerLocator("", AndroidLocator.class);
+ this.registerLocator("", ClasspathLocator.class);
+ this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");
+ this.registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");
+ this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");
+ this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");
+ this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");
+ this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds");
+ this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm");
+ this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr");
+ this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga");
+ this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");
+ this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj");
+ this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl");
+ this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml");
+ this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml");
+ this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");
+ this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");
+ this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");
+
+ logger.info("AndroidAssetManager created.");
+ }
+
+ /**
+ * Loads a texture.
+ *
+ * @return
+ */
+ @Override
+ public Texture loadTexture(TextureKey key) {
+ Texture tex = (Texture) loadAsset(key);
+
+ // XXX: This will improve performance on some really
+ // low end GPUs (e.g. ones with OpenGL ES 1 support only)
+ // but otherwise won't help on the higher ones.
+ // Strongly consider removing this.
+ tex.setMagFilter(Texture.MagFilter.Nearest);
+ tex.setAnisotropicFilter(0);
+ if (tex.getMinFilter().usesMipMapLevels()) {
+ tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap);
+ } else {
+ tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+ }
+ return tex;
+ }
+}
diff --git a/engine/src/android/com/jme3/asset/AndroidImageInfo.java b/engine/src/android/com/jme3/asset/AndroidImageInfo.java
new file mode 100644
index 0000000..c8eb539
--- /dev/null
+++ b/engine/src/android/com/jme3/asset/AndroidImageInfo.java
@@ -0,0 +1,101 @@
+package com.jme3.asset;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * <code>AndroidImageInfo</code> is set in a jME3 image via the {@link Image#setEfficientData(java.lang.Object)}
+ * method to retrieve a {@link Bitmap} when it is needed by the renderer.
+ * User code may extend <code>AndroidImageInfo</code> and provide their own implementation of the
+ * {@link AndroidImageInfo#loadBitmap()} method to acquire a bitmap by their own means.
+ *
+ * @author Kirill Vainer
+ */
+public class AndroidImageInfo {
+
+ protected AssetInfo assetInfo;
+ protected Bitmap bitmap;
+ protected Format format;
+
+ public AndroidImageInfo(AssetInfo assetInfo) {
+ this.assetInfo = assetInfo;
+ }
+
+ public Bitmap getBitmap(){
+ if (bitmap == null || bitmap.isRecycled()){
+ try {
+ loadBitmap();
+ } catch (IOException ex) {
+ // If called first inside AssetManager, the error will propagate
+ // correctly. Assuming that if the first calls succeeds
+ // then subsequent calls will as well.
+ throw new AssetLoadException("Failed to load image " + assetInfo.getKey(), ex);
+ }
+ }
+ return bitmap;
+ }
+
+
+
+ public Format getFormat(){
+ return format;
+ }
+
+ /**
+ * Loads the bitmap directly from the asset info, possibly updating
+ * or creating the image object.
+ */
+ protected void loadBitmap() throws IOException{
+ InputStream in = null;
+ try {
+ in = assetInfo.openStream();
+ bitmap = BitmapFactory.decodeStream(in);
+ if (bitmap == null) {
+ throw new IOException("Failed to load image: " + assetInfo.getKey().getName());
+ }
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+
+ switch (bitmap.getConfig()) {
+ case ALPHA_8:
+ format = Image.Format.Alpha8;
+ break;
+ case ARGB_4444:
+ format = Image.Format.ARGB4444;
+ break;
+ case ARGB_8888:
+ format = Image.Format.RGBA8;
+ break;
+ case RGB_565:
+ format = Image.Format.RGB565;
+ break;
+ default:
+ // This should still work as long
+ // as renderer doesn't check format
+ // but just loads bitmap directly.
+ format = null;
+ }
+
+ TextureKey texKey = (TextureKey) assetInfo.getKey();
+ if (texKey.isFlipY()) {
+ // Flip the image, then delete the old one.
+ Matrix flipMat = new Matrix();
+ flipMat.preScale(1.0f, -1.0f);
+ Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false);
+ bitmap.recycle();
+ bitmap = newBitmap;
+
+ if (bitmap == null) {
+ throw new IOException("Failed to flip image: " + texKey);
+ }
+ }
+ }
+}
diff --git a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
new file mode 100644
index 0000000..01b1cab
--- /dev/null
+++ b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
@@ -0,0 +1,90 @@
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.system.android.JmeAndroidSystem;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class AndroidLocator implements AssetLocator {
+
+ private static final Logger logger = Logger.getLogger(AndroidLocator.class.getName());
+
+ private android.content.res.AssetManager androidManager;
+ private String rootPath = "";
+
+ private class AndroidAssetInfo extends AssetInfo {
+
+ private InputStream in;
+ private final String assetPath;
+
+ public AndroidAssetInfo(com.jme3.asset.AssetManager assetManager, AssetKey<?> key, String assetPath, InputStream in) {
+ super(assetManager, key);
+ this.assetPath = assetPath;
+ this.in = in;
+ }
+
+ @Override
+ public InputStream openStream() {
+ if (in != null){
+ // Reuse the already existing stream (only once)
+ InputStream in2 = in;
+ in = null;
+ return in2;
+ }else{
+ // Create a new stream for subsequent invocations.
+ try {
+ return androidManager.open(assetPath);
+ } catch (IOException ex) {
+ throw new AssetLoadException("Failed to open asset " + assetPath, ex);
+ }
+ }
+ }
+ }
+
+ private AndroidAssetInfo create(AssetManager assetManager, AssetKey key, String assetPath) throws IOException {
+ try {
+ InputStream in = androidManager.open(assetPath);
+ if (in == null){
+ return null;
+ }else{
+ return new AndroidAssetInfo(assetManager, key, assetPath, in);
+ }
+ } catch (IOException ex) {
+ // XXX: Prefer to show warning here?
+ // Should only surpress exceptions for "file missing" type errors.
+ return null;
+ }
+ }
+
+ public AndroidLocator() {
+ androidManager = JmeAndroidSystem.getResources().getAssets();
+ }
+
+ public void setRootPath(String rootPath) {
+ this.rootPath = rootPath;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public AssetInfo locate(com.jme3.asset.AssetManager manager, AssetKey key) {
+ String assetPath = rootPath + key.getName();
+ // Fix path issues
+ if (assetPath.startsWith("/")) {
+ // Remove leading /
+ assetPath = assetPath.substring(1);
+ }
+ assetPath = assetPath.replace("//", "/");
+ try {
+ return create(manager, key, assetPath);
+ }catch (IOException ex){
+ // This is different handling than URL locator
+ // since classpath locating would return null at the getResource()
+ // call, otherwise there's a more critical error...
+ throw new AssetLoadException("Failed to open asset " + assetPath, ex);
+ }
+ }
+}
diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioData.java b/engine/src/android/com/jme3/audio/android/AndroidAudioData.java
new file mode 100644
index 0000000..37f41a8
--- /dev/null
+++ b/engine/src/android/com/jme3/audio/android/AndroidAudioData.java
@@ -0,0 +1,62 @@
+package com.jme3.audio.android;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.util.NativeObject;
+
+public class AndroidAudioData extends AudioData {
+
+ protected AssetKey<?> assetKey;
+ protected float currentVolume = 0f;
+
+ public AndroidAudioData(){
+ super();
+ }
+
+ protected AndroidAudioData(int id){
+ super(id);
+ }
+
+ public AssetKey<?> getAssetKey() {
+ return assetKey;
+ }
+
+ public void setAssetKey(AssetKey<?> assetKey) {
+ this.assetKey = assetKey;
+ }
+
+ @Override
+ public DataType getDataType() {
+ return DataType.Buffer;
+ }
+
+ @Override
+ public float getDuration() {
+ return 0; // TODO: ???
+ }
+
+ @Override
+ public void resetObject() {
+ this.id = -1;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((AudioRenderer)rendererObject).deleteAudioData(this);
+ }
+
+ public float getCurrentVolume() {
+ return currentVolume;
+ }
+
+ public void setCurrentVolume(float currentVolume) {
+ this.currentVolume = currentVolume;
+ }
+
+ @Override
+ public NativeObject createDestructableClone() {
+ return new AndroidAudioData(id);
+ }
+}
diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
new file mode 100644
index 0000000..383def5
--- /dev/null
+++ b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.audio.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.SoundPool;
+import android.util.Log;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.audio.AudioNode.Status;
+import com.jme3.audio.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is the android implementation for {@link AudioRenderer}
+ *
+ * @author larynx
+ * @author plan_rich
+ */
+public class AndroidAudioRenderer implements AudioRenderer,
+ SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener {
+
+ private static final Logger logger = Logger.getLogger(AndroidAudioRenderer.class.getName());
+ private final static int MAX_NUM_CHANNELS = 16;
+ private final HashMap<AudioNode, MediaPlayer> musicPlaying = new HashMap<AudioNode, MediaPlayer>();
+ private SoundPool soundPool = null;
+ private final Vector3f listenerPosition = new Vector3f();
+ // For temp use
+ private final Vector3f distanceVector = new Vector3f();
+ private final Context context;
+ private final AssetManager assetManager;
+ private HashMap<Integer, AudioNode> soundpoolStillLoading = new HashMap<Integer, AudioNode>();
+ private Listener listener;
+ private boolean audioDisabled = false;
+ private final AudioManager manager;
+
+ public AndroidAudioRenderer(Activity context) {
+ this.context = context;
+ manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ assetManager = context.getAssets();
+ }
+
+ @Override
+ public void initialize() {
+ soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
+ 0);
+ soundPool.setOnLoadCompleteListener(this);
+ }
+
+ @Override
+ public void updateSourceParam(AudioNode src, AudioParam param) {
+ // logger.log(Level.INFO, "updateSourceParam " + param);
+
+ if (audioDisabled) {
+ return;
+ }
+
+ if (src.getChannel() < 0) {
+ return;
+ }
+
+ switch (param) {
+ case Position:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ Vector3f pos = src.getWorldTranslation();
+ break;
+ case Velocity:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ Vector3f vel = src.getVelocity();
+ break;
+ case MaxDistance:
+ if (!src.isPositional()) {
+ return;
+ }
+ break;
+ case RefDistance:
+ if (!src.isPositional()) {
+ return;
+ }
+ break;
+ case ReverbFilter:
+ if (!src.isPositional() || !src.isReverbEnabled()) {
+ return;
+ }
+ break;
+ case ReverbEnabled:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ if (src.isReverbEnabled()) {
+ updateSourceParam(src, AudioParam.ReverbFilter);
+ }
+ break;
+ case IsPositional:
+ break;
+ case Direction:
+ if (!src.isDirectional()) {
+ return;
+ }
+
+ Vector3f dir = src.getDirection();
+ break;
+ case InnerAngle:
+ if (!src.isDirectional()) {
+ return;
+ }
+ break;
+ case OuterAngle:
+ if (!src.isDirectional()) {
+ return;
+ }
+ break;
+ case IsDirectional:
+ if (src.isDirectional()) {
+ updateSourceParam(src, AudioParam.Direction);
+ updateSourceParam(src, AudioParam.InnerAngle);
+ updateSourceParam(src, AudioParam.OuterAngle);
+ } else {
+ }
+ break;
+ case DryFilter:
+ if (src.getDryFilter() != null) {
+ Filter f = src.getDryFilter();
+ if (f.isUpdateNeeded()) {
+ // updateFilter(f);
+ }
+ }
+ break;
+ case Looping:
+ if (src.isLooping()) {
+ }
+ break;
+ case Volume:
+
+ soundPool.setVolume(src.getChannel(), src.getVolume(),
+ src.getVolume());
+
+ break;
+ case Pitch:
+
+ break;
+ }
+
+ }
+
+ @Override
+ public void updateListenerParam(Listener listener, ListenerParam param) {
+ // logger.log(Level.INFO, "updateListenerParam " + param);
+ if (audioDisabled) {
+ return;
+ }
+
+ switch (param) {
+ case Position:
+ listenerPosition.set(listener.getLocation());
+
+ break;
+ case Rotation:
+ Vector3f dir = listener.getDirection();
+ Vector3f up = listener.getUp();
+
+ break;
+ case Velocity:
+ Vector3f vel = listener.getVelocity();
+
+ break;
+ case Volume:
+ // alListenerf(AL_GAIN, listener.getVolume());
+ break;
+ }
+
+ }
+
+ @Override
+ public void update(float tpf) {
+ float distance;
+ float volume;
+
+ // Loop over all mediaplayers
+ for (AudioNode src : musicPlaying.keySet()) {
+
+ MediaPlayer mp = musicPlaying.get(src);
+ {
+ // Calc the distance to the listener
+ distanceVector.set(listenerPosition);
+ distanceVector.subtractLocal(src.getLocalTranslation());
+ distance = FastMath.abs(distanceVector.length());
+
+ if (distance < src.getRefDistance()) {
+ distance = src.getRefDistance();
+ }
+ if (distance > src.getMaxDistance()) {
+ distance = src.getMaxDistance();
+ }
+ volume = src.getRefDistance() / distance;
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) {
+ // Left / Right channel get the same volume by now, only
+ // positional
+ mp.setVolume(volume, volume);
+
+ audioData.setCurrentVolume(volume);
+ }
+ }
+ }
+ }
+
+ public void setListener(Listener listener) {
+ if (audioDisabled) {
+ return;
+ }
+
+ if (this.listener != null) {
+ // previous listener no longer associated with current
+ // renderer
+ this.listener.setRenderer(null);
+ }
+
+ this.listener = listener;
+ this.listener.setRenderer(this);
+
+ }
+
+ @Override
+ public void cleanup() {
+ // Cleanup sound pool
+ if (soundPool != null) {
+ soundPool.release();
+ soundPool = null;
+ }
+
+ // Cleanup media player
+ for (AudioNode src : musicPlaying.keySet()) {
+ MediaPlayer mp = musicPlaying.get(src);
+ {
+ mp.stop();
+ mp.release();
+ src.setStatus(Status.Stopped);
+ }
+ }
+ musicPlaying.clear();
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mp.seekTo(0);
+ mp.stop();
+ // XXX: This has bad performance -> maybe change overall structure of
+ // mediaplayer in this audiorenderer?
+ for (AudioNode src : musicPlaying.keySet()) {
+ if (musicPlaying.get(src) == mp) {
+ src.setStatus(Status.Stopped);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Plays using the {@link SoundPool} of Android. Due to hard limitation of
+ * the SoundPool: After playing more instances of the sound you only have
+ * the channel of the last played instance.
+ *
+ * It is not possible to get information about the state of the soundpool of
+ * a specific streamid, so removing is not possilbe -> noone knows when
+ * sound finished.
+ */
+ public void playSourceInstance(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ if (!(audioData.getAssetKey() instanceof AudioKey)) {
+ throw new IllegalArgumentException("Asset is not a AudioKey");
+ }
+
+ AudioKey assetKey = (AudioKey) audioData.getAssetKey();
+
+ try {
+ if (audioData.getId() < 0) { // found something to load
+ int soundId = soundPool.load(
+ assetManager.openFd(assetKey.getName()), 1);
+ audioData.setId(soundId);
+ }
+
+ int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
+
+ if (channel == 0) {
+ soundpoolStillLoading.put(audioData.getId(), src);
+ } else {
+ src.setChannel(channel); // receive a channel at the last
+ // playing at least
+ }
+ } catch (IOException e) {
+ logger.log(Level.SEVERE,
+ "Failed to load sound " + assetKey.getName(), e);
+ audioData.setId(-1);
+ }
+ }
+
+ @Override
+ public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
+ AudioNode src = soundpoolStillLoading.remove(sampleId);
+
+ if (src == null) {
+ logger.warning("Something went terribly wrong! onLoadComplete"
+ + " had sampleId which was not in the HashMap of loading items");
+ return;
+ }
+
+ AudioData audioData = src.getAudioData();
+
+ if (status == 0) // load was successfull
+ {
+ int channelIndex;
+ channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
+ src.setChannel(channelIndex);
+ }
+ }
+
+ public void playSource(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp == null) {
+ mp = new MediaPlayer();
+ mp.setOnCompletionListener(this);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
+
+ try {
+ AssetKey<?> key = audioData.getAssetKey();
+
+ AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
+ mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+ afd.getLength());
+ mp.prepare();
+ mp.setLooping(src.isLooping());
+ mp.start();
+ src.setChannel(0);
+ src.setStatus(Status.Playing);
+ musicPlaying.put(src, mp);
+
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Pause the current playing sounds. Both from the {@link SoundPool} and the
+ * active {@link MediaPlayer}s
+ */
+ public void pauseAll() {
+ if (soundPool != null) {
+ soundPool.autoPause();
+ for (MediaPlayer mp : musicPlaying.values()) {
+ mp.pause();
+ }
+ }
+ }
+
+ /**
+ * Resume all paused sounds.
+ */
+ public void resumeAll() {
+ if (soundPool != null) {
+ soundPool.autoResume();
+ for (MediaPlayer mp : musicPlaying.values()) {
+ mp.start(); //no resume -> api says call start to resume
+ }
+ }
+ }
+
+ public void pauseSource(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.pause();
+ src.setStatus(Status.Paused);
+ } else {
+ int channel = src.getChannel();
+ if (channel != -1) {
+ soundPool.pause(channel); // is not very likley to make
+ } // something useful :)
+ }
+ }
+
+ public void stopSource(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ // can be stream or buffer -> so try to get mediaplayer
+ // if there is non try to stop soundpool
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.stop();
+ src.setStatus(Status.Paused);
+ } else {
+ int channel = src.getChannel();
+ if (channel != -1) {
+ soundPool.pause(channel); // is not very likley to make
+ // something useful :)
+ }
+ }
+
+ }
+
+ @Override
+ public void deleteAudioData(AudioData ad) {
+
+ for (AudioNode src : musicPlaying.keySet()) {
+ if (src.getAudioData() == ad) {
+ MediaPlayer mp = musicPlaying.remove(src);
+ mp.stop();
+ mp.release();
+ src.setStatus(Status.Stopped);
+ src.setChannel(-1);
+ ad.setId(-1);
+ break;
+ }
+ }
+
+ if (ad.getId() > 0) {
+ soundPool.unload(ad.getId());
+ ad.setId(-1);
+ }
+ }
+
+ @Override
+ public void setEnvironment(Environment env) {
+ // not yet supported
+ }
+
+ @Override
+ public void deleteFilter(Filter filter) {
+ }
+}
diff --git a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java
new file mode 100644
index 0000000..fc9c03e
--- /dev/null
+++ b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java
@@ -0,0 +1,19 @@
+package com.jme3.audio.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.audio.android.AndroidAudioData;
+import java.io.IOException;
+
+public class AndroidAudioLoader implements AssetLoader
+{
+
+ @Override
+ public Object load(AssetInfo assetInfo) throws IOException
+ {
+ AndroidAudioData result = new AndroidAudioData();
+ result.setAssetKey( assetInfo.getKey() );
+ return result;
+ }
+
+}
diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java
new file mode 100644
index 0000000..ca1141d
--- /dev/null
+++ b/engine/src/android/com/jme3/input/android/AndroidInput.java
@@ -0,0 +1,619 @@
+package com.jme3.input.android;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.TouchInput;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.input.event.TouchEvent;
+import com.jme3.input.event.TouchEvent.Type;
+import com.jme3.math.Vector2f;
+import com.jme3.util.RingBuffer;
+import java.util.HashMap;
+import java.util.logging.Logger;
+
+/**
+ * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
+ * @author larynx
+ *
+ */
+public class AndroidInput extends GLSurfaceView implements TouchInput,
+ GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener {
+
+ final private static int MAX_EVENTS = 1024;
+ // Custom settings
+ public boolean mouseEventsEnabled = true;
+ public boolean mouseEventsInvertX = false;
+ public boolean mouseEventsInvertY = false;
+ public boolean keyboardEventsEnabled = false;
+ public boolean dontSendHistory = false;
+ // Used to transfer events from android thread to GLThread
+ final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
+ final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
+ final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
+ final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
+ // Internal
+ private ScaleGestureDetector scaledetector;
+ private GestureDetector detector;
+ private int lastX;
+ private int lastY;
+ private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
+ private boolean isInitialized = false;
+ private RawInputListener listener = null;
+ private static final int[] ANDROID_TO_JME = {
+ 0x0, // unknown
+ 0x0, // key code soft left
+ 0x0, // key code soft right
+ KeyInput.KEY_HOME,
+ KeyInput.KEY_ESCAPE, // key back
+ 0x0, // key call
+ 0x0, // key endcall
+ KeyInput.KEY_0,
+ KeyInput.KEY_1,
+ KeyInput.KEY_2,
+ KeyInput.KEY_3,
+ KeyInput.KEY_4,
+ KeyInput.KEY_5,
+ KeyInput.KEY_6,
+ KeyInput.KEY_7,
+ KeyInput.KEY_8,
+ KeyInput.KEY_9,
+ KeyInput.KEY_MULTIPLY,
+ 0x0, // key pound
+ KeyInput.KEY_UP,
+ KeyInput.KEY_DOWN,
+ KeyInput.KEY_LEFT,
+ KeyInput.KEY_RIGHT,
+ KeyInput.KEY_RETURN, // dpad center
+ 0x0, // volume up
+ 0x0, // volume down
+ KeyInput.KEY_POWER, // power (?)
+ 0x0, // camera
+ 0x0, // clear
+ KeyInput.KEY_A,
+ KeyInput.KEY_B,
+ KeyInput.KEY_C,
+ KeyInput.KEY_D,
+ KeyInput.KEY_E,
+ KeyInput.KEY_F,
+ KeyInput.KEY_G,
+ KeyInput.KEY_H,
+ KeyInput.KEY_I,
+ KeyInput.KEY_J,
+ KeyInput.KEY_K,
+ KeyInput.KEY_L,
+ KeyInput.KEY_M,
+ KeyInput.KEY_N,
+ KeyInput.KEY_O,
+ KeyInput.KEY_P,
+ KeyInput.KEY_Q,
+ KeyInput.KEY_R,
+ KeyInput.KEY_S,
+ KeyInput.KEY_T,
+ KeyInput.KEY_U,
+ KeyInput.KEY_V,
+ KeyInput.KEY_W,
+ KeyInput.KEY_X,
+ KeyInput.KEY_Y,
+ KeyInput.KEY_Z,
+ KeyInput.KEY_COMMA,
+ KeyInput.KEY_PERIOD,
+ KeyInput.KEY_LMENU,
+ KeyInput.KEY_RMENU,
+ KeyInput.KEY_LSHIFT,
+ KeyInput.KEY_RSHIFT,
+ // 0x0, // fn
+ // 0x0, // cap (?)
+
+ KeyInput.KEY_TAB,
+ KeyInput.KEY_SPACE,
+ 0x0, // sym (?) symbol
+ 0x0, // explorer
+ 0x0, // envelope
+ KeyInput.KEY_RETURN, // newline/enter
+ KeyInput.KEY_DELETE,
+ KeyInput.KEY_GRAVE,
+ KeyInput.KEY_MINUS,
+ KeyInput.KEY_EQUALS,
+ KeyInput.KEY_LBRACKET,
+ KeyInput.KEY_RBRACKET,
+ KeyInput.KEY_BACKSLASH,
+ KeyInput.KEY_SEMICOLON,
+ KeyInput.KEY_APOSTROPHE,
+ KeyInput.KEY_SLASH,
+ KeyInput.KEY_AT, // at (@)
+ KeyInput.KEY_NUMLOCK, //0x0, // num
+ 0x0, //headset hook
+ 0x0, //focus
+ KeyInput.KEY_ADD,
+ KeyInput.KEY_LMETA, //menu
+ 0x0,//notification
+ 0x0,//search
+ 0x0,//media play/pause
+ 0x0,//media stop
+ 0x0,//media next
+ 0x0,//media previous
+ 0x0,//media rewind
+ 0x0,//media fastforward
+ 0x0,//mute
+ };
+
+ public AndroidInput(Context ctx, AttributeSet attribs) {
+ super(ctx, attribs);
+ detector = new GestureDetector(null, this, null, false);
+ scaledetector = new ScaleGestureDetector(ctx, this);
+
+ }
+
+ public AndroidInput(Context ctx) {
+ super(ctx);
+ detector = new GestureDetector(null, this, null, false);
+ scaledetector = new ScaleGestureDetector(ctx, this);
+ }
+
+ private TouchEvent getNextFreeTouchEvent() {
+ return getNextFreeTouchEvent(false);
+ }
+
+ /**
+ * Fetches a touch event from the reuse pool
+ * @param wait if true waits for a reusable event to get available/released by an other thread, if false returns a new one if needed
+ * @return a usable TouchEvent
+ */
+ private TouchEvent getNextFreeTouchEvent(boolean wait) {
+ TouchEvent evt = null;
+ synchronized (eventPoolUnConsumed) {
+ int size = eventPoolUnConsumed.size();
+ while (size > 0) {
+ evt = eventPoolUnConsumed.pop();
+ if (!evt.isConsumed()) {
+ eventPoolUnConsumed.push(evt);
+ evt = null;
+ } else {
+ break;
+ }
+ size--;
+ }
+ }
+
+
+ if (evt == null) {
+ if (eventPool.isEmpty() && wait) {
+ logger.warning("eventPool buffer underrun");
+ boolean isEmpty;
+ do {
+ synchronized (eventPool) {
+ isEmpty = eventPool.isEmpty();
+ }
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ }
+ } while (isEmpty);
+ synchronized (eventPool) {
+ evt = eventPool.pop();
+ }
+ } else if (eventPool.isEmpty()) {
+ evt = new TouchEvent();
+ logger.warning("eventPool buffer underrun");
+ } else {
+ synchronized (eventPool) {
+ evt = eventPool.pop();
+ }
+ }
+ }
+ return evt;
+ }
+
+ /**
+ * onTouchEvent gets called from android thread on touchpad events
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean bWasHandled = false;
+ TouchEvent touch;
+ // System.out.println("native : " + event.getAction());
+ int action = event.getAction() & MotionEvent.ACTION_MASK;
+ int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ int pointerId = event.getPointerId(pointerIndex);
+
+ // final int historySize = event.getHistorySize();
+ //final int pointerCount = event.getPointerCount();
+
+
+ switch (action) {
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_DOWN:
+
+
+ touch = getNextFreeTouchEvent();
+ touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ processEvent(touch);
+
+ bWasHandled = true;
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+
+ touch = getNextFreeTouchEvent();
+ touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ processEvent(touch);
+
+
+ bWasHandled = true;
+ break;
+ case MotionEvent.ACTION_MOVE:
+
+
+ // Convert all pointers into events
+ for (int p = 0; p < event.getPointerCount(); p++) {
+ Vector2f lastPos = lastPositions.get(pointerIndex);
+ if (lastPos == null) {
+ lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
+ lastPositions.put(pointerId, lastPos);
+ }
+ touch = getNextFreeTouchEvent();
+ touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ processEvent(touch);
+ lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
+ }
+ bWasHandled = true;
+ break;
+
+
+ case MotionEvent.ACTION_OUTSIDE:
+ break;
+
+ }
+
+ // Try to detect gestures
+ this.detector.onTouchEvent(event);
+ this.scaledetector.onTouchEvent(event);
+
+ return bWasHandled;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ TouchEvent evt;
+ evt = getNextFreeTouchEvent();
+ evt.set(TouchEvent.Type.KEY_DOWN);
+ evt.setKeyCode(keyCode);
+ evt.setCharacters(event.getCharacters());
+ evt.setTime(event.getEventTime());
+
+ // Send the event
+ processEvent(evt);
+
+ // Handle all keys ourself except Volume Up/Down
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ TouchEvent evt;
+ evt = getNextFreeTouchEvent();
+ evt.set(TouchEvent.Type.KEY_UP);
+ evt.setKeyCode(keyCode);
+ evt.setCharacters(event.getCharacters());
+ evt.setTime(event.getEventTime());
+
+ // Send the event
+ processEvent(evt);
+
+ // Handle all keys ourself except Volume Up/Down
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ // -----------------------------------------
+ // JME3 Input interface
+ @Override
+ public void initialize() {
+ TouchEvent item;
+ for (int i = 0; i < MAX_EVENTS; i++) {
+ item = new TouchEvent();
+ eventPool.push(item);
+ }
+ isInitialized = true;
+ }
+
+ @Override
+ public void destroy() {
+ isInitialized = false;
+
+ // Clean up queues
+ while (!eventPool.isEmpty()) {
+ eventPool.pop();
+ }
+ while (!eventQueue.isEmpty()) {
+ eventQueue.pop();
+ }
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return isInitialized;
+ }
+
+ @Override
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+ // -----------------------------------------
+
+ private void processEvent(TouchEvent event) {
+ synchronized (eventQueue) {
+ eventQueue.push(event);
+ }
+ }
+
+ // --------------- INSIDE GLThread ---------------
+ @Override
+ public void update() {
+ generateEvents();
+ }
+
+ private void generateEvents() {
+ if (listener != null) {
+ TouchEvent event;
+ MouseButtonEvent btn;
+ int newX;
+ int newY;
+
+ while (!eventQueue.isEmpty()) {
+ synchronized (eventQueue) {
+ event = eventQueue.pop();
+ }
+ if (event != null) {
+ listener.onTouchEvent(event);
+
+ if (mouseEventsEnabled) {
+ if (mouseEventsInvertX) {
+ newX = this.getWidth() - (int) event.getX();
+ } else {
+ newX = (int) event.getX();
+ }
+
+ if (mouseEventsInvertY) {
+ newY = this.getHeight() - (int) event.getY();
+ } else {
+ newY = (int) event.getY();
+ }
+
+ switch (event.getType()) {
+ case DOWN:
+ // Handle mouse down event
+ btn = new MouseButtonEvent(0, true, newX, newY);
+ btn.setTime(event.getTime());
+ listener.onMouseButtonEvent(btn);
+ // Store current pos
+ lastX = -1;
+ lastY = -1;
+ break;
+
+ case UP:
+ // Handle mouse up event
+ btn = new MouseButtonEvent(0, false, newX, newY);
+ btn.setTime(event.getTime());
+ listener.onMouseButtonEvent(btn);
+ // Store current pos
+ lastX = -1;
+ lastY = -1;
+ break;
+
+ case MOVE:
+ int dx;
+ int dy;
+ if (lastX != -1) {
+ dx = newX - lastX;
+ dy = newY - lastY;
+ } else {
+ dx = 0;
+ dy = 0;
+ }
+ MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
+ mot.setTime(event.getTime());
+ listener.onMouseMotionEvent(mot);
+ lastX = newX;
+ lastY = newY;
+ break;
+ }
+
+
+ }
+ }
+
+ if (event.isConsumed() == false) {
+ synchronized (eventPoolUnConsumed) {
+ eventPoolUnConsumed.push(event);
+ }
+
+ } else {
+ synchronized (eventPool) {
+ eventPool.push(event);
+ }
+ }
+ }
+
+ }
+ }
+ // --------------- ENDOF INSIDE GLThread ---------------
+
+ // --------------- Gesture detected callback events ---------------
+ public boolean onDown(MotionEvent event) {
+ return false;
+ }
+
+ public void onLongPress(MotionEvent event) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(event.getEventTime());
+ processEvent(touch);
+ }
+
+ public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);
+ touch.setPointerId(0);
+ touch.setTime(event.getEventTime());
+ processEvent(touch);
+
+ return true;
+ }
+
+ public boolean onSingleTapConfirmed(MotionEvent event) {
+ //Nothing to do here the tap has already been detected.
+ return false;
+ }
+
+ public boolean onDoubleTap(MotionEvent event) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(event.getEventTime());
+ processEvent(touch);
+ return true;
+ }
+
+ public boolean onDoubleTapEvent(MotionEvent event) {
+ return false;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(scaleGestureDetector.getEventTime());
+ touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+ touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+ processEvent(touch);
+ // System.out.println("scaleBegin");
+
+ return true;
+ }
+
+ public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(scaleGestureDetector.getEventTime());
+ touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+ touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+ processEvent(touch);
+ // System.out.println("scale");
+
+ return false;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(scaleGestureDetector.getEventTime());
+ touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+ touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+ processEvent(touch);
+ }
+
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));
+ touch.setPointerId(0);
+ touch.setTime(e1.getEventTime());
+ processEvent(touch);
+ //System.out.println("scroll " + e1.getPointerCount());
+ return false;
+ }
+
+ public void onShowPress(MotionEvent event) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(event.getEventTime());
+ processEvent(touch);
+ }
+
+ public boolean onSingleTapUp(MotionEvent event) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(event.getEventTime());
+ processEvent(touch);
+ return true;
+ }
+
+ @Override
+ public void setSimulateMouse(boolean simulate) {
+ mouseEventsEnabled = simulate;
+ }
+
+ @Override
+ public void setSimulateKeyboard(boolean simulate) {
+ keyboardEventsEnabled = simulate;
+ }
+
+ @Override
+ public void setOmitHistoricEvents(boolean dontSendHistory) {
+ this.dontSendHistory = dontSendHistory;
+ }
+
+ // TODO: move to TouchInput
+ public boolean isMouseEventsEnabled() {
+ return mouseEventsEnabled;
+ }
+
+ public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
+ this.mouseEventsEnabled = mouseEventsEnabled;
+ }
+
+ public boolean isMouseEventsInvertY() {
+ return mouseEventsInvertY;
+ }
+
+ public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
+ this.mouseEventsInvertY = mouseEventsInvertY;
+ }
+
+ public boolean isMouseEventsInvertX() {
+ return mouseEventsInvertX;
+ }
+
+ public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
+ this.mouseEventsInvertX = mouseEventsInvertX;
+ }
+}
diff --git a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java
new file mode 100644
index 0000000..34e4592
--- /dev/null
+++ b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java
@@ -0,0 +1,19 @@
+package com.jme3.input.android;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.TouchEvent;
+
+/**
+ * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens
+ * For use with class AndroidInput
+ * @author larynx
+ *
+ */
+public interface AndroidTouchInputListener extends RawInputListener
+{
+ public void onTouchEvent(TouchEvent evt);
+ public void onMotionEvent(MotionEvent evt);
+ public void onAndroidKeyEvent(KeyEvent evt);
+}
diff --git a/engine/src/android/com/jme3/renderer/android/Android22Workaround.java b/engine/src/android/com/jme3/renderer/android/Android22Workaround.java
new file mode 100644
index 0000000..9c5bf58
--- /dev/null
+++ b/engine/src/android/com/jme3/renderer/android/Android22Workaround.java
@@ -0,0 +1,14 @@
+package com.jme3.renderer.android;
+
+import android.opengl.GLES20;
+
+public class Android22Workaround {
+ public static void glVertexAttribPointer(int location, int components, int format, boolean normalize, int stride, int offset){
+ GLES20.glVertexAttribPointer(location,
+ components,
+ format,
+ normalize,
+ stride,
+ offset);
+ }
+}
diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
new file mode 100644
index 0000000..b5a8c14
--- /dev/null
+++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
@@ -0,0 +1,2951 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.renderer.android;
+
+import android.graphics.Bitmap;
+import android.opengl.GLES10;
+import android.opengl.GLES11;
+import android.opengl.GLES20;
+import android.os.Build;
+import com.jme3.asset.AndroidImageInfo;
+import com.jme3.light.LightList;
+import com.jme3.material.RenderState;
+import com.jme3.math.*;
+import com.jme3.renderer.*;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.shader.Attribute;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.shader.Shader.ShaderType;
+import com.jme3.shader.Uniform;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.RenderBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.ListMap;
+import com.jme3.util.NativeObjectManager;
+import com.jme3.util.SafeArrayList;
+import java.nio.*;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.microedition.khronos.opengles.GL10;
+
+public class OGLESShaderRenderer implements Renderer {
+
+ private static final Logger logger = Logger.getLogger(OGLESShaderRenderer.class.getName());
+ private static final boolean VALIDATE_SHADER = false;
+ private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
+ private final StringBuilder stringBuf = new StringBuilder(250);
+ private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1);
+ private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
+ private final RenderContext context = new RenderContext();
+ private final NativeObjectManager objManager = new NativeObjectManager();
+ private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
+ // current state
+ private Shader boundShader;
+ private int initialDrawBuf, initialReadBuf;
+ private int glslVer;
+ private int vertexTextureUnits;
+ private int fragTextureUnits;
+ private int vertexUniforms;
+ private int fragUniforms;
+ private int vertexAttribs;
+ private int maxFBOSamples;
+ private int maxFBOAttachs;
+ private int maxMRTFBOAttachs;
+ private int maxRBSize;
+ private int maxTexSize;
+ private int maxCubeTexSize;
+ private int maxVertCount;
+ private int maxTriCount;
+ private boolean tdc;
+ private FrameBuffer lastFb = null;
+ private final Statistics statistics = new Statistics();
+ private int vpX, vpY, vpW, vpH;
+ private int clipX, clipY, clipW, clipH;
+ //private final GL10 gl;
+ private boolean powerVr = false;
+ private boolean powerOf2 = false;
+ private boolean verboseLogging = false;
+ private boolean useVBO = false;
+ private boolean checkErrors = true;
+
+ public OGLESShaderRenderer() {
+ }
+
+ public void setUseVA(boolean value) {
+ logger.log(Level.INFO, "use_VBO [{0}] -> [{1}]", new Object[]{useVBO, !value});
+ useVBO = !value;
+ }
+
+ public void setVerboseLogging(boolean value) {
+ logger.log(Level.INFO, "verboseLogging [{0}] -> [{1}]", new Object[]{verboseLogging, value});
+ verboseLogging = value;
+ }
+
+ protected void updateNameBuffer() {
+ int len = stringBuf.length();
+
+ nameBuf.position(0);
+ nameBuf.limit(len);
+ for (int i = 0; i < len; i++) {
+ nameBuf.put((byte) stringBuf.charAt(i));
+ }
+
+ nameBuf.rewind();
+ }
+
+ private void checkGLError() {
+ if (!checkErrors) return;
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ throw new RendererException("OpenGL Error " + error);
+ }
+ }
+
+ private boolean log(String message) {
+ logger.info(message);
+ return true;
+ }
+
+ public Statistics getStatistics() {
+ return statistics;
+ }
+
+ public EnumSet<Caps> getCaps() {
+ return caps;
+ }
+
+ public void initialize() {
+
+ logger.log(Level.INFO, "Vendor: {0}", GLES20.glGetString(GLES20.GL_VENDOR));
+ logger.log(Level.INFO, "Renderer: {0}", GLES20.glGetString(GLES20.GL_RENDERER));
+ logger.log(Level.INFO, "Version: {0}", GLES20.glGetString(GLES20.GL_VERSION));
+
+ powerVr = GLES20.glGetString(GLES20.GL_RENDERER).contains("PowerVR");
+
+ String versionStr = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);
+ logger.log(Level.INFO, "GLES20.Shading Language Version: {0}", versionStr);
+ if (versionStr == null || versionStr.equals("")) {
+ glslVer = -1;
+ throw new UnsupportedOperationException("GLSL and OpenGL2 is "
+ + "required for the OpenGL ES "
+ + "renderer!");
+ }
+
+ // Fix issue in TestRenderToMemory when GL_FRONT is the main
+ // buffer being used.
+
+// initialDrawBuf = GLES20.glGetIntegeri(GLES20.GL_DRAW_BUFFER);
+// initialReadBuf = GLES20.glGetIntegeri(GLES20.GL_READ_BUFFER);
+
+ String openGlEsStr = "OpenGL ES GLSL ES ";
+ int spaceIdx = versionStr.indexOf(" ", openGlEsStr.length());
+ if (spaceIdx >= 1) {
+ versionStr = versionStr.substring(openGlEsStr.length(), spaceIdx).trim();
+ }else{
+ versionStr = versionStr.substring(openGlEsStr.length()).trim();
+ }
+
+ float version = Float.parseFloat(versionStr);
+ glslVer = (int) (version * 100);
+
+ switch (glslVer) {
+ // TODO: When new versions of OpenGL ES shader language come out,
+ // update this.
+ default:
+ caps.add(Caps.GLSL100);
+ break;
+ }
+
+ if (!caps.contains(Caps.GLSL100)) {
+ logger.info("Force-adding GLSL100 support, since OpenGL2 is supported.");
+ caps.add(Caps.GLSL100);
+ }
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16);
+ vertexTextureUnits = intBuf16.get(0);
+ logger.log(Level.INFO, "VTF Units: {0}", vertexTextureUnits);
+ if (vertexTextureUnits > 0) {
+ caps.add(Caps.VertexTextureFetch);
+ }
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16);
+ fragTextureUnits = intBuf16.get(0);
+ logger.log(Level.INFO, "Texture Units: {0}", fragTextureUnits);
+ /*
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
+ vertexUniforms = intBuf16.get(0);
+ logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
+ fragUniforms = intBuf16.get(0);
+ logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
+ */
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_ATTRIBS, intBuf16);
+ vertexAttribs = intBuf16.get(0);
+ logger.log(Level.INFO, "Vertex Attributes: {0}", vertexAttribs);
+
+ /*
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VARYING_FLOATS, intBuf16);
+ int varyingFloats = intBuf16.get(0);
+ logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats);
+ */
+
+ GLES20.glGetIntegerv(GLES20.GL_SUBPIXEL_BITS, intBuf16);
+ int subpixelBits = intBuf16.get(0);
+ logger.log(Level.INFO, "Subpixel Bits: {0}", subpixelBits);
+ /*
+ GLES20.glGetIntegerv(GLES20.GL_MAX_ELEMENTS_VERTICES, intBuf16);
+ maxVertCount = intBuf16.get(0);
+ logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_ELEMENTS_INDICES, intBuf16);
+ maxTriCount = intBuf16.get(0);
+ logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
+ */
+ GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, intBuf16);
+ maxTexSize = intBuf16.get(0);
+ logger.log(Level.INFO, "Maximum Texture Resolution: {0}", maxTexSize);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16);
+ maxCubeTexSize = intBuf16.get(0);
+ logger.log(Level.INFO, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
+
+
+ /*
+ if (ctxCaps.GL_ARB_color_buffer_float){
+ // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
+ if (ctxCaps.GL_ARB_half_float_pixel){
+ caps.add(Caps.FloatColorBuffer);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_depth_buffer_float){
+ caps.add(Caps.FloatDepthBuffer);
+ }
+
+ if (ctxCaps.GL_ARB_draw_instanced)
+ caps.add(Caps.MeshInstancing);
+
+ if (ctxCaps.GL_ARB_fragment_program)
+ caps.add(Caps.ARBprogram);
+
+ if (ctxCaps.GL_ARB_texture_buffer_object)
+ caps.add(Caps.TextureBuffer);
+
+ if (ctxCaps.GL_ARB_texture_float){
+ if (ctxCaps.GL_ARB_half_float_pixel){
+ caps.add(Caps.FloatTexture);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_vertex_array_object)
+ caps.add(Caps.VertexBufferArray);
+
+ boolean latc = ctxCaps.GL_EXT_texture_compression_latc;
+ boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc;
+ if (latc || atdc){
+ caps.add(Caps.TextureCompressionLATC);
+ if (atdc && !latc){
+ tdc = true;
+ }
+ }
+
+ if (ctxCaps.GL_EXT_packed_float){
+ caps.add(Caps.PackedFloatColorBuffer);
+ if (ctxCaps.GL_ARB_half_float_pixel){
+ // because textures are usually uploaded as RGB16F
+ // need half-float pixel
+ caps.add(Caps.PackedFloatTexture);
+ }
+ }
+
+ if (ctxCaps.GL_EXT_texture_array)
+ caps.add(Caps.TextureArray);
+
+ if (ctxCaps.GL_EXT_texture_shared_exponent)
+ caps.add(Caps.SharedExponentTexture);
+
+ if (ctxCaps.GL_EXT_framebuffer_object){
+ caps.add(Caps.FrameBuffer);
+
+ glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
+ maxRBSize = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
+
+ glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16);
+ maxFBOAttachs = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
+
+ if (ctxCaps.GL_EXT_framebuffer_multisample){
+ caps.add(Caps.FrameBufferMultisample);
+
+ glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
+ maxFBOSamples = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
+ }
+
+ if (ctxCaps.GL_ARB_draw_buffers){
+ caps.add(Caps.FrameBufferMRT);
+ glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16);
+ maxMRTFBOAttachs = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_multisample){
+ glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16);
+ boolean available = intBuf16.get(0) != 0;
+ glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16);
+ int samples = intBuf16.get(0);
+ logger.log(Level.FINER, "Samples: {0}", samples);
+ boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB);
+ if (samples > 0 && available && !enabled){
+ glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
+ }
+ }
+ */
+
+ String extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS);
+ logger.log(Level.INFO, "GL_EXTENSIONS: {0}", extensions);
+
+ GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, intBuf16);
+ for (int i = 0; i < intBuf16.limit(); i++) {
+ logger.log(Level.INFO, "Compressed Texture Formats: {0}", intBuf16.get(i));
+ }
+
+ if (extensions.contains("GL_OES_texture_npot")) {
+ powerOf2 = true;
+ }
+
+ applyRenderState(RenderState.DEFAULT);
+// GLES20.glClearDepthf(1.0f);
+
+ if (verboseLogging) {
+ logger.info("GLES20.glDisable(GL10.GL_DITHER)");
+ }
+
+ GLES20.glDisable(GL10.GL_DITHER);
+
+ checkGLError();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST)");
+ }
+
+ //It seems that GL10.GL_PERSPECTIVE_CORRECTION_HINT gives invalid_enum error on android.
+// GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
+
+// checkGLError();
+
+ useVBO = false;
+
+ // NOTE: SDK_INT is only available since 1.6,
+ // but for jME3 it doesn't matter since android versions 1.5 and below
+ // are not supported.
+ if (Build.VERSION.SDK_INT >= 9){
+ useVBO = true;
+ }
+
+ logger.log(Level.INFO, "Caps: {0}", caps);
+ }
+
+ /**
+ * <code>resetGLObjects</code> should be called when die GLView gets recreated to reset all GPU objects
+ */
+ public void resetGLObjects() {
+ objManager.resetObjects();
+ statistics.clearMemory();
+ boundShader = null;
+ lastFb = null;
+ context.reset();
+ }
+
+ public void cleanup() {
+ objManager.deleteAllObjects(this);
+ statistics.clearMemory();
+ }
+
+ private void checkCap(Caps cap) {
+ if (!caps.contains(cap)) {
+ throw new UnsupportedOperationException("Required capability missing: " + cap.name());
+ }
+ }
+
+ /*********************************************************************\
+ |* Render State *|
+ \*********************************************************************/
+ public void setDepthRange(float start, float end) {
+
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glDepthRangef({0}, {1})", new Object[]{start, end});
+ }
+ GLES20.glDepthRangef(start, end);
+ checkGLError();
+ }
+
+ public void clearBuffers(boolean color, boolean depth, boolean stencil) {
+ int bits = 0;
+ if (color) {
+ bits = GLES20.GL_COLOR_BUFFER_BIT;
+ }
+ if (depth) {
+ bits |= GLES20.GL_DEPTH_BUFFER_BIT;
+ }
+ if (stencil) {
+ bits |= GLES20.GL_STENCIL_BUFFER_BIT;
+ }
+ if (bits != 0) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glClear(color={0}, depth={1}, stencil={2})", new Object[]{color, depth, stencil});
+ }
+ GLES20.glClear(bits);
+ checkGLError();
+ }
+ }
+
+ public void setBackgroundColor(ColorRGBA color) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glClearColor({0}, {1}, {2}, {3})", new Object[]{color.r, color.g, color.b, color.a});
+ }
+ GLES20.glClearColor(color.r, color.g, color.b, color.a);
+ checkGLError();
+ }
+
+ public void applyRenderState(RenderState state) {
+ /*
+ if (state.isWireframe() && !context.wireframe){
+ GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_LINE);
+ context.wireframe = true;
+ }else if (!state.isWireframe() && context.wireframe){
+ GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_FILL);
+ context.wireframe = false;
+ }
+ */
+ if (state.isDepthTest() && !context.depthTestEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glEnable(GLES20.GL_DEPTH_TEST)");
+ }
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+ checkGLError();
+ if (verboseLogging) {
+ logger.info("GLES20.glDepthFunc(GLES20.GL_LEQUAL)");
+ }
+ GLES20.glDepthFunc(GLES20.GL_LEQUAL);
+ checkGLError();
+ context.depthTestEnabled = true;
+ } else if (!state.isDepthTest() && context.depthTestEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDisable(GLES20.GL_DEPTH_TEST)");
+ }
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+ checkGLError();
+ context.depthTestEnabled = false;
+ }
+ if (state.isAlphaTest() && !context.alphaTestEnabled) {
+// GLES20.glEnable(GLES20.GL_ALPHA_TEST);
+// GLES20.glAlphaFunc(GLES20.GL_GREATER, state.getAlphaFallOff());
+ context.alphaTestEnabled = true;
+ } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
+// GLES20.glDisable(GLES20.GL_ALPHA_TEST);
+ context.alphaTestEnabled = false;
+ }
+ if (state.isDepthWrite() && !context.depthWriteEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDepthMask(true)");
+ }
+ GLES20.glDepthMask(true);
+ checkGLError();
+ context.depthWriteEnabled = true;
+ } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDepthMask(false)");
+ }
+ GLES20.glDepthMask(false);
+ checkGLError();
+ context.depthWriteEnabled = false;
+ }
+ if (state.isColorWrite() && !context.colorWriteEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glColorMask(true, true, true, true)");
+ }
+ GLES20.glColorMask(true, true, true, true);
+ checkGLError();
+ context.colorWriteEnabled = true;
+ } else if (!state.isColorWrite() && context.colorWriteEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glColorMask(false, false, false, false)");
+ }
+ GLES20.glColorMask(false, false, false, false);
+ checkGLError();
+ context.colorWriteEnabled = false;
+ }
+ if (state.isPointSprite() && !context.pointSprite) {
+// GLES20.glEnable(GLES20.GL_POINT_SPRITE);
+// GLES20.glTexEnvi(GLES20.GL_POINT_SPRITE, GLES20.GL_COORD_REPLACE, GLES20.GL_TRUE);
+// GLES20.glEnable(GLES20.GL_VERTEX_PROGRAM_POINT_SIZE);
+// GLES20.glPointParameterf(GLES20.GL_POINT_SIZE_MIN, 1.0f);
+ } else if (!state.isPointSprite() && context.pointSprite) {
+// GLES20.glDisable(GLES20.GL_POINT_SPRITE);
+ }
+
+ if (state.isPolyOffset()) {
+ if (!context.polyOffsetEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL)");
+ }
+ GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
+ checkGLError();
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glPolygonOffset({0}, {1})", new Object[]{state.getPolyOffsetFactor(), state.getPolyOffsetUnits()});
+ }
+ GLES20.glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ checkGLError();
+ context.polyOffsetEnabled = true;
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ } else {
+ if (state.getPolyOffsetFactor() != context.polyOffsetFactor
+ || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glPolygonOffset({0}, {1})", new Object[]{state.getPolyOffsetFactor(), state.getPolyOffsetUnits()});
+ }
+ GLES20.glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ checkGLError();
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }
+ }
+ } else {
+ if (context.polyOffsetEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL)");
+ }
+ GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
+ checkGLError();
+ context.polyOffsetEnabled = false;
+ context.polyOffsetFactor = 0;
+ context.polyOffsetUnits = 0;
+ }
+ }
+ if (state.getFaceCullMode() != context.cullMode) {
+ if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDisable(GLES20.GL_CULL_FACE)");
+ }
+ GLES20.glDisable(GLES20.GL_CULL_FACE);
+ } else {
+ if (verboseLogging) {
+ logger.info("GLES20.glEnable(GLES20.GL_CULL_FACE)");
+ }
+ GLES20.glEnable(GLES20.GL_CULL_FACE);
+ }
+
+ checkGLError();
+
+ switch (state.getFaceCullMode()) {
+ case Off:
+ break;
+ case Back:
+ if (verboseLogging) {
+ logger.info("GLES20.glCullFace(GLES20.GL_BACK)");
+ }
+ GLES20.glCullFace(GLES20.GL_BACK);
+ break;
+ case Front:
+ if (verboseLogging) {
+ logger.info("GLES20.glCullFace(GLES20.GL_FRONT)");
+ }
+ GLES20.glCullFace(GLES20.GL_FRONT);
+ break;
+ case FrontAndBack:
+ if (verboseLogging) {
+ logger.info("GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK)");
+ }
+ GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized face cull mode: "
+ + state.getFaceCullMode());
+ }
+
+ checkGLError();
+
+ context.cullMode = state.getFaceCullMode();
+ }
+
+ if (state.getBlendMode() != context.blendMode) {
+ if (state.getBlendMode() == RenderState.BlendMode.Off) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDisable(GLES20.GL_BLEND)");
+ }
+ GLES20.glDisable(GLES20.GL_BLEND);
+ } else {
+ if (verboseLogging) {
+ logger.info("GLES20.glEnable(GLES20.GL_BLEND)");
+ }
+ GLES20.glEnable(GLES20.GL_BLEND);
+ switch (state.getBlendMode()) {
+ case Off:
+ break;
+ case Additive:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
+ break;
+ case AlphaAdditive:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE);
+ break;
+ case Color:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR);
+ break;
+ case Alpha:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case PremultAlpha:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case Modulate:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_ZERO)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_ZERO);
+ break;
+ case ModulateX2:
+ if (verboseLogging) {
+ logger.info("GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_SRC_COLOR)");
+ }
+ GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_SRC_COLOR);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized blend mode: "
+ + state.getBlendMode());
+ }
+ }
+
+ checkGLError();
+
+ context.blendMode = state.getBlendMode();
+ }
+ }
+
+ /*********************************************************************\
+ |* Camera and World transforms *|
+ \*********************************************************************/
+ public void setViewPort(int x, int y, int w, int h) {
+ if (x != vpX || vpY != y || vpW != w || vpH != h) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glViewport({0}, {1}, {2}, {3})", new Object[]{x, y, w, h});
+ }
+ GLES20.glViewport(x, y, w, h);
+ checkGLError();
+ vpX = x;
+ vpY = y;
+ vpW = w;
+ vpH = h;
+ }
+ }
+
+ public void setClipRect(int x, int y, int width, int height) {
+ if (!context.clipRectEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glEnable(GLES20.GL_SCISSOR_TEST)");
+ }
+ GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
+ checkGLError();
+ context.clipRectEnabled = true;
+ }
+ if (clipX != x || clipY != y || clipW != width || clipH != height) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glScissor({0}, {1}, {2}, {3})", new Object[]{x, y, width, height});
+ }
+ GLES20.glScissor(x, y, width, height);
+ clipX = x;
+ clipY = y;
+ clipW = width;
+ clipH = height;
+ checkGLError();
+ }
+ }
+
+ public void clearClipRect() {
+ if (context.clipRectEnabled) {
+ if (verboseLogging) {
+ logger.info("GLES20.glDisable(GLES20.GL_SCISSOR_TEST)");
+ }
+ GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
+ checkGLError();
+ context.clipRectEnabled = false;
+
+ clipX = 0;
+ clipY = 0;
+ clipW = 0;
+ clipH = 0;
+ }
+ }
+
+ public void onFrame() {
+ if (!checkErrors){
+ int error = GLES20.glGetError();
+ if (error != GLES20.GL_NO_ERROR){
+ throw new RendererException("OpenGL Error " + error + ". Enable error checking for more info.");
+ }
+ }
+ objManager.deleteUnused(this);
+// statistics.clearFrame();
+ }
+
+ public void setWorldMatrix(Matrix4f worldMatrix) {
+ }
+
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
+ }
+
+ /*********************************************************************\
+ |* Shaders *|
+ \*********************************************************************/
+ protected void updateUniformLocation(Shader shader, Uniform uniform) {
+ stringBuf.setLength(0);
+ stringBuf.append(uniform.getName()).append('\0');
+ updateNameBuffer();
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glGetUniformLocation({0}, {1})", new Object[]{shader.getId(), uniform.getName()});
+ }
+ int loc = GLES20.glGetUniformLocation(shader.getId(), uniform.getName());
+ checkGLError();
+ if (loc < 0) {
+ uniform.setLocation(-1);
+ // uniform is not declared in shader
+ if (verboseLogging) {
+ logger.log(Level.WARNING, "Uniform [{0}] is not declared in shader.", uniform.getName());
+ }
+ } else {
+ uniform.setLocation(loc);
+ }
+ }
+
+ protected void updateUniform(Shader shader, Uniform uniform) {
+ int shaderId = shader.getId();
+
+ assert uniform.getName() != null;
+ assert shader.getId() > 0;
+
+ if (context.boundShaderProgram != shaderId) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glUseProgram({0})", shaderId);
+ }
+ GLES20.glUseProgram(shaderId);
+ checkGLError();
+ statistics.onShaderUse(shader, true);
+ boundShader = shader;
+ context.boundShaderProgram = shaderId;
+ } else {
+ statistics.onShaderUse(shader, false);
+ }
+
+ int loc = uniform.getLocation();
+ if (loc == -1) {
+ if (verboseLogging) {
+ logger.log(Level.WARNING, "no location for uniform [{0}]", uniform.getName());
+ }
+ return;
+ }
+
+ if (loc == -2) {
+ // get uniform location
+ updateUniformLocation(shader, uniform);
+ if (uniform.getLocation() == -1) {
+ // not declared, ignore
+
+ if (verboseLogging) {
+ logger.log(Level.WARNING, "not declared uniform: [{0}]", uniform.getName());
+ }
+
+ uniform.clearUpdateNeeded();
+ return;
+ }
+ loc = uniform.getLocation();
+ }
+
+ if (uniform.getVarType() == null) {
+ logger.warning("value is not set yet.");
+ return; // value not set yet..
+ }
+
+ statistics.onUniformSet();
+
+ uniform.clearUpdateNeeded();
+ FloatBuffer fb;
+ switch (uniform.getVarType()) {
+ case Float:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform1f set Float. " + uniform.getName());
+ }
+ Float f = (Float) uniform.getValue();
+ GLES20.glUniform1f(loc, f.floatValue());
+ break;
+ case Vector2:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform2f set Vector2. " + uniform.getName());
+ }
+ Vector2f v2 = (Vector2f) uniform.getValue();
+ GLES20.glUniform2f(loc, v2.getX(), v2.getY());
+ break;
+ case Vector3:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform3f set Vector3. " + uniform.getName());
+ }
+ Vector3f v3 = (Vector3f) uniform.getValue();
+ GLES20.glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
+ break;
+ case Vector4:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform4f set Vector4." + uniform.getName());
+ }
+ Object val = uniform.getValue();
+ if (val instanceof ColorRGBA) {
+ ColorRGBA c = (ColorRGBA) val;
+ GLES20.glUniform4f(loc, c.r, c.g, c.b, c.a);
+ } else {
+ Quaternion c = (Quaternion) uniform.getValue();
+ GLES20.glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
+ }
+ break;
+ case Boolean:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform1i set Boolean." + uniform.getName());
+ }
+ Boolean b = (Boolean) uniform.getValue();
+ GLES20.glUniform1i(loc, b.booleanValue() ? GLES20.GL_TRUE : GLES20.GL_FALSE);
+ break;
+ case Matrix3:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniformMatrix3fv set Matrix3." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ assert fb.remaining() == 9;
+ GLES20.glUniformMatrix3fv(loc, 1, false, fb);
+ break;
+ case Matrix4:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniformMatrix4fv set Matrix4." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ assert fb.remaining() == 16;
+ GLES20.glUniformMatrix4fv(loc, 1, false, fb);
+ break;
+ case FloatArray:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform1fv set FloatArray." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ GLES20.glUniform1fv(loc, fb.capacity(), fb);
+ break;
+ case Vector2Array:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform2fv set Vector2Array." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ GLES20.glUniform2fv(loc, fb.capacity() / 2, fb);
+ break;
+ case Vector3Array:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform3fv set Vector3Array." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ GLES20.glUniform3fv(loc, fb.capacity() / 3, fb);
+ break;
+ case Vector4Array:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform4fv set Vector4Array." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ GLES20.glUniform4fv(loc, fb.capacity() / 4, fb);
+ break;
+ case Matrix4Array:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform4fv set Matrix4Array." + uniform.getName());
+ }
+ fb = (FloatBuffer) uniform.getValue();
+ GLES20.glUniformMatrix4fv(loc, fb.capacity() / 16, false, fb);
+ break;
+ case Int:
+ if (verboseLogging) {
+ logger.info("GLES20.glUniform1i set Int." + uniform.getName());
+ }
+ Integer i = (Integer) uniform.getValue();
+ GLES20.glUniform1i(loc, i.intValue());
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
+ }
+ checkGLError();
+ }
+
+ protected void updateShaderUniforms(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+// for (Uniform uniform : shader.getUniforms()){
+ for (int i = 0; i < uniforms.size(); i++) {
+ Uniform uniform = uniforms.getValue(i);
+ if (uniform.isUpdateNeeded()) {
+ updateUniform(shader, uniform);
+ }
+ }
+ }
+
+ protected void resetUniformLocations(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+// for (Uniform uniform : shader.getUniforms()){
+ for (int i = 0; i < uniforms.size(); i++) {
+ Uniform uniform = uniforms.getValue(i);
+ uniform.reset(); // e.g check location again
+ }
+ }
+
+ /*
+ * (Non-javadoc)
+ * Only used for fixed-function. Ignored.
+ */
+ public void setLighting(LightList list) {
+ }
+
+ public int convertShaderType(ShaderType type) {
+ switch (type) {
+ case Fragment:
+ return GLES20.GL_FRAGMENT_SHADER;
+ case Vertex:
+ return GLES20.GL_VERTEX_SHADER;
+// case Geometry:
+// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB;
+ default:
+ throw new RuntimeException("Unrecognized shader type.");
+ }
+ }
+
+ public void updateShaderSourceData(ShaderSource source, String language) {
+ int id = source.getId();
+ if (id == -1) {
+ // create id
+ if (verboseLogging) {
+ logger.info("GLES20.glCreateShader(" + source.getType() + ")");
+ }
+ id = GLES20.glCreateShader(convertShaderType(source.getType()));
+ checkGLError();
+ if (id <= 0) {
+ throw new RendererException("Invalid ID received when trying to create shader.");
+ }
+
+ source.setId(id);
+ }
+
+ // upload shader source
+ // merge the defines and source code
+ byte[] versionData = new byte[]{};//"#version 140\n".getBytes();
+// versionData = "#define INSTANCING 1\n".getBytes();
+ byte[] definesCodeData = source.getDefines().getBytes();
+ byte[] sourceCodeData = source.getSource().getBytes();
+ ByteBuffer codeBuf = BufferUtils.createByteBuffer(versionData.length
+ + definesCodeData.length
+ + sourceCodeData.length);
+ codeBuf.put(versionData);
+ codeBuf.put(definesCodeData);
+ codeBuf.put(sourceCodeData);
+ codeBuf.flip();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glShaderSource(" + id + ")");
+ }
+
+ if (powerVr && source.getType() == ShaderType.Vertex) {
+ // XXX: This is to fix a bug in old PowerVR, remove
+ // when no longer applicable.
+ GLES20.glShaderSource(
+ id, source.getDefines()
+ + source.getSource());
+ } else {
+ GLES20.glShaderSource(
+ id,
+ "precision mediump float;\n"
+ + source.getDefines()
+ + source.getSource());
+ }
+
+ checkGLError();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glCompileShader(" + id + ")");
+ }
+
+ GLES20.glCompileShader(id);
+
+ checkGLError();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glGetShaderiv(" + id + ", GLES20.GL_COMPILE_STATUS)");
+ }
+
+ GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, intBuf1);
+
+ checkGLError();
+
+ boolean compiledOK = intBuf1.get(0) == GLES20.GL_TRUE;
+ String infoLog = null;
+
+ if (VALIDATE_SHADER || !compiledOK) {
+ // even if compile succeeded, check
+ // log for warnings
+ if (verboseLogging) {
+ logger.info("GLES20.glGetShaderiv()");
+ }
+ GLES20.glGetShaderiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1);
+ checkGLError();
+ if (verboseLogging) {
+ logger.info("GLES20.glGetShaderInfoLog(" + id + ")");
+ }
+ infoLog = GLES20.glGetShaderInfoLog(id);
+ logger.severe("Errooooooooooot(" + id + ")");
+ }
+
+ if (compiledOK) {
+ if (infoLog != null) {
+ logger.log(Level.INFO, "compile success: " + source.getName() + ", " + infoLog);
+ } else {
+ logger.log(Level.FINE, "compile success: " + source.getName());
+ }
+ } else {
+ logger.log(Level.WARNING, "Bad compile of:\n{0}{1}",
+ new Object[]{source.getDefines(), source.getSource()});
+ if (infoLog != null) {
+ throw new RendererException("compile error in:" + source + " error:" + infoLog);
+ } else {
+ throw new RendererException("compile error in:" + source + " error: <not provided>");
+ }
+ }
+
+ source.clearUpdateNeeded();
+ // only usable if compiled
+ source.setUsable(compiledOK);
+ if (!compiledOK) {
+ // make sure to dispose id cause all program's
+ // shaders will be cleared later.
+ if (verboseLogging) {
+ logger.info("GLES20.glDeleteShader(" + id + ")");
+ }
+ GLES20.glDeleteShader(id);
+ checkGLError();
+ } else {
+ // register for cleanup since the ID is usable
+ objManager.registerForCleanup(source);
+ }
+ }
+
+ public void updateShaderData(Shader shader) {
+ int id = shader.getId();
+ boolean needRegister = false;
+ if (id == -1) {
+ // create program
+
+ if (verboseLogging) {
+ logger.info("GLES20.glCreateProgram()");
+ }
+
+ id = GLES20.glCreateProgram();
+
+ if (id <= 0) {
+ throw new RendererException("Invalid ID received when trying to create shader program.");
+ }
+
+ shader.setId(id);
+ needRegister = true;
+ }
+
+ for (ShaderSource source : shader.getSources()) {
+ if (source.isUpdateNeeded()) {
+ updateShaderSourceData(source, shader.getLanguage());
+ // shader has been compiled here
+ }
+
+ if (!source.isUsable()) {
+ // it's useless.. just forget about everything..
+ shader.setUsable(false);
+ shader.clearUpdateNeeded();
+ return;
+ }
+ if (verboseLogging) {
+ logger.info("GLES20.glAttachShader(" + id + ", " + source.getId() + ")");
+ }
+
+ GLES20.glAttachShader(id, source.getId());
+ }
+
+ // link shaders to program
+ if (verboseLogging) {
+ logger.info("GLES20.glLinkProgram(" + id + ")");
+ }
+
+ GLES20.glLinkProgram(id);
+
+
+ if (verboseLogging) {
+ logger.info("GLES20.glGetProgramiv(" + id + ")");
+ }
+
+ GLES20.glGetProgramiv(id, GLES20.GL_LINK_STATUS, intBuf1);
+
+ boolean linkOK = intBuf1.get(0) == GLES20.GL_TRUE;
+ String infoLog = null;
+
+ if (VALIDATE_SHADER || !linkOK) {
+ if (verboseLogging) {
+ logger.info("GLES20.glGetProgramiv(" + id + ", GLES20.GL_INFO_LOG_LENGTH, buffer)");
+ }
+
+ GLES20.glGetProgramiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1);
+
+ int length = intBuf1.get(0);
+ if (length > 3) {
+ // get infos
+
+ if (verboseLogging) {
+ logger.info("GLES20.glGetProgramInfoLog(" + id + ")");
+ }
+
+ infoLog = GLES20.glGetProgramInfoLog(id);
+ }
+ }
+
+ if (linkOK) {
+ if (infoLog != null) {
+ logger.log(Level.INFO, "shader link success. \n{0}", infoLog);
+ } else {
+ logger.fine("shader link success");
+ }
+ } else {
+ if (infoLog != null) {
+ throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
+ } else {
+ throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
+ }
+ }
+
+ shader.clearUpdateNeeded();
+ if (!linkOK) {
+ // failure.. forget about everything
+ shader.resetSources();
+ shader.setUsable(false);
+ deleteShader(shader);
+ } else {
+ shader.setUsable(true);
+ if (needRegister) {
+ objManager.registerForCleanup(shader);
+ statistics.onNewShader();
+ } else {
+ // OpenGL spec: uniform locations may change after re-link
+ resetUniformLocations(shader);
+ }
+ }
+ }
+
+ public void setShader(Shader shader) {
+ if (verboseLogging) {
+ logger.info("setShader(" + shader + ")");
+ }
+
+ if (shader == null) {
+ if (context.boundShaderProgram > 0) {
+
+ if (verboseLogging) {
+ logger.info("GLES20.glUseProgram(0)");
+ }
+
+ GLES20.glUseProgram(0);
+
+ statistics.onShaderUse(null, true);
+ context.boundShaderProgram = 0;
+ boundShader = null;
+ }
+ } else {
+ if (shader.isUpdateNeeded()) {
+ updateShaderData(shader);
+ }
+
+ // NOTE: might want to check if any of the
+ // sources need an update?
+
+ if (!shader.isUsable()) {
+ logger.warning("shader is not usable.");
+ return;
+ }
+
+ assert shader.getId() > 0;
+
+ updateShaderUniforms(shader);
+ if (context.boundShaderProgram != shader.getId()) {
+ if (VALIDATE_SHADER) {
+ // check if shader can be used
+ // with current state
+ if (verboseLogging) {
+ logger.info("GLES20.glValidateProgram(" + shader.getId() + ")");
+ }
+
+ GLES20.glValidateProgram(shader.getId());
+
+ if (verboseLogging) {
+ logger.info("GLES20.glGetProgramiv(" + shader.getId() + ", GLES20.GL_VALIDATE_STATUS, buffer)");
+ }
+
+ GLES20.glGetProgramiv(shader.getId(), GLES20.GL_VALIDATE_STATUS, intBuf1);
+
+ boolean validateOK = intBuf1.get(0) == GLES20.GL_TRUE;
+
+ if (validateOK) {
+ logger.fine("shader validate success");
+ } else {
+ logger.warning("shader validate failure");
+ }
+ }
+
+ if (verboseLogging) {
+ logger.info("GLES20.glUseProgram(" + shader.getId() + ")");
+ }
+
+ GLES20.glUseProgram(shader.getId());
+
+ statistics.onShaderUse(shader, true);
+ context.boundShaderProgram = shader.getId();
+ boundShader = shader;
+ } else {
+ statistics.onShaderUse(shader, false);
+ }
+ }
+ }
+
+ public void deleteShaderSource(ShaderSource source) {
+ if (source.getId() < 0) {
+ logger.warning("Shader source is not uploaded to GPU, cannot delete.");
+ return;
+ }
+ source.setUsable(false);
+ source.clearUpdateNeeded();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glDeleteShader(" + source.getId() + ")");
+ }
+
+ GLES20.glDeleteShader(source.getId());
+ source.resetObject();
+ }
+
+ public void deleteShader(Shader shader) {
+ if (shader.getId() == -1) {
+ logger.warning("Shader is not uploaded to GPU, cannot delete.");
+ return;
+ }
+ for (ShaderSource source : shader.getSources()) {
+ if (source.getId() != -1) {
+
+ if (verboseLogging) {
+ logger.info("GLES20.glDetachShader(" + shader.getId() + ", " + source.getId() + ")");
+ }
+
+ GLES20.glDetachShader(shader.getId(), source.getId());
+ // the next part is done by the GLObjectManager automatically
+// glDeleteShader(source.getId());
+ }
+ }
+ // kill all references so sources can be collected
+ // if needed.
+ shader.resetSources();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glDeleteProgram(" + shader.getId() + ")");
+ }
+
+ GLES20.glDeleteProgram(shader.getId());
+
+ statistics.onDeleteShader();
+ }
+
+ /*********************************************************************\
+ |* Framebuffers *|
+ \*********************************************************************/
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
+ logger.warning("copyFrameBuffer is not supported.");
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
+ logger.warning("copyFrameBuffer is not supported.");
+ }
+ /*
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst){
+ if (GLContext.getCapabilities().GL_EXT_framebuffer_blit){
+ int srcW = 0;
+ int srcH = 0;
+ int dstW = 0;
+ int dstH = 0;
+ int prevFBO = context.boundFBO;
+
+ if (src != null && src.isUpdateNeeded())
+ updateFrameBuffer(src);
+
+ if (dst != null && dst.isUpdateNeeded())
+ updateFrameBuffer(dst);
+
+ if (src == null){
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+ // srcW = viewWidth;
+ // srcH = viewHeight;
+ }else{
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src.getId());
+ srcW = src.getWidth();
+ srcH = src.getHeight();
+ }
+ if (dst == null){
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+ // dstW = viewWidth;
+ // dstH = viewHeight;
+ }else{
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
+ dstW = dst.getWidth();
+ dstH = dst.getHeight();
+ }
+ glBlitFramebufferEXT(0, 0, srcW, srcH,
+ 0, 0, dstW, dstH,
+ GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prevFBO);
+ try {
+ checkFrameBufferError();
+ } catch (IllegalStateException ex){
+ logger.log(Level.SEVERE, "Source FBO:\n{0}", src);
+ logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst);
+ throw ex;
+ }
+ }else{
+ throw new UnsupportedOperationException("EXT_framebuffer_blit required.");
+ // TODO: support non-blit copies?
+ }
+ }
+ */
+
+ private void checkFrameBufferError() {
+ logger.warning("checkFrameBufferError is not supported.");
+ }
+ /*
+ private void checkFrameBufferError() {
+ int status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ switch (status) {
+ case GL_FRAMEBUFFER_COMPLETE_EXT:
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ //Choose different formats
+ throw new IllegalStateException("Framebuffer object format is " +
+ "unsupported by the video hardware.");
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ throw new IllegalStateException("Framebuffer has erronous attachment.");
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ throw new IllegalStateException("Framebuffer is missing required attachment.");
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+ throw new IllegalStateException("Framebuffer attachments must have same dimensions.");
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
+ throw new IllegalStateException("Framebuffer attachments must have same formats.");
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ throw new IllegalStateException("Incomplete draw buffer.");
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ throw new IllegalStateException("Incomplete read buffer.");
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
+ throw new IllegalStateException("Incomplete multisample buffer.");
+ default:
+ //Programming error; will fail on all hardware
+ throw new IllegalStateException("Some video driver error " +
+ "or programming error occured. " +
+ "Framebuffer object status is invalid. ");
+ }
+ }
+ */
+
+ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
+ logger.warning("updateRenderBuffer is not supported.");
+ }
+ /*
+ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb){
+ int id = rb.getId();
+ if (id == -1){
+ glGenRenderbuffersEXT(intBuf1);
+ id = intBuf1.get(0);
+ rb.setId(id);
+ }
+
+ if (context.boundRB != id){
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
+ context.boundRB = id;
+ }
+
+ if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize)
+ throw new UnsupportedOperationException("Resolution "+fb.getWidth()+
+ ":"+fb.getHeight()+" is not supported.");
+
+ if (fb.getSamples() > 0 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample){
+ int samples = fb.getSamples();
+ if (maxFBOSamples < samples){
+ samples = maxFBOSamples;
+ }
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
+ samples,
+ TextureUtil.convertTextureFormat(rb.getFormat()),
+ fb.getWidth(),
+ fb.getHeight());
+ }else{
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
+ TextureUtil.convertTextureFormat(rb.getFormat()),
+ fb.getWidth(),
+ fb.getHeight());
+ }
+ }
+ */
+
+ private int convertAttachmentSlot(int attachmentSlot) {
+ logger.warning("convertAttachmentSlot is not supported.");
+ return -1;
+ }
+ /*
+ private int convertAttachmentSlot(int attachmentSlot){
+ // can also add support for stencil here
+ if (attachmentSlot == -100){
+ return GL_DEPTH_ATTACHMENT_EXT;
+ }else if (attachmentSlot < 0 || attachmentSlot >= 16){
+ throw new UnsupportedOperationException("Invalid FBO attachment slot: "+attachmentSlot);
+ }
+
+ return GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
+ }
+ */
+
+ public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
+ logger.warning("updateRenderTexture is not supported.");
+ }
+ /*
+ public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb){
+ Texture tex = rb.getTexture();
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded())
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels());
+
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ convertAttachmentSlot(rb.getSlot()),
+ convertTextureType(tex.getType()),
+ image.getId(),
+ 0);
+ }
+ */
+
+ public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
+ logger.warning("updateFrameBufferAttachment is not supported.");
+ }
+ /*
+ public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb){
+ boolean needAttach;
+ if (rb.getTexture() == null){
+ // if it hasn't been created yet, then attach is required.
+ needAttach = rb.getId() == -1;
+ updateRenderBuffer(fb, rb);
+ }else{
+ needAttach = false;
+ updateRenderTexture(fb, rb);
+ }
+ if (needAttach){
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
+ convertAttachmentSlot(rb.getSlot()),
+ GL_RENDERBUFFER_EXT,
+ rb.getId());
+ }
+ }
+ */
+
+ public void updateFrameBuffer(FrameBuffer fb) {
+ logger.warning("updateFrameBuffer is not supported.");
+ }
+ /*
+ public void updateFrameBuffer(FrameBuffer fb) {
+ int id = fb.getId();
+ if (id == -1){
+ // create FBO
+ glGenFramebuffersEXT(intBuf1);
+ id = intBuf1.get(0);
+ fb.setId(id);
+ objManager.registerForCleanup(fb);
+
+ statistics.onNewFrameBuffer();
+ }
+
+ if (context.boundFBO != id){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
+ // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
+ context.boundDrawBuf = 0;
+ context.boundFBO = id;
+ }
+
+ FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
+ if (depthBuf != null){
+ updateFrameBufferAttachment(fb, depthBuf);
+ }
+
+ for (int i = 0; i < fb.getNumColorBuffers(); i++){
+ FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
+ updateFrameBufferAttachment(fb, colorBuf);
+ }
+
+ fb.clearUpdateNeeded();
+ }
+ */
+
+ public void setMainFrameBufferOverride(FrameBuffer fb){
+ }
+
+ public void setFrameBuffer(FrameBuffer fb) {
+ if (verboseLogging) {
+ logger.warning("setFrameBuffer is not supported.");
+ }
+ }
+ /*
+ public void setFrameBuffer(FrameBuffer fb) {
+ if (lastFb == fb)
+ return;
+
+ // generate mipmaps for last FB if needed
+ if (lastFb != null){
+ for (int i = 0; i < lastFb.getNumColorBuffers(); i++){
+ RenderBuffer rb = lastFb.getColorBuffer(i);
+ Texture tex = rb.getTexture();
+ if (tex != null
+ && tex.getMinFilter().usesMipMapLevels()){
+ setTexture(0, rb.getTexture());
+ glGenerateMipmapEXT(convertTextureType(tex.getType()));
+ }
+ }
+ }
+
+
+ if (fb == null){
+ // unbind any fbos
+ if (context.boundFBO != 0){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ statistics.onFrameBufferUse(null, true);
+
+ context.boundFBO = 0;
+ }
+ // select back buffer
+ if (context.boundDrawBuf != -1){
+ glDrawBuffer(initialDrawBuf);
+ context.boundDrawBuf = -1;
+ }
+ if (context.boundReadBuf != -1){
+ glReadBuffer(initialReadBuf);
+ context.boundReadBuf = -1;
+ }
+
+ lastFb = null;
+ }else{
+ if (fb.isUpdateNeeded())
+ updateFrameBuffer(fb);
+
+ if (context.boundFBO != fb.getId()){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb.getId());
+ statistics.onFrameBufferUse(fb, true);
+
+ // update viewport to reflect framebuffer's resolution
+ setViewPort(0, 0, fb.getWidth(), fb.getHeight());
+
+ context.boundFBO = fb.getId();
+ }else{
+ statistics.onFrameBufferUse(fb, false);
+ }
+ if (fb.getNumColorBuffers() == 0){
+ // make sure to select NONE as draw buf
+ // no color buffer attached. select NONE
+ if (context.boundDrawBuf != -2){
+ glDrawBuffer(GL_NONE);
+ context.boundDrawBuf = -2;
+ }
+ if (context.boundReadBuf != -2){
+ glReadBuffer(GL_NONE);
+ context.boundReadBuf = -2;
+ }
+ }else{
+ if (fb.isMultiTarget()){
+ if (fb.getNumColorBuffers() > maxMRTFBOAttachs)
+ throw new UnsupportedOperationException("Framebuffer has more"
+ + " targets than are supported"
+ + " on the system!");
+
+ if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()){
+ intBuf16.clear();
+ for (int i = 0; i < fb.getNumColorBuffers(); i++)
+ intBuf16.put( GL_COLOR_ATTACHMENT0_EXT + i );
+
+ intBuf16.flip();
+ glDrawBuffers(intBuf16);
+ context.boundDrawBuf = 100 + fb.getNumColorBuffers();
+ }
+ }else{
+ RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
+ // select this draw buffer
+ if (context.boundDrawBuf != rb.getSlot()){
+ glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ context.boundDrawBuf = rb.getSlot();
+ }
+ }
+ }
+
+ assert fb.getId() >= 0;
+ assert context.boundFBO == fb.getId();
+ lastFb = fb;
+ }
+
+ try {
+ checkFrameBufferError();
+ } catch (IllegalStateException ex){
+ logger.log(Level.SEVERE, "Problem FBO:\n{0}", fb);
+ throw ex;
+ }
+ }
+ */
+
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+ logger.warning("readFrameBuffer is not supported.");
+ }
+ /*
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf){
+ if (fb != null){
+ RenderBuffer rb = fb.getColorBuffer();
+ if (rb == null)
+ throw new IllegalArgumentException("Specified framebuffer" +
+ " does not have a colorbuffer");
+
+ setFrameBuffer(fb);
+ if (context.boundReadBuf != rb.getSlot()){
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ context.boundReadBuf = rb.getSlot();
+ }
+ }else{
+ setFrameBuffer(null);
+ }
+
+ glReadPixels(vpX, vpY, vpW, vpH, GL_RGBA GL_BGRA, GL_UNSIGNED_BYTE, byteBuf);
+ }
+ */
+
+ private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
+ logger.warning("deleteRenderBuffer is not supported.");
+ }
+ /*
+ private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb){
+ intBuf1.put(0, rb.getId());
+ glDeleteRenderbuffersEXT(intBuf1);
+ }
+ */
+
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ logger.warning("deleteFrameBuffer is not supported.");
+ }
+ /*
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ if (fb.getId() != -1){
+ if (context.boundFBO == fb.getId()){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ context.boundFBO = 0;
+ }
+
+ if (fb.getDepthBuffer() != null){
+ deleteRenderBuffer(fb, fb.getDepthBuffer());
+ }
+ if (fb.getColorBuffer() != null){
+ deleteRenderBuffer(fb, fb.getColorBuffer());
+ }
+
+ intBuf1.put(0, fb.getId());
+ glDeleteFramebuffersEXT(intBuf1);
+ fb.resetObject();
+
+ statistics.onDeleteFrameBuffer();
+ }
+ }
+ */
+
+ /*********************************************************************\
+ |* Textures *|
+ \*********************************************************************/
+ private int convertTextureType(Texture.Type type) {
+ switch (type) {
+ case TwoDimensional:
+ return GLES20.GL_TEXTURE_2D;
+ // case TwoDimensionalArray:
+ // return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT;
+// case ThreeDimensional:
+ // return GLES20.GL_TEXTURE_3D;
+ case CubeMap:
+ return GLES20.GL_TEXTURE_CUBE_MAP;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + type);
+ }
+ }
+
+ private int convertMagFilter(Texture.MagFilter filter) {
+ switch (filter) {
+ case Bilinear:
+ return GLES20.GL_LINEAR;
+ case Nearest:
+ return GLES20.GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown mag filter: " + filter);
+ }
+ }
+
+ private int convertMinFilter(Texture.MinFilter filter) {
+ switch (filter) {
+ case Trilinear:
+ return GLES20.GL_LINEAR_MIPMAP_LINEAR;
+ case BilinearNearestMipMap:
+ return GLES20.GL_LINEAR_MIPMAP_NEAREST;
+ case NearestLinearMipMap:
+ return GLES20.GL_NEAREST_MIPMAP_LINEAR;
+ case NearestNearestMipMap:
+ return GLES20.GL_NEAREST_MIPMAP_NEAREST;
+ case BilinearNoMipMaps:
+ return GLES20.GL_LINEAR;
+ case NearestNoMipMaps:
+ return GLES20.GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown min filter: " + filter);
+ }
+ }
+
+ private int convertWrapMode(Texture.WrapMode mode) {
+ switch (mode) {
+// case BorderClamp:
+// return GLES20.GL_CLAMP_TO_BORDER;
+// case Clamp:
+// return GLES20.GL_CLAMP;
+ case EdgeClamp:
+ return GLES20.GL_CLAMP_TO_EDGE;
+ case Repeat:
+ return GLES20.GL_REPEAT;
+ case MirroredRepeat:
+ return GLES20.GL_MIRRORED_REPEAT;
+ default:
+ throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
+ }
+ }
+
+ /**
+ * <code>setupTextureParams</code> sets the OpenGL context texture parameters
+ * @param tex the Texture to set the texture parameters from
+ */
+ private void setupTextureParams(Texture tex) {
+ int target = convertTextureType(tex.getType());
+
+ // filter things
+ int minFilter = convertMinFilter(tex.getMinFilter());
+ int magFilter = convertMagFilter(tex.getMagFilter());
+
+ if (verboseLogging) {
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MIN_FILTER, " + minFilter + ")");
+ }
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MIN_FILTER, minFilter);
+
+ if (verboseLogging) {
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MAG_FILTER, " + magFilter + ")");
+ }
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MAG_FILTER, magFilter);
+
+ /*
+ if (tex.getAnisotropicFilter() > 1){
+
+ if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
+ glTexParameterf(target,
+ EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT,
+ tex.getAnisotropicFilter());
+ }
+
+ }
+ */
+ // repeat modes
+
+ switch (tex.getType()) {
+ case ThreeDimensional:
+ case CubeMap: // cubemaps use 3D coords
+ // GL_TEXTURE_WRAP_R is not available in api 8
+ //GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
+ case TwoDimensional:
+ case TwoDimensionalArray:
+
+ if (verboseLogging) {
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_T, " + convertWrapMode(tex.getWrap(WrapAxis.T)));
+ }
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
+
+ // fall down here is intentional..
+// case OneDimensional:
+
+ if (verboseLogging) {
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_S, " + convertWrapMode(tex.getWrap(WrapAxis.S)));
+ }
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
+ }
+
+ // R to Texture compare mode
+/*
+ if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off){
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_MODE, GLES20.GL_COMPARE_R_TO_TEXTURE);
+ GLES20.glTexParameteri(target, GLES20.GL_DEPTH_TEXTURE_MODE, GLES20.GL_INTENSITY);
+ if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual){
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_GEQUAL);
+ }else{
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_LEQUAL);
+ }
+ }
+ */
+ }
+
+ /**
+ * <code>updateTexImageData</code> activates and binds the texture
+ * @param img
+ * @param type
+ * @param mips
+ */
+ public void updateTexImageData(Image img, Texture.Type type, boolean mips) {
+ int texId = img.getId();
+ if (texId == -1) {
+ // create texture
+ if (verboseLogging) {
+ logger.info("GLES20.glGenTexture(1, buffer)");
+ }
+
+ GLES20.glGenTextures(1, intBuf1);
+ texId = intBuf1.get(0);
+ img.setId(texId);
+ objManager.registerForCleanup(img);
+
+ statistics.onNewTexture();
+ }
+
+ // bind texture
+ int target = convertTextureType(type);
+ if (context.boundTextures[0] != img) {
+ if (context.boundTextureUnit != 0) {
+ if (verboseLogging) {
+ logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0)");
+ }
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ context.boundTextureUnit = 0;
+ }
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBindTexture(" + target + ", " + texId + ")");
+ }
+
+ GLES20.glBindTexture(target, texId);
+ context.boundTextures[0] = img;
+ }
+
+
+ if (target == GLES20.GL_TEXTURE_CUBE_MAP) {
+ // Upload a cube map / sky box
+ @SuppressWarnings("unchecked")
+ List<AndroidImageInfo> bmps = (List<AndroidImageInfo>) img.getEfficentData();
+ if (bmps != null) {
+ // Native android bitmap
+ if (bmps.size() != 6) {
+ throw new UnsupportedOperationException("Invalid texture: " + img
+ + "Cubemap textures must contain 6 data units.");
+ }
+ for (int i = 0; i < 6; i++) {
+ TextureUtil.uploadTextureBitmap(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, bmps.get(i).getBitmap(), false, powerOf2);
+ }
+ } else {
+ // Standard jme3 image data
+ List<ByteBuffer> data = img.getData();
+ if (data.size() != 6) {
+ logger.log(Level.WARNING, "Invalid texture: {0}\n"
+ + "Cubemap textures must contain 6 data units.", img);
+ return;
+ }
+ for (int i = 0; i < 6; i++) {
+ TextureUtil.uploadTexture(img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, false, powerOf2);
+ }
+ }
+ } else {
+ TextureUtil.uploadTexture(img, target, 0, 0, tdc, false, powerOf2);
+
+ if (verboseLogging) {
+ logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)");
+ }
+
+ if (!img.hasMipmaps() && mips) {
+ // No pregenerated mips available,
+ // generate from base level if required
+ if (verboseLogging) {
+ logger.info("GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D)");
+ }
+ GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
+ }
+ }
+
+ img.clearUpdateNeeded();
+ }
+
+ public void setTexture(int unit, Texture tex) {
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded()) {
+ /*
+ Bitmap bmp = (Bitmap)image.getEfficentData();
+ if (bmp != null)
+ {
+ // Check if the bitmap got recycled, can happen after wakeup/restart
+ if ( bmp.isRecycled() )
+ {
+ // We need to reload the bitmap
+ Texture textureReloaded = JmeSystem.newAssetManager().loadTexture((TextureKey)tex.getKey());
+ image.setEfficentData( textureReloaded.getImage().getEfficentData());
+ }
+ }
+ */
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels());
+ }
+
+ int texId = image.getId();
+ assert texId != -1;
+
+ if (texId == -1) {
+ logger.warning("error: texture image has -1 id");
+ }
+
+ Image[] textures = context.boundTextures;
+
+ int type = convertTextureType(tex.getType());
+ if (!context.textureIndexList.moveToNew(unit)) {
+// if (context.boundTextureUnit != unit){
+// glActiveTexture(GL_TEXTURE0 + unit);
+// context.boundTextureUnit = unit;
+// }
+// glEnable(type);
+ }
+
+ if (textures[unit] != image) {
+ if (context.boundTextureUnit != unit) {
+ if (verboseLogging) {
+ logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + " + unit + ")");
+ }
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBindTexture(" + type + ", " + texId + ")");
+ }
+
+ GLES20.glBindTexture(type, texId);
+ textures[unit] = image;
+
+ statistics.onTextureUse(tex.getImage(), true);
+ } else {
+ statistics.onTextureUse(tex.getImage(), false);
+ }
+
+ setupTextureParams(tex);
+ }
+
+ public void clearTextureUnits() {
+ IDList textureList = context.textureIndexList;
+ Image[] textures = context.boundTextures;
+ for (int i = 0; i < textureList.oldLen; i++) {
+ int idx = textureList.oldList[i];
+// if (context.boundTextureUnit != idx){
+// glActiveTexture(GL_TEXTURE0 + idx);
+// context.boundTextureUnit = idx;
+// }
+// glDisable(convertTextureType(textures[idx].getType()));
+ textures[idx] = null;
+ }
+ context.textureIndexList.copyNewToOld();
+ }
+
+ public void deleteImage(Image image) {
+ int texId = image.getId();
+ if (texId != -1) {
+ intBuf1.put(0, texId);
+ intBuf1.position(0).limit(1);
+
+ if (verboseLogging) {
+ logger.info("GLES20.glDeleteTexture(1, buffer)");
+ }
+
+ GLES20.glDeleteTextures(1, intBuf1);
+ image.resetObject();
+
+ statistics.onDeleteTexture();
+ }
+ }
+
+ /*********************************************************************\
+ |* Vertex Buffers and Attributes *|
+ \*********************************************************************/
+ private int convertUsage(Usage usage) {
+ switch (usage) {
+ case Static:
+ return GLES20.GL_STATIC_DRAW;
+ case Dynamic:
+ return GLES20.GL_DYNAMIC_DRAW;
+ case Stream:
+ return GLES20.GL_STREAM_DRAW;
+ default:
+ throw new RuntimeException("Unknown usage type.");
+ }
+ }
+
+ private int convertFormat(Format format) {
+ switch (format) {
+ case Byte:
+ return GLES20.GL_BYTE;
+ case UnsignedByte:
+ return GLES20.GL_UNSIGNED_BYTE;
+ case Short:
+ return GLES20.GL_SHORT;
+ case UnsignedShort:
+ return GLES20.GL_UNSIGNED_SHORT;
+ case Int:
+ return GLES20.GL_INT;
+ case UnsignedInt:
+ return GLES20.GL_UNSIGNED_INT;
+ /*
+ case Half:
+ return NVHalfFloat.GL_HALF_FLOAT_NV;
+ // return ARBHalfFloatVertex.GL_HALF_FLOAT;
+ */
+ case Float:
+ return GLES20.GL_FLOAT;
+// case Double:
+// return GLES20.GL_DOUBLE;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+
+ }
+ }
+
+ public void updateBufferData(VertexBuffer vb) {
+
+ if (verboseLogging) {
+ logger.info("updateBufferData(" + vb + ")");
+ }
+
+ int bufId = vb.getId();
+ boolean created = false;
+ if (bufId == -1) {
+ // create buffer
+
+ if (verboseLogging) {
+ logger.info("GLES20.glGenBuffers(" + 1 + ", buffer)");
+ }
+
+ GLES20.glGenBuffers(1, intBuf1);
+ bufId = intBuf1.get(0);
+ vb.setId(bufId);
+ objManager.registerForCleanup(vb);
+
+ created = true;
+ }
+
+ // bind buffer
+ int target;
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ target = GLES20.GL_ELEMENT_ARRAY_BUFFER;
+
+ if (verboseLogging) {
+ logger.info("vb.getBufferType() == VertexBuffer.Type.Index");
+ }
+
+ if (context.boundElementArrayVBO != bufId) {
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBindBuffer(" + target + ", " + bufId + ")");
+ }
+
+ GLES20.glBindBuffer(target, bufId);
+ context.boundElementArrayVBO = bufId;
+ }
+ } else {
+ if (verboseLogging) {
+ logger.info("vb.getBufferType() != VertexBuffer.Type.Index");
+ }
+
+ target = GLES20.GL_ARRAY_BUFFER;
+
+ if (context.boundArrayVBO != bufId) {
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBindBuffer(" + target + ", " + bufId + ")");
+ }
+
+ GLES20.glBindBuffer(target, bufId);
+ context.boundArrayVBO = bufId;
+ }
+ }
+
+ int usage = convertUsage(vb.getUsage());
+ vb.getData().clear();
+
+ if (created || vb.hasDataSizeChanged()) {
+ // upload data based on format
+ int size = vb.getData().capacity() * vb.getFormat().getComponentSize();
+
+ switch (vb.getFormat()) {
+ case Byte:
+ case UnsignedByte:
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+ }
+
+ GLES20.glBufferData(target, size, (ByteBuffer) vb.getData(), usage);
+ break;
+ // case Half:
+ case Short:
+ case UnsignedShort:
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+ }
+
+ GLES20.glBufferData(target, size, (ShortBuffer) vb.getData(), usage);
+ break;
+ case Int:
+ case UnsignedInt:
+
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+ }
+
+ GLES20.glBufferData(target, size, (IntBuffer) vb.getData(), usage);
+ break;
+ case Float:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+ }
+
+ GLES20.glBufferData(target, size, (FloatBuffer) vb.getData(), usage);
+ break;
+ case Double:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+ }
+
+ GLES20.glBufferData(target, size, (DoubleBuffer) vb.getData(), usage);
+ break;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+ }
+ } else {
+ int size = vb.getData().capacity() * vb.getFormat().getComponentSize();
+
+ switch (vb.getFormat()) {
+ case Byte:
+ case UnsignedByte:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+ }
+
+ GLES20.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData());
+ break;
+ case Short:
+ case UnsignedShort:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+ }
+
+ GLES20.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData());
+ break;
+ case Int:
+ case UnsignedInt:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+ }
+
+ GLES20.glBufferSubData(target, 0, size, (IntBuffer) vb.getData());
+ break;
+ case Float:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+ }
+
+ GLES20.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData());
+ break;
+ case Double:
+ if (verboseLogging) {
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+ }
+
+ GLES20.glBufferSubData(target, 0, size, (DoubleBuffer) vb.getData());
+ break;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+ }
+ }
+// }else{
+// if (created || vb.hasDataSizeChanged()){
+// glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage);
+// }
+//
+// ByteBuffer buf = glMapBuffer(target,
+// GL_WRITE_ONLY,
+// vb.getMappedData());
+//
+// if (buf != vb.getMappedData()){
+// buf = buf.order(ByteOrder.nativeOrder());
+// vb.setMappedData(buf);
+// }
+//
+// buf.clear();
+//
+// switch (vb.getFormat()){
+// case Byte:
+// case UnsignedByte:
+// buf.put( (ByteBuffer) vb.getData() );
+// break;
+// case Short:
+// case UnsignedShort:
+// buf.asShortBuffer().put( (ShortBuffer) vb.getData() );
+// break;
+// case Int:
+// case UnsignedInt:
+// buf.asIntBuffer().put( (IntBuffer) vb.getData() );
+// break;
+// case Float:
+// buf.asFloatBuffer().put( (FloatBuffer) vb.getData() );
+// break;
+// case Double:
+// break;
+// default:
+// throw new RuntimeException("Unknown buffer format.");
+// }
+//
+// glUnmapBuffer(target);
+// }
+
+ vb.clearUpdateNeeded();
+ }
+
+ public void deleteBuffer(VertexBuffer vb) {
+ int bufId = vb.getId();
+ if (bufId != -1) {
+ // delete buffer
+ intBuf1.put(0, bufId);
+ intBuf1.position(0).limit(1);
+ if (verboseLogging) {
+ logger.info("GLES20.glDeleteBuffers(1, buffer)");
+ }
+
+ GLES20.glDeleteBuffers(1, intBuf1);
+ vb.resetObject();
+ }
+ }
+
+ public void clearVertexAttribs() {
+ IDList attribList = context.attribIndexList;
+ for (int i = 0; i < attribList.oldLen; i++) {
+ int idx = attribList.oldList[i];
+
+ if (verboseLogging) {
+ logger.info("GLES20.glDisableVertexAttribArray(" + idx + ")");
+ }
+
+ GLES20.glDisableVertexAttribArray(idx);
+ context.boundAttribs[idx] = null;
+ }
+ context.attribIndexList.copyNewToOld();
+ }
+
+ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
+ if (verboseLogging) {
+ logger.info("setVertexAttrib(" + vb + ", " + idb + ")");
+ }
+
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+ }
+
+ if (vb.isUpdateNeeded() && idb == null) {
+ updateBufferData(vb);
+ }
+
+ int programId = context.boundShaderProgram;
+ if (programId > 0) {
+ Attribute attrib = boundShader.getAttribute(vb.getBufferType());
+ int loc = attrib.getLocation();
+ if (loc == -1) {
+
+ if (verboseLogging) {
+ logger.warning("location is invalid for attrib: [" + vb.getBufferType().name() + "]");
+ }
+
+ return; // not defined
+ }
+
+ if (loc == -2) {
+// stringBuf.setLength(0);
+// stringBuf.append("in").append(vb.getBufferType().name()).append('\0');
+// updateNameBuffer();
+
+ String attributeName = "in" + vb.getBufferType().name();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glGetAttribLocation(" + programId + ", " + attributeName + ")");
+ }
+
+ loc = GLES20.glGetAttribLocation(programId, attributeName);
+
+ // not really the name of it in the shader (inPosition\0) but
+ // the internal name of the enum (Position).
+ if (loc < 0) {
+ attrib.setLocation(-1);
+
+ if (verboseLogging) {
+ logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]");
+ }
+
+ return; // not available in shader.
+ } else {
+ attrib.setLocation(loc);
+ }
+ }
+
+ VertexBuffer[] attribs = context.boundAttribs;
+ if (!context.attribIndexList.moveToNew(loc)) {
+ if (verboseLogging) {
+ logger.info("GLES20.glEnableVertexAttribArray(" + loc + ")");
+ }
+
+ GLES20.glEnableVertexAttribArray(loc);
+ //System.out.println("Enabled ATTRIB IDX: "+loc);
+ }
+ if (attribs[loc] != vb) {
+ // NOTE: Use id from interleaved buffer if specified
+ int bufId = idb != null ? idb.getId() : vb.getId();
+ assert bufId != -1;
+
+ if (bufId == -1) {
+ logger.warning("invalid buffer id");
+ }
+
+ if (context.boundArrayVBO != bufId) {
+ if (verboseLogging) {
+ logger.info("GLES20.glBindBuffer(" + GLES20.GL_ARRAY_BUFFER + ", " + bufId + ")");
+ }
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufId);
+ context.boundArrayVBO = bufId;
+ }
+
+ vb.getData().clear();
+
+ if (verboseLogging) {
+ logger.info("GLES20.glVertexAttribPointer("
+ + "location=" + loc + ", "
+ + "numComponents=" + vb.getNumComponents() + ", "
+ + "format=" + vb.getFormat() + ", "
+ + "isNormalized=" + vb.isNormalized() + ", "
+ + "stride=" + vb.getStride() + ", "
+ + "data.capacity=" + vb.getData().capacity() + ")");
+ }
+
+ Android22Workaround.glVertexAttribPointer(loc,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ 0);
+
+ attribs[loc] = vb;
+ }
+ } else {
+ throw new IllegalStateException("Cannot render mesh without shader bound");
+ }
+ }
+
+ public void setVertexAttrib(VertexBuffer vb) {
+ setVertexAttrib(vb, null);
+ }
+
+ public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
+ /* if (count > 1){
+ ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0,
+ vertCount, count);
+ }else{*/
+ if (verboseLogging) {
+ logger.info("GLES20.glDrawArrays(" + vertCount + ")");
+ }
+
+ GLES20.glDrawArrays(convertElementMode(mode), 0, vertCount);
+ /*
+ }*/
+ }
+
+ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
+
+ if (verboseLogging) {
+ logger.info("drawTriangleList(" + count + ")");
+ }
+
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+ }
+
+ if (indexBuf.isUpdateNeeded()) {
+ if (verboseLogging) {
+ logger.info("updateBufferData for indexBuf.");
+ }
+ updateBufferData(indexBuf);
+ }
+
+ int bufId = indexBuf.getId();
+ assert bufId != -1;
+
+ if (bufId == -1) {
+ logger.info("invalid buffer id!");
+ }
+
+ if (context.boundElementArrayVBO != bufId) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, {0})", bufId);
+ }
+
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufId);
+ context.boundElementArrayVBO = bufId;
+ }
+
+ int vertCount = mesh.getVertexCount();
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+
+ Buffer indexData = indexBuf.getData();
+
+ if (mesh.getMode() == Mode.Hybrid) {
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+ int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++) {
+ if (i == stripStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ } else if (i == fanStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+
+ if (useInstancing) {
+ //ARBDrawInstanced.
+ throw new IllegalArgumentException("instancing is not supported.");
+ /*
+ GLES20.glDrawElementsInstancedARB(elMode,
+ elementLength,
+ fmt,
+ curOffset,
+ count);
+ */
+ } else {
+ indexBuf.getData().position(curOffset);
+ if (verboseLogging) {
+ logger.log(Level.INFO, "glDrawElements(): {0}, {1}", new Object[]{elementLength, curOffset});
+ }
+
+ GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
+ /*
+ glDrawRangeElements(elMode,
+ 0,
+ vertCount,
+ elementLength,
+ fmt,
+ curOffset);
+ */
+ }
+
+ curOffset += elementLength * elSize;
+ }
+ } else {
+ if (useInstancing) {
+ throw new IllegalArgumentException("instancing is not supported.");
+ //ARBDrawInstanced.
+/*
+ GLES20.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ 0,
+ count);
+ */
+ } else {
+ indexData.clear();
+
+ if (verboseLogging) {
+ logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount});
+ }
+
+ GLES11.glDrawElements(
+ convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ 0);
+ }
+ }
+ }
+
+ /*********************************************************************\
+ |* Render Calls *|
+ \*********************************************************************/
+ public int convertElementMode(Mesh.Mode mode) {
+ switch (mode) {
+ case Points:
+ return GLES20.GL_POINTS;
+ case Lines:
+ return GLES20.GL_LINES;
+ case LineLoop:
+ return GLES20.GL_LINE_LOOP;
+ case LineStrip:
+ return GLES20.GL_LINE_STRIP;
+ case Triangles:
+ return GLES20.GL_TRIANGLES;
+ case TriangleFan:
+ return GLES20.GL_TRIANGLE_FAN;
+ case TriangleStrip:
+ return GLES20.GL_TRIANGLE_STRIP;
+ default:
+ throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
+ }
+ }
+
+ public void updateVertexArray(Mesh mesh) {
+ logger.log(Level.INFO, "updateVertexArray({0})", mesh);
+ int id = mesh.getId();
+ /*
+ if (id == -1){
+ IntBuffer temp = intBuf1;
+ // ARBVertexArrayObject.glGenVertexArrays(temp);
+ GLES20.glGenVertexArrays(temp);
+ id = temp.get(0);
+ mesh.setId(id);
+ }
+
+ if (context.boundVertexArray != id){
+ // ARBVertexArrayObject.glBindVertexArray(id);
+ GLES20.glBindVertexArray(id);
+ context.boundVertexArray = id;
+ }
+ */
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()) {
+ updateBufferData(interleavedData);
+ }
+
+
+ for (VertexBuffer vb : mesh.getBufferList().getArray()){
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
+
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttrib(vb);
+ } else {
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+ }
+
+ /**
+ * renderMeshVertexArray renders a mesh using vertex arrays
+ * @param mesh
+ * @param lod
+ * @param count
+ */
+ private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
+ if (verboseLogging) {
+ logger.info("renderMeshVertexArray");
+ }
+
+ // IntMap<VertexBuffer> buffers = mesh.getBuffers();
+ for (VertexBuffer vb : mesh.getBufferList().getArray()){
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
+
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttrib_Array(vb);
+ } else {
+ // interleaved
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ setVertexAttrib_Array(vb, interleavedData);
+ }
+ }
+
+ VertexBuffer indices = null;
+ if (mesh.getNumLodLevels() > 0) {
+ indices = mesh.getLodLevel(lod);
+ } else {
+ indices = mesh.getBuffer(Type.Index);//buffers.get(Type.Index.ordinal());
+ }
+ if (indices != null) {
+ drawTriangleList_Array(indices, mesh, count);
+ } else {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glDrawArrays({0}, {1}, {2})",
+ new Object[]{mesh.getMode(), 0, mesh.getVertexCount()});
+ }
+
+ GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ private void renderMeshDefault(Mesh mesh, int lod, int count) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "renderMeshDefault({0}, {1}, {2})",
+ new Object[]{mesh, lod, count});
+ }
+ VertexBuffer indices = null;
+
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()) {
+ updateBufferData(interleavedData);
+ }
+
+ //IntMap<VertexBuffer> buffers = mesh.getBuffers(); ;
+ if (mesh.getNumLodLevels() > 0) {
+ indices = mesh.getLodLevel(lod);
+ } else {
+ indices = mesh.getBuffer(Type.Index);// buffers.get(Type.Index.ordinal());
+ }
+ for (VertexBuffer vb : mesh.getBufferList().getArray()){
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
+
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttrib(vb);
+ } else {
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+ if (indices != null) {
+ drawTriangleList(indices, mesh, count);
+ } else {
+// throw new UnsupportedOperationException("Cannot render without index buffer");
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glDrawArrays({0}, 0, {1})",
+ new Object[]{convertElementMode(mesh.getMode()), mesh.getVertexCount()});
+ }
+
+ GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ public void renderMesh(Mesh mesh, int lod, int count) {
+ if (context.pointSize != mesh.getPointSize()) {
+
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES10.glPointSize({0})", mesh.getPointSize());
+ }
+
+ GLES10.glPointSize(mesh.getPointSize());
+ context.pointSize = mesh.getPointSize();
+ }
+ if (context.lineWidth != mesh.getLineWidth()) {
+
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glLineWidth({0})", mesh.getLineWidth());
+ }
+
+ GLES20.glLineWidth(mesh.getLineWidth());
+ context.lineWidth = mesh.getLineWidth();
+ }
+
+ statistics.onMeshDrawn(mesh, lod);
+// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
+// renderMeshVertexArray(mesh, lod, count);
+// }else{
+
+ if (useVBO) {
+ if (verboseLogging) {
+ logger.info("RENDERING A MESH USING VertexBufferObject");
+ }
+
+ renderMeshDefault(mesh, lod, count);
+ } else {
+ if (verboseLogging) {
+ logger.info("RENDERING A MESH USING VertexArray");
+ }
+
+ renderMeshVertexArray(mesh, lod, count);
+ }
+
+// }
+ }
+
+ /**
+ * drawTriangleList_Array uses Vertex Array
+ * @param indexBuf
+ * @param mesh
+ * @param count
+ */
+ public void drawTriangleList_Array(VertexBuffer indexBuf, Mesh mesh, int count) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "drawTriangleList_Array(Count = {0})", count);
+ }
+
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+ }
+
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+ if (useInstancing) {
+ throw new IllegalArgumentException("Caps.MeshInstancing is not supported.");
+ }
+
+ int vertCount = mesh.getVertexCount();
+ Buffer indexData = indexBuf.getData();
+ indexData.clear();
+
+ if (mesh.getMode() == Mode.Hybrid) {
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+ int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++) {
+ if (i == stripStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ } else if (i == fanStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+
+ indexBuf.getData().position(curOffset);
+ if (verboseLogging) {
+ logger.log(Level.INFO, "glDrawElements(): {0}, {1}", new Object[]{elementLength, curOffset});
+ }
+
+ GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
+
+ curOffset += elementLength * elSize;
+ }
+ } else {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount});
+ }
+
+ GLES20.glDrawElements(
+ convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ indexBuf.getData());
+ }
+ }
+
+ /**
+ * setVertexAttrib_Array uses Vertex Array
+ * @param vb
+ * @param idb
+ */
+ public void setVertexAttrib_Array(VertexBuffer vb, VertexBuffer idb) {
+ if (verboseLogging) {
+ logger.log(Level.INFO, "setVertexAttrib_Array({0}, {1})", new Object[]{vb, idb});
+ }
+
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+ }
+
+ // Get shader
+ int programId = context.boundShaderProgram;
+ if (programId > 0) {
+ VertexBuffer[] attribs = context.boundAttribs;
+
+ Attribute attrib = boundShader.getAttribute(vb.getBufferType());
+ int loc = attrib.getLocation();
+ if (loc == -1) {
+ //throw new IllegalArgumentException("Location is invalid for attrib: [" + vb.getBufferType().name() + "]");
+ if (verboseLogging) {
+ logger.log(Level.WARNING, "attribute is invalid in shader: [{0}]", vb.getBufferType().name());
+ }
+ return;
+ } else if (loc == -2) {
+ String attributeName = "in" + vb.getBufferType().name();
+
+ if (verboseLogging) {
+ logger.log(Level.INFO, "GLES20.glGetAttribLocation({0}, {1})", new Object[]{programId, attributeName});
+ }
+
+ loc = GLES20.glGetAttribLocation(programId, attributeName);
+ if (loc < 0) {
+ attrib.setLocation(-1);
+ if (verboseLogging) {
+ logger.log(Level.WARNING, "attribute is invalid in shader: [{0}]", vb.getBufferType().name());
+ }
+ return; // not available in shader.
+ } else {
+ attrib.setLocation(loc);
+ }
+
+ } // if (loc == -2)
+
+ if ((attribs[loc] != vb) || vb.isUpdateNeeded()) {
+ // NOTE: Use data from interleaved buffer if specified
+ VertexBuffer avb = idb != null ? idb : vb;
+ avb.getData().clear();
+ avb.getData().position(vb.getOffset());
+
+ if (verboseLogging) {
+ logger.log(Level.INFO,
+ "GLES20.glVertexAttribPointer(" +
+ "location={0}, " +
+ "numComponents={1}, " +
+ "format={2}, " +
+ "isNormalized={3}, " +
+ "stride={4}, " +
+ "data.capacity={5})",
+ new Object[]{loc, vb.getNumComponents(),
+ vb.getFormat(),
+ vb.isNormalized(),
+ vb.getStride(),
+ avb.getData().capacity()});
+ }
+
+
+ // Upload attribute data
+ GLES20.glVertexAttribPointer(loc,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ avb.getData());
+ checkGLError();
+
+ GLES20.glEnableVertexAttribArray(loc);
+
+ attribs[loc] = vb;
+ } // if (attribs[loc] != vb)
+ } else {
+ throw new IllegalStateException("Cannot render mesh without shader bound");
+ }
+ }
+
+ /**
+ * setVertexAttrib_Array uses Vertex Array
+ * @param vb
+ */
+ public void setVertexAttrib_Array(VertexBuffer vb) {
+ setVertexAttrib_Array(vb, null);
+ }
+
+ public void setAlphaToCoverage(boolean value) {
+ if (value) {
+ GLES20.glEnable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE);
+ } else {
+ GLES20.glDisable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE);
+ }
+ }
+
+ @Override
+ public void invalidateState() {
+ context.reset();
+ boundShader = null;
+ lastFb = null;
+ }
+}
diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
new file mode 100644
index 0000000..53b96b4
--- /dev/null
+++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
@@ -0,0 +1,297 @@
+package com.jme3.renderer.android;
+
+import android.graphics.Bitmap;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import com.jme3.asset.AndroidImageInfo;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.nio.ByteBuffer;
+import javax.microedition.khronos.opengles.GL10;
+
+public class TextureUtil {
+
+ public static int convertTextureFormat(Format fmt){
+ switch (fmt){
+ case Alpha16:
+ case Alpha8:
+ return GL10.GL_ALPHA;
+ case Luminance8Alpha8:
+ case Luminance16Alpha16:
+ return GL10.GL_LUMINANCE_ALPHA;
+ case Luminance8:
+ case Luminance16:
+ return GL10.GL_LUMINANCE;
+ case RGB10:
+ case RGB16:
+ case BGR8:
+ case RGB8:
+ case RGB565:
+ return GL10.GL_RGB;
+ case RGB5A1:
+ case RGBA16:
+ case RGBA8:
+ return GL10.GL_RGBA;
+
+ case Depth:
+ return GLES20.GL_DEPTH_COMPONENT;
+ case Depth16:
+ return GLES20.GL_DEPTH_COMPONENT16;
+ case Depth24:
+ case Depth32:
+ case Depth32F:
+ throw new UnsupportedOperationException("Unsupported depth format: " + fmt);
+
+ case DXT1A:
+ throw new UnsupportedOperationException("Unsupported format: " + fmt);
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: " + fmt);
+ }
+ }
+
+ private static void buildMipmap(Bitmap bitmap) {
+ int level = 0;
+ int height = bitmap.getHeight();
+ int width = bitmap.getWidth();
+
+ while (height >= 1 || width >= 1) {
+ //First of all, generate the texture from our bitmap and set it to the according level
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
+
+ if (height == 1 || width == 1) {
+ break;
+ }
+
+ //Increase the mipmap level
+ level++;
+
+ height /= 2;
+ width /= 2;
+ Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
+
+ bitmap.recycle();
+ bitmap = bitmap2;
+ }
+ }
+
+ /**
+ * <code>uploadTextureBitmap</code> uploads a native android bitmap
+ * @param target
+ * @param bitmap
+ * @param generateMips
+ * @param powerOf2
+ */
+ public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2)
+ {
+ if (!powerOf2)
+ {
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height))
+ {
+ // scale to power of two
+ width = FastMath.nearestPowerOfTwo(width);
+ height = FastMath.nearestPowerOfTwo(height);
+ Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
+ bitmap.recycle();
+ bitmap = bitmap2;
+ }
+ }
+
+ if (generateMips)
+ {
+ buildMipmap(bitmap);
+ }
+ else
+ {
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ //bitmap.recycle();
+ }
+ }
+
+ public static void uploadTexture(
+ Image img,
+ int target,
+ int index,
+ int border,
+ boolean tdc,
+ boolean generateMips,
+ boolean powerOf2){
+
+ if (img.getEfficentData() instanceof AndroidImageInfo){
+ // If image was loaded from asset manager, use fast path
+ AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
+ uploadTextureBitmap(target, imageInfo.getBitmap(), generateMips, powerOf2);
+ return;
+ }
+
+ // Otherwise upload image directly.
+ // Prefer to only use power of 2 textures here to avoid errors.
+
+ Image.Format fmt = img.getFormat();
+ ByteBuffer data;
+ if (index >= 0 || img.getData() != null && img.getData().size() > 0){
+ data = img.getData(index);
+ }else{
+ data = null;
+ }
+
+ int width = img.getWidth();
+ int height = img.getHeight();
+ int depth = img.getDepth();
+
+ boolean compress = false;
+ int internalFormat = -1;
+ int format = -1;
+ int dataType = -1;
+
+ switch (fmt){
+ case Alpha16:
+ case Alpha8:
+ format = GLES20.GL_ALPHA;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance8:
+ format = GLES20.GL_LUMINANCE;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance8Alpha8:
+ format = GLES20.GL_LUMINANCE_ALPHA;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16Alpha16:
+ format = GLES20.GL_LUMINANCE_ALPHA;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16:
+ format = GLES20.GL_LUMINANCE;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case RGB565:
+ format = GLES20.GL_RGB;
+ internalFormat = GLES20.GL_RGB565;
+ dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case ARGB4444:
+ format = GLES20.GL_RGBA;
+ dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case RGB10:
+ format = GLES20.GL_RGB;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case RGB16:
+ format = GLES20.GL_RGB;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case RGB5A1:
+ format = GLES20.GL_RGBA;
+ internalFormat = GLES20.GL_RGB5_A1;
+ dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1;
+ break;
+ case RGB8:
+ format = GLES20.GL_RGB;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case BGR8:
+ format = GLES20.GL_RGB;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case RGBA16:
+ format = GLES20.GL_RGBA;
+ internalFormat = GLES20.GL_RGBA4;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case RGBA8:
+ format = GLES20.GL_RGBA;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case DXT1A:
+ format = GLES20.GL_COMPRESSED_TEXTURE_FORMATS;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ case Depth:
+ format = GLES20.GL_DEPTH_COMPONENT;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case Depth16:
+ format = GLES20.GL_DEPTH_COMPONENT;
+ internalFormat = GLES20.GL_DEPTH_COMPONENT16;
+ dataType = GLES20.GL_UNSIGNED_BYTE;
+ break;
+ case Depth24:
+ case Depth32:
+ case Depth32F:
+ throw new UnsupportedOperationException("Unsupported depth format: " + fmt);
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: " + fmt);
+ }
+
+ if (internalFormat == -1)
+ {
+ internalFormat = format;
+ }
+
+ if (data != null)
+ GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
+
+ int[] mipSizes = img.getMipMapSizes();
+ int pos = 0;
+ if (mipSizes == null){
+ if (data != null)
+ mipSizes = new int[]{ data.capacity() };
+ else
+ mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 };
+ }
+
+ // XXX: might want to change that when support
+ // of more than paletted compressions is added..
+ if (compress){
+ data.clear();
+ GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
+ 1 - mipSizes.length,
+ format,
+ width,
+ height,
+ 0,
+ data.capacity(),
+ data);
+ return;
+ }
+
+ for (int i = 0; i < mipSizes.length; i++){
+ int mipWidth = Math.max(1, width >> i);
+ int mipHeight = Math.max(1, height >> i);
+ int mipDepth = Math.max(1, depth >> i);
+
+ if (data != null){
+ data.position(pos);
+ data.limit(pos + mipSizes[i]);
+ }
+
+ if (compress && data != null){
+ GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
+ i,
+ format,
+ mipWidth,
+ mipHeight,
+ 0,
+ data.remaining(),
+ data);
+ }else{
+ GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,
+ i,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ 0,
+ format,
+ dataType,
+ data);
+ }
+
+ pos += mipSizes[i];
+ }
+ }
+
+}
diff --git a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java
new file mode 100644
index 0000000..e55fa55
--- /dev/null
+++ b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java
@@ -0,0 +1,362 @@
+package com.jme3.system.android;
+
+import android.graphics.PixelFormat;
+import android.opengl.GLSurfaceView.EGLConfigChooser;
+import java.util.logging.Logger;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * AndroidConfigChooser is used to determine the best suited EGL Config
+ * @author larynx
+ *
+ */
+public class AndroidConfigChooser implements EGLConfigChooser
+{
+ private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
+
+ protected int clientOpenGLESVersion = 0;
+ protected EGLConfig bestConfig = null;
+ protected EGLConfig fastestConfig = null;
+ protected EGLConfig choosenConfig = null;
+ protected ConfigType type;
+ protected int pixelFormat;
+
+ protected boolean verbose = false;
+
+ private final static int EGL_OPENGL_ES2_BIT = 4;
+
+ public enum ConfigType
+ {
+ /**
+ * RGB565, 0 alpha, 16 depth, 0 stencil
+ */
+ FASTEST,
+ /**
+ * RGB???, 0 alpha, >=16 depth, 0 stencil
+ */
+ BEST,
+ /**
+ * Turn off config chooser and use hardcoded
+ * setEGLContextClientVersion(2);
+ * setEGLConfigChooser(5, 6, 5, 0, 16, 0);
+ */
+ LEGACY
+ }
+
+ public AndroidConfigChooser(ConfigType type, boolean verbose)
+ {
+ this.type = type;
+ this.verbose = verbose;
+ }
+
+ /**
+ * Gets called by the GLSurfaceView class to return the best config
+ */
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
+ {
+ logger.info("GLSurfaceView asks for egl config, returning: ");
+ logEGLConfig(choosenConfig, display, egl);
+ return choosenConfig;
+ }
+
+ /**
+ * findConfig is used to locate the best config and init the chooser with
+ * @param egl
+ * @param display
+ * @return true if successfull, false if no config was found
+ */
+ public boolean findConfig(EGL10 egl, EGLDisplay display)
+ {
+
+ if (type == ConfigType.BEST)
+ {
+ ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0);
+ choosenConfig = compChooser.chooseConfig(egl, display);
+
+ if (choosenConfig == null)
+ {
+ compChooser = new ComponentSizeChooser(8, 8, 8, 0, 32, 0);
+ choosenConfig = compChooser.chooseConfig(egl, display);
+ if (choosenConfig == null)
+ {
+ compChooser = new ComponentSizeChooser(8, 8, 8, 8, 16, 0);
+ choosenConfig = compChooser.chooseConfig(egl, display);
+ if (choosenConfig == null)
+ {
+ compChooser = new ComponentSizeChooser(8, 8, 8, 0, 16, 0);
+ choosenConfig = compChooser.chooseConfig(egl, display);
+ }
+ }
+ }
+
+ logger.info("JME3 using best EGL configuration available here: ");
+ }
+ else
+ {
+ ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0);
+ choosenConfig = compChooser.chooseConfig(egl, display);
+ logger.info("JME3 using fastest EGL configuration available here: ");
+ }
+
+ if (choosenConfig != null)
+ {
+ logger.info("JME3 using choosen config: ");
+ logEGLConfig(choosenConfig, display, egl);
+ pixelFormat = getPixelFormat(choosenConfig, display, egl);
+ clientOpenGLESVersion = getOpenGLVersion(choosenConfig, display, egl);
+ return true;
+ }
+ else
+ {
+ logger.severe("###ERROR### Unable to get a valid OpenGL ES 2.0 config, nether Fastest nor Best found! Bug. Please report this.");
+ clientOpenGLESVersion = 1;
+ pixelFormat = PixelFormat.UNKNOWN;
+ return false;
+ }
+ }
+
+
+ private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl)
+ {
+ int[] value = new int[1];
+ int result = PixelFormat.RGB_565;
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value);
+ if (value[0] == 8)
+ {
+ result = PixelFormat.RGBA_8888;
+ /*
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value);
+ if (value[0] == 8)
+ {
+ result = PixelFormat.RGBA_8888;
+ }
+ else
+ {
+ result = PixelFormat.RGB_888;
+ }*/
+ }
+
+ if (verbose)
+ {
+ logger.info("Using PixelFormat " + result);
+ }
+
+ //return result; TODO Test pixelformat
+ return PixelFormat.TRANSPARENT;
+ }
+
+ private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl)
+ {
+ int[] value = new int[1];
+ int result = 1;
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);
+ // Check if conf is OpenGL ES 2.0
+ if ((value[0] & EGL_OPENGL_ES2_BIT) != 0)
+ {
+ result = 2;
+ }
+
+ return result;
+ }
+
+ /**
+ * log output with egl config details
+ * @param conf
+ * @param display
+ * @param egl
+ */
+ public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl)
+ {
+ int[] value = new int[1];
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value);
+ logger.info(String.format("EGL_RED_SIZE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_GREEN_SIZE, value);
+ logger.info(String.format("EGL_GREEN_SIZE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_BLUE_SIZE, value);
+ logger.info(String.format("EGL_BLUE_SIZE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value);
+ logger.info(String.format("EGL_ALPHA_SIZE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_DEPTH_SIZE, value);
+ logger.info(String.format("EGL_DEPTH_SIZE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_STENCIL_SIZE, value);
+ logger.info(String.format("EGL_STENCIL_SIZE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);
+ logger.info(String.format("EGL_RENDERABLE_TYPE = %d", value[0] ) );
+
+ egl.eglGetConfigAttrib(display, conf, EGL10.EGL_SURFACE_TYPE, value);
+ logger.info(String.format("EGL_SURFACE_TYPE = %d", value[0] ) );
+ }
+
+ public int getClientOpenGLESVersion()
+ {
+ return clientOpenGLESVersion;
+ }
+
+ public void setClientOpenGLESVersion(int clientOpenGLESVersion)
+ {
+ this.clientOpenGLESVersion = clientOpenGLESVersion;
+ }
+
+ public int getPixelFormat()
+ {
+ return pixelFormat;
+ }
+
+
+
+ private abstract class BaseConfigChooser implements EGLConfigChooser
+ {
+ private boolean bClientOpenGLESVersionSet;
+
+ public BaseConfigChooser(int[] configSpec)
+ {
+ bClientOpenGLESVersionSet = false;
+ mConfigSpec = filterConfigSpec(configSpec);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
+ {
+ int[] num_config = new int[1];
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0)
+ {
+ //throw new IllegalArgumentException("No configs match configSpec");
+
+ return null;
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
+ EGLConfig config = chooseConfig(egl, display, configs);
+ //if (config == null) {
+ // throw new IllegalArgumentException("No config chosen");
+ //}
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+
+ private int[] filterConfigSpec(int[] configSpec)
+ {
+ if (bClientOpenGLESVersionSet == true) {
+ return configSpec;
+ }
+ /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+ * And we know the configSpec is well formed.
+ */
+ int len = configSpec.length;
+ int[] newConfigSpec = new int[len + 2];
+ System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
+ newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+ newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ newConfigSpec[len+1] = EGL10.EGL_NONE;
+
+ bClientOpenGLESVersionSet = true;
+
+ return newConfigSpec;
+ }
+ }
+
+ /**
+ * Choose a configuration with exactly the specified r,g,b,a sizes,
+ * and at least the specified depth and stencil sizes.
+ */
+ private class ComponentSizeChooser extends BaseConfigChooser
+ {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize)
+ {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs)
+ {
+ for (EGLConfig config : configs)
+ {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if ((d >= mDepthSize) && (s >= mStencilSize))
+ {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ if ((r == mRedSize) && (g == mGreenSize)
+ && (b == mBlueSize) && (a == mAlphaSize)) {
+ return config;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue)
+ {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue))
+ {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+
+
+
+}
diff --git a/engine/src/android/com/jme3/system/android/AndroidTimer.java b/engine/src/android/com/jme3/system/android/AndroidTimer.java
new file mode 100644
index 0000000..4cabc33
--- /dev/null
+++ b/engine/src/android/com/jme3/system/android/AndroidTimer.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.android;
+
+import com.jme3.system.Timer;
+
+/**
+ * <code>AndroidTimer</code> is a System.nanoTime implementation of <code>Timer</code>.
+ */
+public class AndroidTimer extends Timer {
+
+ //private static final long TIMER_RESOLUTION = 1000L;
+ //private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L;
+ private static final long TIMER_RESOLUTION = 1000000000L;
+ private static final float INVERSE_TIMER_RESOLUTION = 1f/1000000000L;
+
+ private long startTime;
+ private long previousTime;
+ private float tpf;
+ private float fps;
+
+ public AndroidTimer() {
+ //startTime = System.currentTimeMillis();
+ startTime = System.nanoTime();
+ }
+
+ /**
+ * Returns the time in seconds. The timer starts
+ * at 0.0 seconds.
+ *
+ * @return the current time in seconds
+ */
+ @Override
+ public float getTimeInSeconds() {
+ return getTime() * INVERSE_TIMER_RESOLUTION;
+ }
+
+ public long getTime() {
+ //return System.currentTimeMillis() - startTime;
+ return System.nanoTime() - startTime;
+ }
+
+ public long getResolution() {
+ return TIMER_RESOLUTION;
+ }
+
+ public float getFrameRate() {
+ return fps;
+ }
+
+ public float getTimePerFrame() {
+ return tpf;
+ }
+
+ public void update() {
+ tpf = (getTime() - previousTime) * (1.0f / TIMER_RESOLUTION);
+ fps = 1.0f / tpf;
+ previousTime = getTime();
+ }
+
+ public void reset() {
+ //startTime = System.currentTimeMillis();
+ startTime = System.nanoTime();
+ previousTime = getTime();
+ }
+}
diff --git a/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java b/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
new file mode 100644
index 0000000..6550101
--- /dev/null
+++ b/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
@@ -0,0 +1,134 @@
+package com.jme3.system.android;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Environment;
+import com.jme3.asset.AndroidAssetManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.android.AndroidAudioRenderer;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystemDelegate;
+import com.jme3.system.Platform;
+import com.jme3.util.AndroidLogHandler;
+import com.jme3.util.JmeFormatter;
+import java.io.File;
+import java.net.URL;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+
+public class JmeAndroidSystem extends JmeSystemDelegate {
+
+ private static Resources res;
+ private static Activity activity;
+
+ @Override
+ public AssetManager newAssetManager(URL configFile) {
+ logger.log(Level.INFO, "newAssetManager({0})", configFile);
+ return new AndroidAssetManager(configFile);
+ }
+
+ @Override
+ public AssetManager newAssetManager() {
+ logger.log(Level.INFO, "newAssetManager()");
+ return new AndroidAssetManager(null);
+ }
+
+ @Override
+ public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) {
+ return true;
+ }
+
+ @Override
+ public JmeContext newContext(AppSettings settings, Type contextType) {
+ initialize(settings);
+ return new OGLESContext();
+ }
+
+ @Override
+ public AudioRenderer newAudioRenderer(AppSettings settings) {
+ return new AndroidAudioRenderer(activity);
+ }
+
+ @Override
+ public void initialize(AppSettings settings) {
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+ try {
+ JmeFormatter formatter = new JmeFormatter();
+
+ Handler consoleHandler = new AndroidLogHandler();
+ consoleHandler.setFormatter(formatter);
+ } catch (SecurityException ex) {
+ logger.log(Level.SEVERE, "Security error in creating log file", ex);
+ }
+ logger.log(Level.INFO, "Running on {0}", getFullName());
+ }
+
+ @Override
+ public Platform getPlatform() {
+ String arch = System.getProperty("os.arch").toLowerCase();
+ if (arch.contains("arm")) {
+ if (arch.contains("v5")) {
+ return Platform.Android_ARM5;
+ } else if (arch.contains("v6")) {
+ return Platform.Android_ARM6;
+ } else if (arch.contains("v7")) {
+ return Platform.Android_ARM7;
+ } else {
+ return Platform.Android_ARM5; // unknown ARM
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported Android Platform");
+ }
+ }
+
+ @Override
+ public synchronized File getStorageFolder() {
+ //http://developer.android.com/reference/android/content/Context.html#getExternalFilesDir
+ //http://developer.android.com/guide/topics/data/data-storage.html
+
+ boolean mExternalStorageWriteable = false;
+ String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ mExternalStorageWriteable = true;
+ } else {
+ mExternalStorageWriteable = false;
+ }
+
+ if (mExternalStorageWriteable) {
+ //getExternalFilesDir automatically creates the directory if necessary.
+ //directory structure should be: /mnt/sdcard/Android/data/<packagename>/files
+ //when created this way, the directory is automatically removed by the Android
+ // system when the app is uninstalled
+ storageFolder = activity.getApplicationContext().getExternalFilesDir(null);
+ logger.log(Level.INFO, "Storage Folder Path: {0}", storageFolder.getAbsolutePath());
+
+ return storageFolder;
+ } else {
+ return null;
+ }
+
+ }
+
+ public static void setResources(Resources res) {
+ JmeAndroidSystem.res = res;
+ }
+
+ public static Resources getResources() {
+ return res;
+ }
+
+ public static void setActivity(Activity activity) {
+ JmeAndroidSystem.activity = activity;
+ }
+
+ public static Activity getActivity() {
+ return activity;
+ }
+}
diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java
new file mode 100644
index 0000000..668b68d
--- /dev/null
+++ b/engine/src/android/com/jme3/system/android/OGLESContext.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.view.SurfaceHolder;
+import com.jme3.app.AndroidHarness;
+import com.jme3.app.Application;
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.android.AndroidInput;
+import com.jme3.input.controls.TouchTrigger;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
+import com.jme3.renderer.android.OGLESShaderRenderer;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.SystemListener;
+import com.jme3.system.Timer;
+import com.jme3.system.android.AndroidConfigChooser.ConfigType;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
+
+ private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());
+ protected final AtomicBoolean created = new AtomicBoolean(false);
+ protected final AtomicBoolean renderable = new AtomicBoolean(false);
+ protected final AtomicBoolean needClose = new AtomicBoolean(false);
+ protected final AppSettings settings = new AppSettings(true);
+
+ /*
+ * >= OpenGL ES 2.0 (Android 2.2+)
+ */
+ protected OGLESShaderRenderer renderer;
+ protected Timer timer;
+ protected SystemListener listener;
+ protected boolean autoFlush = true;
+ protected AndroidInput view;
+ private boolean firstDrawFrame = true;
+
+ //protected int minFrameDuration = 1000 / frameRate; // Set a max FPS of 33
+ protected int minFrameDuration = 0; // No FPS cap
+ /**
+ * EGL_RENDERABLE_TYPE: EGL_OPENGL_ES_BIT = OpenGL ES 1.0 |
+ * EGL_OPENGL_ES2_BIT = OpenGL ES 2.0
+ */
+ protected int clientOpenGLESVersion = 1;
+ protected boolean verboseLogging = false;
+ final private String ESCAPE_EVENT = "TouchEscape";
+
+ public OGLESContext() {
+ }
+
+ @Override
+ public Type getType() {
+ return Type.Display;
+ }
+
+ /**
+ * <code>createView</code>
+ *
+ * @param activity The Android activity which is parent for the
+ * GLSurfaceView
+ * @return GLSurfaceView The newly created view
+ */
+ public GLSurfaceView createView(Activity activity) {
+ return createView(new AndroidInput(activity));
+ }
+
+ /**
+ * <code>createView</code>
+ *
+ * @param view The Android input which will be used as the GLSurfaceView for
+ * this context
+ * @return GLSurfaceView The newly created view
+ */
+ public GLSurfaceView createView(AndroidInput view) {
+ return createView(view, ConfigType.FASTEST, false);
+ }
+
+ /**
+ * <code>createView</code> initializes the GLSurfaceView
+ *
+ * @param view The Android input which will be used as the GLSurfaceView for
+ * this context
+ * @param configType ConfigType.FASTEST (Default) | ConfigType.LEGACY |
+ * ConfigType.BEST
+ * @param eglConfigVerboseLogging if true show all found configs
+ * @return GLSurfaceView The newly created view
+ */
+ public GLSurfaceView createView(AndroidInput view, ConfigType configType, boolean eglConfigVerboseLogging) {
+ // Start to set up the view
+ this.view = view;
+ verboseLogging = eglConfigVerboseLogging;
+
+ if (configType == ConfigType.LEGACY) {
+ // Hardcoded egl setup
+ clientOpenGLESVersion = 2;
+ view.setEGLContextClientVersion(2);
+ //RGB565, Depth16
+ view.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
+ logger.info("ConfigType.LEGACY using RGB565");
+ } else {
+ EGL10 egl = (EGL10) EGLContext.getEGL();
+ EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ int[] version = new int[2];
+ if (egl.eglInitialize(display, version) == true) {
+ logger.info("Display EGL Version: " + version[0] + "." + version[1]);
+ }
+
+ try {
+ // Create a config chooser
+ AndroidConfigChooser configChooser = new AndroidConfigChooser(configType, eglConfigVerboseLogging);
+ // Init chooser
+ if (!configChooser.findConfig(egl, display)) {
+ listener.handleError("Unable to find suitable EGL config", null);
+ return null;
+ }
+
+ clientOpenGLESVersion = configChooser.getClientOpenGLESVersion();
+ if (clientOpenGLESVersion < 2) {
+ listener.handleError("OpenGL ES 2.0 is not supported on this device", null);
+ return null;
+ }
+
+ // Requesting client version from GLSurfaceView which is extended by
+ // AndroidInput.
+ view.setEGLContextClientVersion(clientOpenGLESVersion);
+ view.setEGLConfigChooser(configChooser);
+ view.getHolder().setFormat(configChooser.getPixelFormat());
+ } finally {
+ if (display != null) {
+ egl.eglTerminate(display);
+ }
+ }
+ }
+
+ view.setFocusableInTouchMode(true);
+ view.setFocusable(true);
+ view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ view.setRenderer(this);
+
+ return view;
+ }
+
+ // renderer:initialize
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig cfg) {
+
+ if (created.get() && renderer != null) {
+ renderer.resetGLObjects();
+ } else {
+ if (!created.get()) {
+ logger.info("GL Surface created, doing JME3 init");
+ initInThread();
+ } else {
+ logger.warning("GL Surface already created");
+ }
+ }
+ }
+
+ protected void initInThread() {
+ created.set(true);
+
+ logger.info("OGLESContext create");
+ logger.info("Running on thread: " + Thread.currentThread().getName());
+
+ final Context ctx = this.view.getContext();
+
+ // Setup unhandled Exception Handler
+ if (ctx instanceof AndroidHarness) {
+ Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ ((AndroidHarness) ctx).handleError("Exception thrown in " + thread.toString(), thrown);
+ }
+ });
+ } else {
+ Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Exception thrown in " + thread.toString(), thrown);
+ }
+ });
+ }
+
+ if (clientOpenGLESVersion < 2) {
+ throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device");
+ }
+
+ timer = new AndroidTimer();
+ renderer = new OGLESShaderRenderer();
+
+ renderer.setUseVA(true);
+ renderer.setVerboseLogging(verboseLogging);
+
+ renderer.initialize();
+ listener.initialize();
+
+ // Setup exit hook
+ if (ctx instanceof AndroidHarness) {
+ Application app = ((AndroidHarness) ctx).getJmeApplication();
+ if (app.getInputManager() != null) {
+ app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK));
+ app.getInputManager().addListener((AndroidHarness) ctx, new String[]{ESCAPE_EVENT});
+ }
+ }
+
+ needClose.set(false);
+ renderable.set(true);
+ }
+
+ /**
+ * De-initialize in the OpenGL thread.
+ */
+ protected void deinitInThread() {
+ if (renderable.get()) {
+ created.set(false);
+ if (renderer != null) {
+ renderer.cleanup();
+ }
+
+ listener.destroy();
+
+ listener = null;
+ renderer = null;
+ timer = null;
+
+ // do android specific cleaning here
+ logger.info("Display destroyed.");
+
+ renderable.set(false);
+ }
+ }
+
+ protected void applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) {
+ logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]");
+ logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]");
+ renderer.setUseVA(settings.getBoolean("USE_VA"));
+ renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING"));
+ }
+
+ protected void applySettings(AppSettings settings) {
+ setSettings(settings);
+ if (renderer != null) {
+ applySettingsToRenderer(renderer, this.settings);
+ }
+ }
+
+ @Override
+ public void setSettings(AppSettings settings) {
+ this.settings.copyFrom(settings);
+ }
+
+ @Override
+ public void setSystemListener(SystemListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public AppSettings getSettings() {
+ return settings;
+ }
+
+ @Override
+ public com.jme3.renderer.Renderer getRenderer() {
+ return renderer;
+ }
+
+ @Override
+ public MouseInput getMouseInput() {
+ return new DummyMouseInput();
+ }
+
+ @Override
+ public KeyInput getKeyInput() {
+ return new DummyKeyInput();
+ }
+
+ @Override
+ public JoyInput getJoyInput() {
+ return null;
+ }
+
+ @Override
+ public TouchInput getTouchInput() {
+ return view;
+ }
+
+ @Override
+ public Timer getTimer() {
+ return timer;
+ }
+
+ @Override
+ public void setTitle(String title) {
+ }
+
+ @Override
+ public boolean isCreated() {
+ return created.get();
+ }
+
+ @Override
+ public void setAutoFlushFrames(boolean enabled) {
+ this.autoFlush = enabled;
+ }
+
+ // SystemListener:reshape
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ logger.info("GL Surface changed, width: " + width + " height: " + height);
+ settings.setResolution(width, height);
+ listener.reshape(width, height);
+ }
+
+ // SystemListener:update
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ if (needClose.get()) {
+ deinitInThread();
+ return;
+ }
+
+ if (renderable.get()) {
+ if (!created.get()) {
+ throw new IllegalStateException("onDrawFrame without create");
+ }
+
+ long milliStart = System.currentTimeMillis();
+
+ listener.update();
+
+ // call to AndroidHarness to remove the splash screen, if present.
+ // call after listener.update() to make sure no gap between
+ // splash screen going away and app display being shown.
+ if (firstDrawFrame) {
+ final Context ctx = this.view.getContext();
+ if (ctx instanceof AndroidHarness) {
+ ((AndroidHarness) ctx).removeSplashScreen();
+ }
+ firstDrawFrame = false;
+ }
+
+ if (autoFlush) {
+ renderer.onFrame();
+ }
+
+ long milliDelta = System.currentTimeMillis() - milliStart;
+
+ // Enforce a FPS cap
+ if (milliDelta < minFrameDuration) {
+ //logger.log(Level.INFO, "Time per frame {0}", milliDelta);
+ try {
+ Thread.sleep(minFrameDuration - milliDelta);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ }
+ }
+
+ @Override
+ public boolean isRenderable() {
+ return renderable.get();
+ }
+
+ @Override
+ public void create(boolean waitFor) {
+ if (waitFor) {
+ waitFor(true);
+ }
+ }
+
+ public void create() {
+ create(false);
+ }
+
+ @Override
+ public void restart() {
+ }
+
+ @Override
+ public void destroy(boolean waitFor) {
+ needClose.set(true);
+ if (waitFor) {
+ waitFor(false);
+ }
+ }
+
+ public void destroy() {
+ destroy(true);
+ }
+
+ protected void waitFor(boolean createdVal) {
+ while (renderable.get() != createdVal) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+
+ public int getClientOpenGLESVersion() {
+ return clientOpenGLESVersion;
+ }
+}
diff --git a/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java b/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java
new file mode 100644
index 0000000..17f850e
--- /dev/null
+++ b/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java
@@ -0,0 +1,20 @@
+package com.jme3.texture.plugins;
+
+import android.graphics.Bitmap;
+import com.jme3.asset.AndroidImageInfo;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.texture.Image;
+import java.io.IOException;
+
+public class AndroidImageLoader implements AssetLoader {
+
+ public Object load(AssetInfo info) throws IOException {
+ AndroidImageInfo imageInfo = new AndroidImageInfo(info);
+ Bitmap bitmap = imageInfo.getBitmap();
+
+ Image image = new Image(imageInfo.getFormat(), bitmap.getWidth(), bitmap.getHeight(), null);
+ image.setEfficentData(imageInfo);
+ return image;
+ }
+}
diff --git a/engine/src/android/com/jme3/util/AndroidLogHandler.java b/engine/src/android/com/jme3/util/AndroidLogHandler.java
new file mode 100644
index 0000000..8fb21c2
--- /dev/null
+++ b/engine/src/android/com/jme3/util/AndroidLogHandler.java
@@ -0,0 +1,37 @@
+package com.jme3.util;
+
+import android.util.Log;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+public class AndroidLogHandler extends Handler {
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ Level level = record.getLevel();
+ String clsName = record.getSourceClassName();
+ String msg = record.getMessage();
+ Throwable t = record.getThrown();
+ if (level == Level.INFO){
+ Log.i(clsName, msg, t);
+ }else if (level == Level.SEVERE){
+ Log.e(clsName, msg, t);
+ }else if (level == Level.WARNING){
+ Log.w(clsName, msg, t);
+ }else if (level == Level.CONFIG){
+ Log.d(clsName, msg, t);
+ }else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST){
+ Log.v(clsName, msg, t);
+ }
+ }
+
+}
diff --git a/engine/src/android/com/jme3/util/FastInteger.java b/engine/src/android/com/jme3/util/FastInteger.java
new file mode 100644
index 0000000..49294b4
--- /dev/null
+++ b/engine/src/android/com/jme3/util/FastInteger.java
@@ -0,0 +1,359 @@
+package com.jme3.util;
+
+
+/**
+ * The wrapper for the primitive type {@code int}.
+ * <p>
+ * As with the specification, this implementation relies on code laid out in <a
+ * href="http://www.hackersdelight.org/">Henry S. Warren, Jr.'s Hacker's
+ * Delight, (Addison Wesley, 2002)</a> as well as <a
+ * href="http://aggregate.org/MAGIC/">The Aggregate's Magic Algorithms</a>.
+ *
+ * @see java.lang.Number
+ * @since 1.1
+ */
+public final class FastInteger {
+
+ /**
+ * Constant for the maximum {@code int} value, 2<sup>31</sup>-1.
+ */
+ public static final int MAX_VALUE = 0x7FFFFFFF;
+
+ /**
+ * Constant for the minimum {@code int} value, -2<sup>31</sup>.
+ */
+ public static final int MIN_VALUE = 0x80000000;
+
+ /**
+ * Constant for the number of bits needed to represent an {@code int} in
+ * two's complement form.
+ *
+ * @since 1.5
+ */
+ public static final int SIZE = 32;
+
+ /*
+ * Progressively smaller decimal order of magnitude that can be represented
+ * by an instance of Integer. Used to help compute the String
+ * representation.
+ */
+ private static final int[] decimalScale = new int[] { 1000000000, 100000000,
+ 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };
+
+ /**
+ * Converts the specified integer into its decimal string representation.
+ * The returned string is a concatenation of a minus sign if the number is
+ * negative and characters from '0' to '9'.
+ *
+ * @param value
+ * the integer to convert.
+ * @return the decimal string representation of {@code value}.
+ */
+ public static boolean toCharArray(int value, char[] output) {
+ if (value == 0)
+ {
+ output[0] = '0';
+ output[1] = 0;
+ return true;
+ }
+
+ // Faster algorithm for smaller Integers
+ if (value < 1000 && value > -1000) {
+
+ int positive_value = value < 0 ? -value : value;
+ int first_digit = 0;
+ if (value < 0) {
+ output[0] = '-';
+ first_digit++;
+ }
+ int last_digit = first_digit;
+ int quot = positive_value;
+ do {
+ int res = quot / 10;
+ int digit_value = quot - ((res << 3) + (res << 1));
+ digit_value += '0';
+ output[last_digit++] = (char) digit_value;
+ quot = res;
+ } while (quot != 0);
+
+ int count = last_digit--;
+ do {
+ char tmp = output[last_digit];
+ output[last_digit--] = output[first_digit];
+ output[first_digit++] = tmp;
+ } while (first_digit < last_digit);
+ output[count] = 0;
+ return true;
+ }
+ if (value == MIN_VALUE) {
+ System.arraycopy("-2147483648".toCharArray(), 0, output, 0, 12);
+ output[12] = 0;
+ return true;
+ }
+
+
+ int positive_value = value < 0 ? -value : value;
+ byte first_digit = 0;
+ if (value < 0) {
+ output[0] = '-';
+ first_digit++;
+ }
+ byte last_digit = first_digit;
+ byte count;
+ int number;
+ boolean start = false;
+ for (int i = 0; i < 9; i++) {
+ count = 0;
+ if (positive_value < (number = decimalScale[i])) {
+ if (start) {
+ output[last_digit++] = '0';
+ }
+ continue;
+ }
+
+ if (i > 0) {
+ number = (decimalScale[i] << 3);
+ if (positive_value >= number) {
+ positive_value -= number;
+ count += 8;
+ }
+ number = (decimalScale[i] << 2);
+ if (positive_value >= number) {
+ positive_value -= number;
+ count += 4;
+ }
+ }
+ number = (decimalScale[i] << 1);
+ if (positive_value >= number) {
+ positive_value -= number;
+ count += 2;
+ }
+ if (positive_value >= decimalScale[i]) {
+ positive_value -= decimalScale[i];
+ count++;
+ }
+ if (count > 0 && !start) {
+ start = true;
+ }
+ if (start) {
+ output[last_digit++] = (char) (count + '0');
+ }
+ }
+
+ output[last_digit++] = (char) (positive_value + '0');
+ output[last_digit] = 0;
+ count = last_digit--;
+ return true;
+ }
+
+
+ /**
+ * Determines the highest (leftmost) bit of the specified integer that is 1
+ * and returns the bit mask value for that bit. This is also referred to as
+ * the Most Significant 1 Bit. Returns zero if the specified integer is
+ * zero.
+ *
+ * @param i
+ * the integer to examine.
+ * @return the bit mask indicating the highest 1 bit in {@code i}.
+ * @since 1.5
+ */
+ public static int highestOneBit(int i) {
+ i |= (i >> 1);
+ i |= (i >> 2);
+ i |= (i >> 4);
+ i |= (i >> 8);
+ i |= (i >> 16);
+ return (i & ~(i >>> 1));
+ }
+
+ /**
+ * Determines the lowest (rightmost) bit of the specified integer that is 1
+ * and returns the bit mask value for that bit. This is also referred
+ * to as the Least Significant 1 Bit. Returns zero if the specified integer
+ * is zero.
+ *
+ * @param i
+ * the integer to examine.
+ * @return the bit mask indicating the lowest 1 bit in {@code i}.
+ * @since 1.5
+ */
+ public static int lowestOneBit(int i) {
+ return (i & (-i));
+ }
+
+ /**
+ * Determines the number of leading zeros in the specified integer prior to
+ * the {@link #highestOneBit(int) highest one bit}.
+ *
+ * @param i
+ * the integer to examine.
+ * @return the number of leading zeros in {@code i}.
+ * @since 1.5
+ */
+ public static int numberOfLeadingZeros(int i) {
+ i |= i >> 1;
+ i |= i >> 2;
+ i |= i >> 4;
+ i |= i >> 8;
+ i |= i >> 16;
+ return bitCount(~i);
+ }
+
+ /**
+ * Determines the number of trailing zeros in the specified integer after
+ * the {@link #lowestOneBit(int) lowest one bit}.
+ *
+ * @param i
+ * the integer to examine.
+ * @return the number of trailing zeros in {@code i}.
+ * @since 1.5
+ */
+ public static int numberOfTrailingZeros(int i) {
+ return bitCount((i & -i) - 1);
+ }
+
+ /**
+ * Counts the number of 1 bits in the specified integer; this is also
+ * referred to as population count.
+ *
+ * @param i
+ * the integer to examine.
+ * @return the number of 1 bits in {@code i}.
+ * @since 1.5
+ */
+ public static int bitCount(int i) {
+ i -= ((i >> 1) & 0x55555555);
+ i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
+ i = (((i >> 4) + i) & 0x0F0F0F0F);
+ i += (i >> 8);
+ i += (i >> 16);
+ return (i & 0x0000003F);
+ }
+
+ /**
+ * Rotates the bits of the specified integer to the left by the specified
+ * number of bits.
+ *
+ * @param i
+ * the integer value to rotate left.
+ * @param distance
+ * the number of bits to rotate.
+ * @return the rotated value.
+ * @since 1.5
+ */
+ public static int rotateLeft(int i, int distance) {
+ if (distance == 0) {
+ return i;
+ }
+ /*
+ * According to JLS3, 15.19, the right operand of a shift is always
+ * implicitly masked with 0x1F, which the negation of 'distance' is
+ * taking advantage of.
+ */
+ return ((i << distance) | (i >>> (-distance)));
+ }
+
+ /**
+ * Rotates the bits of the specified integer to the right by the specified
+ * number of bits.
+ *
+ * @param i
+ * the integer value to rotate right.
+ * @param distance
+ * the number of bits to rotate.
+ * @return the rotated value.
+ * @since 1.5
+ */
+ public static int rotateRight(int i, int distance) {
+ if (distance == 0) {
+ return i;
+ }
+ /*
+ * According to JLS3, 15.19, the right operand of a shift is always
+ * implicitly masked with 0x1F, which the negation of 'distance' is
+ * taking advantage of.
+ */
+ return ((i >>> distance) | (i << (-distance)));
+ }
+
+ /**
+ * Reverses the order of the bytes of the specified integer.
+ *
+ * @param i
+ * the integer value for which to reverse the byte order.
+ * @return the reversed value.
+ * @since 1.5
+ */
+ public static int reverseBytes(int i) {
+ int b3 = i >>> 24;
+ int b2 = (i >>> 8) & 0xFF00;
+ int b1 = (i & 0xFF00) << 8;
+ int b0 = i << 24;
+ return (b0 | b1 | b2 | b3);
+ }
+
+ /**
+ * Reverses the order of the bits of the specified integer.
+ *
+ * @param i
+ * the integer value for which to reverse the bit order.
+ * @return the reversed value.
+ * @since 1.5
+ */
+ public static int reverse(int i) {
+ // From Hacker's Delight, 7-1, Figure 7-1
+ i = (i & 0x55555555) << 1 | (i >> 1) & 0x55555555;
+ i = (i & 0x33333333) << 2 | (i >> 2) & 0x33333333;
+ i = (i & 0x0F0F0F0F) << 4 | (i >> 4) & 0x0F0F0F0F;
+ return reverseBytes(i);
+ }
+
+ /**
+ * Returns the value of the {@code signum} function for the specified
+ * integer.
+ *
+ * @param i
+ * the integer value to check.
+ * @return -1 if {@code i} is negative, 1 if {@code i} is positive, 0 if
+ * {@code i} is zero.
+ * @since 1.5
+ */
+ public static int signum(int i) {
+ return (i == 0 ? 0 : (i < 0 ? -1 : 1));
+ }
+
+ /**
+ * Returns a {@code Integer} instance for the specified integer value.
+ * <p>
+ * If it is not necessary to get a new {@code Integer} instance, it is
+ * recommended to use this method instead of the constructor, since it
+ * maintains a cache of instances which may result in better performance.
+ *
+ * @param i
+ * the integer value to store in the instance.
+ * @return a {@code Integer} instance containing {@code i}.
+ * @since 1.5
+ */
+ public static Integer valueOf(int i) {
+ if (i < -128 || i > 127) {
+ return new Integer(i);
+ }
+ return valueOfCache.CACHE [i+128];
+
+ }
+
+ static class valueOfCache {
+ /**
+ * <p>
+ * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing.
+ */
+ static final Integer[] CACHE = new Integer[256];
+
+ static {
+ for(int i=-128; i<=127; i++) {
+ CACHE[i+128] = new Integer(i);
+ }
+ }
+ }
+}
diff --git a/engine/src/android/com/jme3/util/RingBuffer.java b/engine/src/android/com/jme3/util/RingBuffer.java
new file mode 100644
index 0000000..786417b
--- /dev/null
+++ b/engine/src/android/com/jme3/util/RingBuffer.java
@@ -0,0 +1,75 @@
+package com.jme3.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Ring buffer (fixed size queue) implementation using a circular array (array with wrap-around).
+ */
+// suppress unchecked warnings in Java 1.5.0_6 and later
+@SuppressWarnings("unchecked")
+public class RingBuffer<Item> implements Iterable<Item> {
+
+ private Item[] buffer; // queue elements
+ private int count = 0; // number of elements on queue
+ private int indexOut = 0; // index of first element of queue
+ private int indexIn = 0; // index of next available slot
+
+ // cast needed since no generic array creation in Java
+ public RingBuffer(int capacity) {
+ buffer = (Item[]) new Object[capacity];
+ }
+
+ public boolean isEmpty() {
+ return count == 0;
+ }
+
+ public int size() {
+ return count;
+ }
+
+ public void push(Item item) {
+ if (count == buffer.length) {
+ throw new RuntimeException("Ring buffer overflow");
+ }
+ buffer[indexIn] = item;
+ indexIn = (indexIn + 1) % buffer.length; // wrap-around
+ count++;
+ }
+
+ public Item pop() {
+ if (isEmpty()) {
+ throw new RuntimeException("Ring buffer underflow");
+ }
+ Item item = buffer[indexOut];
+ buffer[indexOut] = null; // to help with garbage collection
+ count--;
+ indexOut = (indexOut + 1) % buffer.length; // wrap-around
+ return item;
+ }
+
+ public Iterator<Item> iterator() {
+ return new RingBufferIterator();
+ }
+
+ // an iterator, doesn't implement remove() since it's optional
+ private class RingBufferIterator implements Iterator<Item> {
+
+ private int i = 0;
+
+ public boolean hasNext() {
+ return i < count;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Item next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return buffer[i++];
+ }
+ }
+}
diff --git a/engine/src/android/jme3test/android/AndroidManifest.xml b/engine/src/android/jme3test/android/AndroidManifest.xml
new file mode 100644
index 0000000..a1f8f3f
--- /dev/null
+++ b/engine/src/android/jme3test/android/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.jme3.androiddemo"
+ android:versionCode="6"
+ android:versionName="1.2.2">
+
+ <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+ <!-- Tell the system that you need ES 2.0. -->
+ <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+ <!-- Tell the system that you need distinct touches (for the zoom gesture). -->
+ <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="true" />
+
+ <application android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:name=".DemoMainActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".DemoAndroidHarness"
+ android:label="@string/app_name">
+ </activity>
+
+ </application>
+</manifest> \ No newline at end of file
diff --git a/engine/src/android/jme3test/android/DemoAndroidHarness.java b/engine/src/android/jme3test/android/DemoAndroidHarness.java
new file mode 100644
index 0000000..0a431cf
--- /dev/null
+++ b/engine/src/android/jme3test/android/DemoAndroidHarness.java
@@ -0,0 +1,54 @@
+package jme3test.android;
+
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import com.jme3.app.AndroidHarness;
+import com.jme3.system.android.AndroidConfigChooser.ConfigType;
+
+public class DemoAndroidHarness extends AndroidHarness
+{
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ // Set the application class to run
+ // First Extract the bundle from intent
+ Bundle bundle = getIntent().getExtras();
+
+ //Next extract the values using the key as
+ appClass = bundle.getString("APPCLASSNAME");
+
+
+ String eglConfig = bundle.getString("EGLCONFIG");
+ if (eglConfig.equals("Best"))
+ {
+ eglConfigType = ConfigType.BEST;
+ }
+ else if (eglConfig.equals("Legacy"))
+ {
+ eglConfigType = ConfigType.LEGACY;
+ }
+ else
+ {
+ eglConfigType = ConfigType.FASTEST;
+ }
+
+
+ if (bundle.getBoolean("VERBOSE"))
+ {
+ eglConfigVerboseLogging = true;
+ }
+ else
+ {
+ eglConfigVerboseLogging = false;
+ }
+
+
+ exitDialogTitle = "Close Demo?";
+ exitDialogMessage = "Press Yes";
+
+ screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+
+ super.onCreate(savedInstanceState);
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/DemoLaunchAdapter.java b/engine/src/android/jme3test/android/DemoLaunchAdapter.java
new file mode 100644
index 0000000..9d4c9de
--- /dev/null
+++ b/engine/src/android/jme3test/android/DemoLaunchAdapter.java
@@ -0,0 +1,72 @@
+package jme3test.android;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+import java.util.List;
+
+/**
+ * The view adapter which gets a list of LaunchEntries and displaqs them
+ * @author larynx
+ *
+ */
+public class DemoLaunchAdapter extends BaseAdapter implements OnClickListener
+{
+
+ private Context context;
+
+ private List<DemoLaunchEntry> listDemos;
+
+ public DemoLaunchAdapter(Context context, List<DemoLaunchEntry> listDemos) {
+ this.context = context;
+ this.listDemos = listDemos;
+ }
+
+ public int getCount() {
+ return listDemos.size();
+ }
+
+ public Object getItem(int position) {
+ return listDemos.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup viewGroup) {
+ DemoLaunchEntry entry = listDemos.get(position);
+ if (convertView == null) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(R.layout.demo_row, null);
+ }
+ TextView tvDemoName = (TextView) convertView.findViewById(R.id.tvDemoName);
+ tvDemoName.setText(entry.getName());
+
+ TextView tvDescription = (TextView) convertView.findViewById(R.id.tvDescription);
+ tvDescription.setText(entry.getDescription());
+
+ return convertView;
+ }
+
+ @Override
+ public void onClick(View view) {
+ DemoLaunchEntry entry = (DemoLaunchEntry) view.getTag();
+
+
+
+
+ }
+
+ private void showDialog(DemoLaunchEntry entry) {
+ // Create and show your dialog
+ // Depending on the Dialogs button clicks delete it or do nothing
+ }
+
+}
+
diff --git a/engine/src/android/jme3test/android/DemoLaunchEntry.java b/engine/src/android/jme3test/android/DemoLaunchEntry.java
new file mode 100644
index 0000000..e23d5eb
--- /dev/null
+++ b/engine/src/android/jme3test/android/DemoLaunchEntry.java
@@ -0,0 +1,38 @@
+package jme3test.android;
+
+/**
+ * Name (=appClass) and Description of one demo launch inside the main apk
+ * @author larynx
+ *
+ */
+public class DemoLaunchEntry
+{
+ private String name;
+ private String description;
+
+ /**
+ * @param name
+ * @param description
+ */
+ public DemoLaunchEntry(String name, String description) {
+ super();
+ this.name = name;
+ this.description = description;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+
+
+}
diff --git a/engine/src/android/jme3test/android/DemoMainActivity.java b/engine/src/android/jme3test/android/DemoMainActivity.java
new file mode 100644
index 0000000..dfc88aa
--- /dev/null
+++ b/engine/src/android/jme3test/android/DemoMainActivity.java
@@ -0,0 +1,131 @@
+package jme3test.android;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DemoMainActivity extends Activity {
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Intent myIntent = new Intent(DemoMainActivity.this, DemoAndroidHarness.class);
+
+ //Next create the bundle and initialize it
+ final Bundle bundle = new Bundle();
+
+
+ final Spinner spinnerConfig = (Spinner) findViewById(R.id.spinnerConfig);
+ ArrayAdapter<CharSequence> adapterDropDownConfig = ArrayAdapter.createFromResource(
+ this, R.array.eglconfig_array, android.R.layout.simple_spinner_item);
+ adapterDropDownConfig.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinnerConfig.setAdapter(adapterDropDownConfig);
+
+
+ spinnerConfig.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent,
+ View view, int pos, long id) {
+ Toast.makeText(parent.getContext(), "Set EGLConfig " +
+ parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
+ //Add the parameters to bundle as
+ bundle.putString("EGLCONFIG", parent.getItemAtPosition(pos).toString());
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ // Do nothing.
+ }
+ });
+
+
+ final Spinner spinnerLogging = (Spinner) findViewById(R.id.spinnerLogging);
+ ArrayAdapter<CharSequence> adapterDropDownLogging = ArrayAdapter.createFromResource(
+ this, R.array.logging_array, android.R.layout.simple_spinner_item);
+ adapterDropDownLogging.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinnerLogging.setAdapter(adapterDropDownLogging);
+
+
+ spinnerLogging.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent,
+ View view, int pos, long id) {
+ Toast.makeText(parent.getContext(), "Set Logging " +
+ parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
+
+ //Add the parameters to bundle as
+ bundle.putBoolean("VERBOSE", parent.getItemAtPosition(pos).toString().equals("Verbose"));
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ // Do nothing.
+ }
+ });
+
+
+ ListView list = (ListView) findViewById(R.id.ListView01);
+ list.setClickable(true);
+
+ final List<DemoLaunchEntry> listDemos = new ArrayList<DemoLaunchEntry>();
+
+ listDemos.add(new DemoLaunchEntry("jme3test.android.SimpleTexturedTest", "An field of textured boxes rotating"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestSkyLoadingLagoon", "Sky box demonstration with jpg"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestSkyLoadingPrimitives", "Sky box demonstration with png"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestBumpModel", "Shows a bump mapped well with a moving light"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestNormalMapping", "Shows a normal mapped sphere"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestUnshadedModel", "Shows an unshaded model of the sphere"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestMovingParticle", "Demonstrates particle effects"));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestAmbient", "Positional sound - You sit in a dark cave under a waterfall"));
+
+ //listDemos.add(new DemoLaunchEntry("jme3test.effect.TestParticleEmitter", ""));
+ //listDemos.add(new DemoLaunchEntry("jme3test.effect.TestPointSprite", ""));
+ //listDemos.add(new DemoLaunchEntry("jme3test.light.TestLightRadius", ""));
+ listDemos.add(new DemoLaunchEntry("jme3test.android.TestMotionPath", "Shows cinematics - see a teapot on its journey - model loading needs a long time - just let it load, looks like freezed"));
+ //listDemos.add(new DemoLaunchEntry("com.jme3.androiddemo.TestSimpleWater", "Post processors - not working correctly due to missing framebuffer support, looks interresting :)"));
+ //listDemos.add(new DemoLaunchEntry("jme3test.model.TestHoverTank", ""));
+ //listDemos.add(new DemoLaunchEntry("jme3test.niftygui.TestNiftyGui", ""));
+ //listDemos.add(new DemoLaunchEntry("com.jme3.androiddemo.TestNiftyGui", ""));
+
+
+ DemoLaunchAdapter adapterList = new DemoLaunchAdapter(this, listDemos);
+
+ list.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View view, int position, long index) {
+ System.out.println("onItemClick");
+ showToast(listDemos.get(position).getName());
+
+
+ //Add the parameters to bundle as
+ bundle.putString("APPCLASSNAME", listDemos.get(position).getName());
+
+ //Add this bundle to the intent
+ myIntent.putExtras(bundle);
+
+ //Start the JME3 app harness activity
+ DemoMainActivity.this.startActivity(myIntent);
+
+ }
+ });
+
+ list.setAdapter(adapterList);
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ }
+}
+
diff --git a/engine/src/android/jme3test/android/R.java b/engine/src/android/jme3test/android/R.java
new file mode 100644
index 0000000..ffb1f37
--- /dev/null
+++ b/engine/src/android/jme3test/android/R.java
@@ -0,0 +1,46 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package jme3test.android;
+
+public final class R {
+ public static final class array {
+ public static final int eglconfig_array=0x7f060000;
+ public static final int logging_array=0x7f060001;
+ }
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int icon=0x7f020000;
+ }
+ public static final class id {
+ public static final int LinearLayout01=0x7f070000;
+ public static final int LinearLayout02=0x7f070002;
+ public static final int ListView01=0x7f070009;
+ public static final int TextView01=0x7f070003;
+ public static final int spinnerConfig=0x7f070006;
+ public static final int spinnerLogging=0x7f070008;
+ public static final int tvConfig=0x7f070005;
+ public static final int tvDemoName=0x7f070001;
+ public static final int tvDescription=0x7f070004;
+ public static final int tvLogging=0x7f070007;
+ }
+ public static final class layout {
+ public static final int demo_row=0x7f030000;
+ public static final int main=0x7f030001;
+ }
+ public static final class raw {
+ public static final int oddbounce=0x7f040000;
+ }
+ public static final class string {
+ public static final int app_name=0x7f050000;
+ public static final int eglconfig_prompt=0x7f050001;
+ public static final int eglconfig_text=0x7f050002;
+ public static final int logging_prompt=0x7f050003;
+ public static final int logging_text=0x7f050004;
+ }
+}
diff --git a/engine/src/android/jme3test/android/SimpleSoundTest.java b/engine/src/android/jme3test/android/SimpleSoundTest.java
new file mode 100644
index 0000000..9503bca
--- /dev/null
+++ b/engine/src/android/jme3test/android/SimpleSoundTest.java
@@ -0,0 +1,40 @@
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.InputListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.math.Vector3f;
+
+public class SimpleSoundTest extends SimpleApplication implements InputListener {
+
+ private AudioNode gun;
+ private AudioNode nature;
+
+ @Override
+ public void simpleInitApp() {
+ gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav");
+ gun.setPositional(true);
+ gun.setLocalTranslation(new Vector3f(0, 0, 0));
+ gun.setMaxDistance(100);
+ gun.setRefDistance(5);
+
+ nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true);
+ nature.setVolume(3);
+ nature.setLooping(true);
+ nature.play();
+
+ inputManager.addMapping("click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(this, "click");
+
+ rootNode.attachChild(gun);
+ rootNode.attachChild(nature);
+ }
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("click") && isPressed) {
+ gun.playInstance();
+ }
+ }
+}
diff --git a/engine/src/android/jme3test/android/SimpleTexturedTest.java b/engine/src/android/jme3test/android/SimpleTexturedTest.java
new file mode 100644
index 0000000..3f7b8d4
--- /dev/null
+++ b/engine/src/android/jme3test/android/SimpleTexturedTest.java
@@ -0,0 +1,150 @@
+
+/*
+ * Android 2.2+ SimpleTextured test.
+ *
+ * created: Mon Nov 8 00:08:22 EST 2010
+ */
+
+package jme3test.android;
+
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Texture;
+import com.jme3.util.TangentBinormalGenerator;
+
+
+public class SimpleTexturedTest extends SimpleApplication {
+
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(SimpleTexturedTest.class.getName());
+
+
+ private Node spheresContainer = new Node("spheres-container");
+
+
+ private boolean lightingEnabled = true;
+ private boolean texturedEnabled = true;
+ private boolean spheres = true;
+
+ @Override
+ public void simpleInitApp() {
+
+ //flyCam.setRotationSpeed(0.01f);
+
+
+ Mesh shapeSphere = null;
+ Mesh shapeBox = null;
+
+
+ shapeSphere = new Sphere(16, 16, .5f);
+ shapeBox = new Box(Vector3f.ZERO, 0.3f, 0.3f, 0.3f);
+
+
+ // ModelConverter.optimize(geom);
+
+ Texture texture = assetManager.loadTexture(new TextureKey("Interface/Logo/Monkey.jpg"));
+ Texture textureMonkey = assetManager.loadTexture(new TextureKey("Interface/Logo/Monkey.jpg"));
+
+ Material material = null;
+ Material materialMonkey = null;
+
+ if (texturedEnabled) {
+ if (lightingEnabled) {
+ material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ material.setBoolean("VertexLighting", true);
+ material.setFloat("Shininess", 127);
+ material.setBoolean("LowQuality", true);
+ material.setTexture("DiffuseMap", texture);
+
+ materialMonkey = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ materialMonkey.setBoolean("VertexLighting", true);
+ materialMonkey.setFloat("Shininess", 127);
+ materialMonkey.setBoolean("LowQuality", true);
+ materialMonkey.setTexture("DiffuseMap", textureMonkey);
+
+ } else {
+ material = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
+ material.setTexture("ColorMap", texture);
+
+ materialMonkey = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
+ materialMonkey.setTexture("ColorMap", textureMonkey);
+ }
+ } else {
+ material = new Material(assetManager, "Common/MatDefs/Misc/SolidColor.j3md");
+ material.setColor("Color", ColorRGBA.Red);
+ materialMonkey = new Material(assetManager, "Common/MatDefs/Misc/SolidColor.j3md");
+ materialMonkey.setColor("Color", ColorRGBA.Red);
+ }
+
+ TangentBinormalGenerator.generate(shapeSphere);
+ TangentBinormalGenerator.generate(shapeBox);
+
+ int iFlipper = 0;
+ for (int y = -1; y < 2; y++) {
+ for (int x = -1; x < 2; x++){
+ Geometry geomClone = null;
+
+ //iFlipper++;
+ if (iFlipper % 2 == 0)
+ {
+ geomClone = new Geometry("geometry-" + y + "-" + x, shapeBox);
+ }
+ else
+ {
+ geomClone = new Geometry("geometry-" + y + "-" + x, shapeSphere);
+ }
+ if (iFlipper % 3 == 0)
+ {
+ geomClone.setMaterial(materialMonkey);
+ }
+ else
+ {
+ geomClone.setMaterial(material);
+ }
+ geomClone.setLocalTranslation(x, y, 0);
+
+// Transform t = geom.getLocalTransform().clone();
+// Transform t2 = geomClone.getLocalTransform().clone();
+// t.combineWithParent(t2);
+// geomClone.setLocalTransform(t);
+
+ spheresContainer.attachChild(geomClone);
+ }
+ }
+
+ spheresContainer.setLocalTranslation(new Vector3f(0, 0, -4f));
+ spheresContainer.setLocalScale(2.0f);
+
+ rootNode.attachChild(spheresContainer);
+
+ PointLight pointLight = new PointLight();
+
+ pointLight.setColor(new ColorRGBA(0.7f, 0.7f, 1.0f, 1.0f));
+
+ pointLight.setPosition(new Vector3f(0f, 0f, 0f));
+ pointLight.setRadius(8);
+
+ rootNode.addLight(pointLight);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+
+ // secondCounter has been removed from SimpleApplication
+ //if (secondCounter == 0)
+ // logger.info("Frames per second: " + timer.getFrameRate());
+
+ spheresContainer.rotate(0.2f * tpf, 0.4f * tpf, 0.8f * tpf);
+ }
+
+}
+
diff --git a/engine/src/android/jme3test/android/TestAmbient.java b/engine/src/android/jme3test/android/TestAmbient.java
new file mode 100644
index 0000000..50ca739
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestAmbient.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.android;
+
+import android.media.SoundPool;
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.math.Vector3f;
+
+public class TestAmbient extends SimpleApplication {
+
+ private AudioNode footsteps, beep;
+ private AudioNode nature, waves;
+
+ SoundPool soundPool;
+
+// private PointAudioSource waves;
+ private float time = 0;
+ private float nextTime = 1;
+
+ public static void main(String[] args){
+ TestAmbient test = new TestAmbient();
+ test.start();
+ }
+
+
+ @Override
+ public void simpleInitApp()
+ {
+ /*
+ footsteps = new AudioNode(audioRenderer, assetManager, "Sound/Effects/Foot steps.ogg", true);
+
+ footsteps.setPositional(true);
+ footsteps.setLocalTranslation(new Vector3f(4, -1, 30));
+ footsteps.setMaxDistance(5);
+ footsteps.setRefDistance(1);
+ footsteps.setLooping(true);
+
+ beep = new AudioNode(audioRenderer, assetManager, "Sound/Effects/Beep.ogg", true);
+ beep.setVolume(3);
+ beep.setLooping(true);
+
+ audioRenderer.playSourceInstance(footsteps);
+ audioRenderer.playSource(beep);
+ */
+
+ waves = new AudioNode(assetManager, "Sound/Environment/Ocean Waves.ogg", true);
+ waves.setPositional(true);
+
+ nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true);
+
+ waves.setLocalTranslation(new Vector3f(4, -1, 30));
+ waves.setMaxDistance(5);
+ waves.setRefDistance(1);
+
+ nature.setVolume(3);
+ audioRenderer.playSourceInstance(waves);
+ audioRenderer.playSource(nature);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf)
+ {
+
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestBumpModel.java b/engine/src/android/jme3test/android/TestBumpModel.java
new file mode 100644
index 0000000..196286f
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestBumpModel.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.ogre.OgreMeshKey;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestBumpModel extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestBumpModel app = new TestBumpModel();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Spatial signpost = (Spatial) assetManager.loadAsset(new OgreMeshKey("Models/Sign Post/Sign Post.mesh.xml"));
+ signpost.setMaterial( (Material) assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"));
+ TangentBinormalGenerator.generate(signpost);
+ rootNode.attachChild(signpost);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial( (Material) assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ // flourescent main light
+ pl = new PointLight();
+ pl.setColor(new ColorRGBA(0.88f, 0.92f, 0.95f, 1.0f));
+ rootNode.addLight(pl);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(new ColorRGBA(0.44f, 0.40f, 0.20f, 1.0f));
+ rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1,-1,-1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.92f, 0.85f, 0.8f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 6f, 3f, FastMath.sin(angle) * 6f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestMovingParticle.java b/engine/src/android/jme3test/android/TestMovingParticle.java
new file mode 100644
index 0000000..eda7db9
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestMovingParticle.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+
+/**
+ * Particle that moves in a circle.
+ *
+ * @author Kirill Vainer
+ */
+public class TestMovingParticle extends SimpleApplication {
+
+ private ParticleEmitter emit;
+ private float angle = 0;
+
+ public static void main(String[] args) {
+ TestMovingParticle app = new TestMovingParticle();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ emit = new ParticleEmitter("Emitter", Type.Triangle, 300);
+ emit.setGravity(0, 0, 0);
+ emit.setVelocityVariation(1);
+ emit.setLowLife(1);
+ emit.setHighLife(1);
+ emit.setInitialVelocity(new Vector3f(0, .5f, 0));
+ emit.setImagesX(15);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png"));
+ emit.setMaterial(mat);
+
+ rootNode.attachChild(emit);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(new ColorRGBA(0.84f, 0.80f, 0.80f, 1.0f));
+ rootNode.addLight(al);
+
+
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if ("setNum".equals(name) && isPressed) {
+ emit.setNumParticles(1000);
+ }
+ }
+ }, "setNum");
+
+ inputManager.addMapping("setNum", new KeyTrigger(KeyInput.KEY_SPACE));
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+ float x = FastMath.cos(angle) * 2;
+ float y = FastMath.sin(angle) * 2;
+ emit.setLocalTranslation(x, 0, y);
+ }
+}
diff --git a/engine/src/android/jme3test/android/TestNormalMapping.java b/engine/src/android/jme3test/android/TestNormalMapping.java
new file mode 100644
index 0000000..2dde1a4
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestNormalMapping.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestNormalMapping extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestNormalMapping app = new TestNormalMapping();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Sphere sphMesh = new Sphere(32, 32, 1);
+ sphMesh.setTextureMode(Sphere.TextureMode.Projected);
+ sphMesh.updateGeometry(32, 32, 1, false, false);
+ TangentBinormalGenerator.generate(sphMesh);
+
+ Geometry sphere = new Geometry("Rock Ball", sphMesh);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ sphere.setMaterial(mat);
+ rootNode.attachChild(sphere);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ pl.setPosition(new Vector3f(0f, 0f, 4f));
+ rootNode.addLight(pl);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(new ColorRGBA(0.44f, 0.40f, 0.20f, 1.0f));
+ rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1,-1,-1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.92f, 0.85f, 0.8f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 4f, 0.5f, FastMath.sin(angle) * 4f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestSkyLoadingLagoon.java b/engine/src/android/jme3test/android/TestSkyLoadingLagoon.java
new file mode 100644
index 0000000..507c661
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestSkyLoadingLagoon.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.Texture;
+import com.jme3.util.SkyFactory;
+
+public class TestSkyLoadingLagoon extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestSkyLoadingLagoon app = new TestSkyLoadingLagoon();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+
+ Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
+ Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
+ Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
+ Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
+ Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
+ Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
+
+
+ /*
+ Texture west = assetManager.loadTexture("Textures/Sky/Primitives/primitives_positive_x.png");
+ Texture east = assetManager.loadTexture("Textures/Sky/Primitives/primitives_negative_x.png");
+ Texture north = assetManager.loadTexture("Textures/Sky/Primitives/primitives_negative_z.png");
+ Texture south = assetManager.loadTexture("Textures/Sky/Primitives/primitives_positive_z.png");
+ Texture up = assetManager.loadTexture("Textures/Sky/Primitives/primitives_positive_y.png");
+ Texture down = assetManager.loadTexture("Textures/Sky/Primitives/primitives_negative_y.png");
+ */
+
+ Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
+ rootNode.attachChild(sky);
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestSkyLoadingPrimitives.java b/engine/src/android/jme3test/android/TestSkyLoadingPrimitives.java
new file mode 100644
index 0000000..282e237
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestSkyLoadingPrimitives.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.Texture;
+import com.jme3.util.SkyFactory;
+
+public class TestSkyLoadingPrimitives extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestSkyLoadingPrimitives app = new TestSkyLoadingPrimitives();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ /*
+ Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
+ Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
+ Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
+ Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
+ Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
+ Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
+ */
+
+ Texture west = assetManager.loadTexture("Textures/Sky/Primitives/primitives_positive_x.png");
+ Texture east = assetManager.loadTexture("Textures/Sky/Primitives/primitives_negative_x.png");
+ Texture north = assetManager.loadTexture("Textures/Sky/Primitives/primitives_negative_z.png");
+ Texture south = assetManager.loadTexture("Textures/Sky/Primitives/primitives_positive_z.png");
+ Texture up = assetManager.loadTexture("Textures/Sky/Primitives/primitives_positive_y.png");
+ Texture down = assetManager.loadTexture("Textures/Sky/Primitives/primitives_negative_y.png");
+
+ Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
+ rootNode.attachChild(sky);
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestUnshadedModel.java b/engine/src/android/jme3test/android/TestUnshadedModel.java
new file mode 100644
index 0000000..4e4ff8c
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestUnshadedModel.java
@@ -0,0 +1,44 @@
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestUnshadedModel extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestUnshadedModel app = new TestUnshadedModel();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Sphere sphMesh = new Sphere(32, 32, 1);
+ sphMesh.setTextureMode(Sphere.TextureMode.Projected);
+ sphMesh.updateGeometry(32, 32, 1, false, false);
+ TangentBinormalGenerator.generate(sphMesh);
+
+ Geometry sphere = new Geometry("Rock Ball", sphMesh);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ mat.setColor("Ambient", ColorRGBA.DarkGray);
+ mat.setColor("Diffuse", ColorRGBA.White);
+ mat.setBoolean("UseMaterialColors", true);
+ sphere.setMaterial(mat);
+ rootNode.attachChild(sphere);
+
+ PointLight pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ pl.setPosition(new Vector3f(4f, 0f, 0f));
+ rootNode.addLight(pl);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White);
+ rootNode.addLight(al);
+ }
+}
diff --git a/engine/src/android/jme3tools/android/Fixed.java b/engine/src/android/jme3tools/android/Fixed.java
new file mode 100644
index 0000000..4420999
--- /dev/null
+++ b/engine/src/android/jme3tools/android/Fixed.java
@@ -0,0 +1,431 @@
+package jme3tools.android;
+
+import java.util.Random;
+
+/**
+ * Fixed point maths class. This can be tailored for specific needs by
+ * changing the bits allocated to the 'fraction' part (see <code>FIXED_POINT
+ * </code>, which would also require <code>SIN_PRECALC</code> and <code>
+ * COS_PRECALC</code> updating).
+ *
+ * <p><a href="http://blog.numfum.com/2007/09/java-fixed-point-maths.html">
+ * http://blog.numfum.com/2007/09/java-fixed-point-maths.html</a></p>
+ *
+ * @version 1.0
+ * @author CW
+ *
+ * @deprecated Most devices with OpenGL ES 2.0 have an FPU. Please use
+ * floats instead of this class for decimal math.
+ */
+@Deprecated
+public final class Fixed {
+
+ /**
+ * Number of bits used for 'fraction'.
+ */
+ public static final int FIXED_POINT = 16;
+ /**
+ * Decimal one as represented by the Fixed class.
+ */
+ public static final int ONE = 1 << FIXED_POINT;
+ /**
+ * Half in fixed point.
+ */
+ public static final int HALF = ONE >> 1;
+ /**
+ * Quarter circle resolution for trig functions (should be a power of
+ * two). This is the number of discrete steps in 90 degrees.
+ */
+ public static final int QUARTER_CIRCLE = 64;
+ /**
+ * Mask used to limit angles to one revolution. If a quarter circle is 64
+ * (i.e. 90 degrees is broken into 64 steps) then the mask is 255.
+ */
+ public static final int FULL_CIRCLE_MASK = QUARTER_CIRCLE * 4 - 1;
+ /**
+ * The trig table is generated at a higher precision than the typical
+ * 16.16 format used for the rest of the fixed point maths. The table
+ * values are then shifted to match the actual fixed point used.
+ */
+ private static final int TABLE_SHIFT = 30;
+ /**
+ * Equivalent to: sin((2 * PI) / (QUARTER_CIRCLE * 4))
+ * <p>
+ * Note: if either QUARTER_CIRCLE or TABLE_SHIFT is changed this value
+ * will need recalculating (put the above formular into a calculator set
+ * radians, then shift the result by <code>TABLE_SHIFT</code>).
+ */
+ private static final int SIN_PRECALC = 26350943;
+ /**
+ * Equivalent to: cos((2 * PI) / (QUARTER_CIRCLE * 4)) * 2
+ *
+ * Note: if either QUARTER_CIRCLE or TABLE_SHIFT is changed this value
+ * will need recalculating ((put the above formular into a calculator set
+ * radians, then shift the result by <code>TABLE_SHIFT</code>).
+ */
+ private static final int COS_PRECALC = 2146836866;
+ /**
+ * One quarter sine wave as fixed point values.
+ */
+ private static final int[] SINE_TABLE = new int[QUARTER_CIRCLE + 1];
+ /**
+ * Scale value for indexing ATAN_TABLE[].
+ */
+ private static final int ATAN_SHIFT;
+ /**
+ * Reverse atan lookup table.
+ */
+ private static final byte[] ATAN_TABLE;
+ /**
+ * ATAN_TABLE.length
+ */
+ private static final int ATAN_TABLE_LEN;
+
+ /*
+ * Generates the tables and fills in any remaining static ints.
+ */
+ static {
+ // Generate the sine table using recursive synthesis.
+ SINE_TABLE[0] = 0;
+ SINE_TABLE[1] = SIN_PRECALC;
+ for (int n = 2; n < QUARTER_CIRCLE + 1; n++) {
+ SINE_TABLE[n] = (int) (((long) SINE_TABLE[n - 1] * COS_PRECALC) >> TABLE_SHIFT) - SINE_TABLE[n - 2];
+ }
+ // Scale the values to the fixed point format used.
+ for (int n = 0; n < QUARTER_CIRCLE + 1; n++) {
+ SINE_TABLE[n] = SINE_TABLE[n] + (1 << (TABLE_SHIFT - FIXED_POINT - 1)) >> TABLE_SHIFT - FIXED_POINT;
+ }
+
+ // Calculate a shift used to scale atan lookups
+ int rotl = 0;
+ int tan0 = tan(0);
+ int tan1 = tan(1);
+ while (rotl < 32) {
+ if ((tan1 >>= 1) > (tan0 >>= 1)) {
+ rotl++;
+ } else {
+ break;
+ }
+ }
+ ATAN_SHIFT = rotl;
+ // Create the a table of tan values
+ int[] lut = new int[QUARTER_CIRCLE];
+ for (int n = 0; n < QUARTER_CIRCLE; n++) {
+ lut[n] = tan(n) >> rotl;
+ }
+ ATAN_TABLE_LEN = lut[QUARTER_CIRCLE - 1];
+ // Then from the tan values create a reverse lookup
+ ATAN_TABLE = new byte[ATAN_TABLE_LEN];
+ for (byte n = 0; n < QUARTER_CIRCLE - 1; n++) {
+ int min = lut[n];
+ int max = lut[n + 1];
+ for (int i = min; i < max; i++) {
+ ATAN_TABLE[i] = n;
+ }
+ }
+ }
+ /**
+ * How many decimal places to use when converting a fixed point value to
+ * a decimal string.
+ *
+ * @see #toString
+ */
+ private static final int STRING_DECIMAL_PLACES = 2;
+ /**
+ * Value to add in order to round down a fixed point number when
+ * converting to a string.
+ */
+ private static final int STRING_DECIMAL_PLACES_ROUND;
+
+ static {
+ int i = 10;
+ for (int n = 1; n < STRING_DECIMAL_PLACES; n++) {
+ i *= i;
+ }
+ if (STRING_DECIMAL_PLACES == 0) {
+ STRING_DECIMAL_PLACES_ROUND = ONE / 2;
+ } else {
+ STRING_DECIMAL_PLACES_ROUND = ONE / (2 * i);
+ }
+ }
+ /**
+ * Random number generator. The standard <code>java.utll.Random</code> is
+ * used since it is available to both J2ME and J2SE. If a guaranteed
+ * sequence is required this would not be adequate.
+ */
+ private static Random rng = null;
+
+ /**
+ * Fixed can't be instantiated.
+ */
+ private Fixed() {
+ }
+
+ /**
+ * Returns an integer as a fixed point value.
+ */
+ public static int intToFixed(int n) {
+ return n << FIXED_POINT;
+ }
+
+ /**
+ * Returns a fixed point value as a float.
+ */
+ public static float fixedToFloat(int i) {
+ float fp = i;
+ fp = fp / ((float) ONE);
+ return fp;
+ }
+
+ /**
+ * Returns a float as a fixed point value.
+ */
+ public static int floatToFixed(float fp) {
+ return (int) (fp * ((float) ONE));
+ }
+
+ /**
+ * Converts a fixed point value into a decimal string.
+ */
+ public static String toString(int n) {
+ StringBuffer sb = new StringBuffer(16);
+ sb.append((n += STRING_DECIMAL_PLACES_ROUND) >> FIXED_POINT);
+ sb.append('.');
+ n &= ONE - 1;
+ for (int i = 0; i < STRING_DECIMAL_PLACES; i++) {
+ n *= 10;
+ sb.append((n / ONE) % 10);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Multiplies two fixed point values and returns the result.
+ */
+ public static int mul(int a, int b) {
+ return (int) ((long) a * (long) b >> FIXED_POINT);
+ }
+
+ /**
+ * Divides two fixed point values and returns the result.
+ */
+ public static int div(int a, int b) {
+ return (int) (((long) a << FIXED_POINT * 2) / (long) b >> FIXED_POINT);
+ }
+
+ /**
+ * Sine of an angle.
+ *
+ * @see #QUARTER_CIRCLE
+ */
+ public static int sin(int n) {
+ n &= FULL_CIRCLE_MASK;
+ if (n < QUARTER_CIRCLE * 2) {
+ if (n < QUARTER_CIRCLE) {
+ return SINE_TABLE[n];
+ } else {
+ return SINE_TABLE[QUARTER_CIRCLE * 2 - n];
+ }
+ } else {
+ if (n < QUARTER_CIRCLE * 3) {
+ return -SINE_TABLE[n - QUARTER_CIRCLE * 2];
+ } else {
+ return -SINE_TABLE[QUARTER_CIRCLE * 4 - n];
+ }
+ }
+ }
+
+ /**
+ * Cosine of an angle.
+ *
+ * @see #QUARTER_CIRCLE
+ */
+ public static int cos(int n) {
+ n &= FULL_CIRCLE_MASK;
+ if (n < QUARTER_CIRCLE * 2) {
+ if (n < QUARTER_CIRCLE) {
+ return SINE_TABLE[QUARTER_CIRCLE - n];
+ } else {
+ return -SINE_TABLE[n - QUARTER_CIRCLE];
+ }
+ } else {
+ if (n < QUARTER_CIRCLE * 3) {
+ return -SINE_TABLE[QUARTER_CIRCLE * 3 - n];
+ } else {
+ return SINE_TABLE[n - QUARTER_CIRCLE * 3];
+ }
+ }
+ }
+
+ /**
+ * Tangent of an angle.
+ *
+ * @see #QUARTER_CIRCLE
+ */
+ public static int tan(int n) {
+ return div(sin(n), cos(n));
+ }
+
+ /**
+ * Returns the arc tangent of an angle.
+ */
+ public static int atan(int n) {
+ n = n + (1 << (ATAN_SHIFT - 1)) >> ATAN_SHIFT;
+ if (n < 0) {
+ if (n <= -ATAN_TABLE_LEN) {
+ return -(QUARTER_CIRCLE - 1);
+ }
+ return -ATAN_TABLE[-n];
+ } else {
+ if (n >= ATAN_TABLE_LEN) {
+ return QUARTER_CIRCLE - 1;
+ }
+ return ATAN_TABLE[n];
+ }
+ }
+
+ /**
+ * Returns the polar angle of a rectangular coordinate.
+ */
+ public static int atan(int x, int y) {
+ int n = atan(div(x, abs(y) + 1)); // kludge to prevent ArithmeticException
+ if (y > 0) {
+ return n;
+ }
+ if (y < 0) {
+ if (x < 0) {
+ return -QUARTER_CIRCLE * 2 - n;
+ }
+ if (x > 0) {
+ return QUARTER_CIRCLE * 2 - n;
+ }
+ return QUARTER_CIRCLE * 2;
+ }
+ if (x > 0) {
+ return QUARTER_CIRCLE;
+ }
+ return -QUARTER_CIRCLE;
+ }
+
+ /**
+ * Rough calculation of the hypotenuse. Whilst not accurate it is very fast.
+ * <p>
+ * Derived from a piece in Graphics Gems.
+ */
+ public static int hyp(int x1, int y1, int x2, int y2) {
+ if ((x2 -= x1) < 0) {
+ x2 = -x2;
+ }
+ if ((y2 -= y1) < 0) {
+ y2 = -y2;
+ }
+ return x2 + y2 - (((x2 > y2) ? y2 : x2) >> 1);
+ }
+
+ /**
+ * Fixed point square root.
+ * <p>
+ * Derived from a 1993 Usenet algorithm posted by Christophe Meessen.
+ */
+ public static int sqrt(int n) {
+ if (n <= 0) {
+ return 0;
+ }
+ long sum = 0;
+ int bit = 0x40000000;
+ while (bit >= 0x100) { // lower values give more accurate results
+ long tmp = sum | bit;
+ if (n >= tmp) {
+ n -= tmp;
+ sum = tmp + bit;
+ }
+ bit >>= 1;
+ n <<= 1;
+ }
+ return (int) (sum >> 16 - (FIXED_POINT / 2));
+ }
+
+ /**
+ * Returns the absolute value.
+ */
+ public static int abs(int n) {
+ return (n < 0) ? -n : n;
+ }
+
+ /**
+ * Returns the sign of a value, -1 for negative numbers, otherwise 1.
+ */
+ public static int sgn(int n) {
+ return (n < 0) ? -1 : 1;
+ }
+
+ /**
+ * Returns the minimum of two values.
+ */
+ public static int min(int a, int b) {
+ return (a < b) ? a : b;
+ }
+
+ /**
+ * Returns the maximum of two values.
+ */
+ public static int max(int a, int b) {
+ return (a > b) ? a : b;
+ }
+
+ /**
+ * Clamps the value n between min and max.
+ */
+ public static int clamp(int n, int min, int max) {
+ return (n < min) ? min : (n > max) ? max : n;
+ }
+
+ /**
+ * Wraps the value n between 0 and the required limit.
+ */
+ public static int wrap(int n, int limit) {
+ return ((n %= limit) < 0) ? limit + n : n;
+ }
+
+ /**
+ * Returns the nearest int to a fixed point value. Equivalent to <code>
+ * Math.round()</code> in the standard library.
+ */
+ public static int round(int n) {
+ return n + HALF >> FIXED_POINT;
+ }
+
+ /**
+ * Returns the nearest int rounded down from a fixed point value.
+ * Equivalent to <code>Math.floor()</code> in the standard library.
+ */
+ public static int floor(int n) {
+ return n >> FIXED_POINT;
+ }
+
+ /**
+ * Returns the nearest int rounded up from a fixed point value.
+ * Equivalent to <code>Math.ceil()</code> in the standard library.
+ */
+ public static int ceil(int n) {
+ return n + (ONE - 1) >> FIXED_POINT;
+ }
+
+ /**
+ * Returns a fixed point value greater than or equal to decimal 0.0 and
+ * less than 1.0 (in 16.16 format this would be 0 to 65535 inclusive).
+ */
+ public static int rand() {
+ if (rng == null) {
+ rng = new Random();
+ }
+ return rng.nextInt() >>> (32 - FIXED_POINT);
+ }
+
+ /**
+ * Returns a random number between 0 and <code>n</code> (exclusive).
+ */
+ public static int rand(int n) {
+ return (rand() * n) >> FIXED_POINT;
+ }
+} \ No newline at end of file
diff --git a/engine/src/android/res/layout/about.xml b/engine/src/android/res/layout/about.xml
new file mode 100644
index 0000000..1d8c2b0
--- /dev/null
+++ b/engine/src/android/res/layout/about.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+>
+
+ <LinearLayout
+ android:id="@+id/buttonsContainer"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="copyright (c) 2009-2010 JMonkeyEngine"
+ />
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="http://www.jmonkeyengine.org"
+ />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/engine/src/android/res/layout/tests.xml b/engine/src/android/res/layout/tests.xml
new file mode 100644
index 0000000..70bd5ec
--- /dev/null
+++ b/engine/src/android/res/layout/tests.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+>
+ <LinearLayout
+ android:id="@+id/buttonsContainer"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+<!--
+ <Button
+ android:id="@+id/SimpleTextured"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:text="Simple Textured"
+ android:layout_weight="1"
+ />
+-->
+ </LinearLayout>
+</LinearLayout>
diff --git a/engine/src/android/res/menu/options.xml b/engine/src/android/res/menu/options.xml
new file mode 100644
index 0000000..8efec52
--- /dev/null
+++ b/engine/src/android/res/menu/options.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/about_button"
+ android:title="@string/about"
+ />
+
+ <item
+ android:id="@+id/quit_button"
+ android:title="@string/quit"
+ />
+</menu>
diff --git a/engine/src/android/res/values/strings.xml b/engine/src/android/res/values/strings.xml
new file mode 100644
index 0000000..92d63fa
--- /dev/null
+++ b/engine/src/android/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">JMEAndroidTest</string>
+ <string name="about">About</string>
+ <string name="quit">Quit</string>
+</resources>
diff --git a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag
new file mode 100644
index 0000000..55862ac
--- /dev/null
+++ b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.frag
@@ -0,0 +1,7 @@
+uniform sampler3D m_Texture;
+
+varying vec3 texCoord;
+
+void main(){
+ gl_FragColor= texture3D(m_Texture,texCoord);
+} \ No newline at end of file
diff --git a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md
new file mode 100644
index 0000000..1ba2605
--- /dev/null
+++ b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.j3md
@@ -0,0 +1,16 @@
+MaterialDef My MaterialDef {
+
+ MaterialParameters {
+ Texture3D Texture
+ }
+
+ Technique {
+ VertexShader GLSL100: jme3test/texture/tex3D.vert
+ FragmentShader GLSL100: jme3test/texture/tex3D.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+}
diff --git a/engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert
new file mode 100644
index 0000000..f91b7b3
--- /dev/null
+++ b/engine/src/blender/Common/MatDefs/Texture3D/tex3D.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inTexCoord;
+attribute vec3 inPosition;
+
+varying vec3 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
+ texCoord=inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/asset/BlenderKey.java b/engine/src/blender/com/jme3/asset/BlenderKey.java
new file mode 100644
index 0000000..a098682
--- /dev/null
+++ b/engine/src/blender/com/jme3/asset/BlenderKey.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.asset;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.Light;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Node;
+import com.jme3.scene.SceneGraphVisitor;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.ogre.AnimData;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Blender key. Contains path of the blender file and its loading properties.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class BlenderKey extends ModelKey {
+
+ protected static final int DEFAULT_FPS = 25;
+ /**
+ * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
+ * between the frames.
+ */
+ protected int fps = DEFAULT_FPS;
+ /** Width of generated textures (in pixels). */
+ protected int generatedTextureWidth = 60;
+ /** Height of generated textures (in pixels). */
+ protected int generatedTextureHeight = 60;
+ /** Depth of generated textures (in pixels). */
+ protected int generatedTextureDepth = 60;
+ /**
+ * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
+ */
+ protected int featuresToLoad = FeaturesToLoad.ALL;
+ /** This variable determines if assets that are not linked to the objects should be loaded. */
+ protected boolean loadUnlinkedAssets;
+ /** The root path for all the assets. */
+ protected String assetRootPath;
+ /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
+ protected boolean fixUpAxis = true;
+ /**
+ * The name of world settings that the importer will use. If not set or specified name does not occur in the file
+ * then the first world settings in the file will be used.
+ */
+ protected String usedWorld;
+ /**
+ * User's default material that is set fo objects that have no material definition in blender. The default value is
+ * null. If the value is null the importer will use its own default material (gray color - like in blender).
+ */
+ protected Material defaultMaterial;
+ /** Face cull mode. By default it is disabled. */
+ protected FaceCullMode faceCullMode = FaceCullMode.Off;
+ /**
+ * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
+ * If set to -1 then the current layer will be loaded.
+ */
+ protected int layersToLoad = -1;
+
+ /**
+ * Constructor used by serialization mechanisms.
+ */
+ public BlenderKey() {}
+
+ /**
+ * Constructor. Creates a key for the given file name.
+ * @param name
+ * the name (path) of a file
+ */
+ public BlenderKey(String name) {
+ super(name);
+ }
+
+ /**
+ * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
+ * @return the frames per second amount
+ */
+ public int getFps() {
+ return fps;
+ }
+
+ /**
+ * This method sets frames per second amount.
+ * @param fps
+ * the frames per second amount
+ */
+ public void setFps(int fps) {
+ this.fps = fps;
+ }
+
+ /**
+ * This method sets the width of generated texture (in pixels). By default the value is 140 px.
+ * @param generatedTextureWidth
+ * the width of generated texture
+ */
+ public void setGeneratedTextureWidth(int generatedTextureWidth) {
+ this.generatedTextureWidth = generatedTextureWidth;
+ }
+
+ /**
+ * This method returns the width of generated texture (in pixels). By default the value is 140 px.
+ * @return the width of generated texture
+ */
+ public int getGeneratedTextureWidth() {
+ return generatedTextureWidth;
+ }
+
+ /**
+ * This method sets the height of generated texture (in pixels). By default the value is 20 px.
+ * @param generatedTextureHeight
+ * the height of generated texture
+ */
+ public void setGeneratedTextureHeight(int generatedTextureHeight) {
+ this.generatedTextureHeight = generatedTextureHeight;
+ }
+
+ /**
+ * This method returns the height of generated texture (in pixels). By default the value is 20 px.
+ * @return the height of generated texture
+ */
+ public int getGeneratedTextureHeight() {
+ return generatedTextureHeight;
+ }
+
+ /**
+ * This method sets the depth of generated texture (in pixels). By default the value is 20 px.
+ * @param generatedTextureDepth
+ * the depth of generated texture
+ */
+ public void setGeneratedTextureDepth(int generatedTextureDepth) {
+ this.generatedTextureDepth = generatedTextureDepth;
+ }
+
+ /**
+ * This method returns the depth of generated texture (in pixels). By default the value is 20 px.
+ * @return the depth of generated texture
+ */
+ public int getGeneratedTextureDepth() {
+ return generatedTextureDepth;
+ }
+
+ /**
+ * This method returns the face cull mode.
+ * @return the face cull mode
+ */
+ public FaceCullMode getFaceCullMode() {
+ return faceCullMode;
+ }
+
+ /**
+ * This method sets the face cull mode.
+ * @param faceCullMode
+ * the face cull mode
+ */
+ public void setFaceCullMode(FaceCullMode faceCullMode) {
+ this.faceCullMode = faceCullMode;
+ }
+
+ /**
+ * This method sets layers to be loaded.
+ * @param layersToLoad
+ * layers to be loaded
+ */
+ public void setLayersToLoad(int layersToLoad) {
+ this.layersToLoad = layersToLoad;
+ }
+
+ /**
+ * This method returns layers to be loaded.
+ * @return layers to be loaded
+ */
+ public int getLayersToLoad() {
+ return layersToLoad;
+ }
+
+ /**
+ * This method sets the asset root path.
+ * @param assetRootPath
+ * the assets root path
+ */
+ public void setAssetRootPath(String assetRootPath) {
+ this.assetRootPath = assetRootPath;
+ }
+
+ /**
+ * This method returns the asset root path.
+ * @return the asset root path
+ */
+ public String getAssetRootPath() {
+ return assetRootPath;
+ }
+
+ /**
+ * This method adds features to be loaded.
+ * @param featuresToLoad
+ * bitwise flag of FeaturesToLoad interface values
+ */
+ public void includeInLoading(int featuresToLoad) {
+ this.featuresToLoad |= featuresToLoad;
+ }
+
+ /**
+ * This method removes features from being loaded.
+ * @param featuresNotToLoad
+ * bitwise flag of FeaturesToLoad interface values
+ */
+ public void excludeFromLoading(int featuresNotToLoad) {
+ this.featuresToLoad &= ~featuresNotToLoad;
+ }
+
+ /**
+ * This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
+ * the blender file loader.
+ * @return features that will be loaded by the blender file loader
+ */
+ public int getFeaturesToLoad() {
+ return featuresToLoad;
+ }
+
+ /**
+ * This method determines if unlinked assets should be loaded.
+ * If not then only objects on selected layers will be loaded and their assets if required.
+ * If yes then all assets will be loaded even if they are on inactive layers or are not linked
+ * to anything.
+ * @return <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
+ */
+ public boolean isLoadUnlinkedAssets() {
+ return loadUnlinkedAssets;
+ }
+
+ /**
+ * This method sets if unlinked assets should be loaded.
+ * If not then only objects on selected layers will be loaded and their assets if required.
+ * If yes then all assets will be loaded even if they are on inactive layers or are not linked
+ * to anything.
+ * @param loadUnlinkedAssets
+ * <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
+ */
+ public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
+ this.loadUnlinkedAssets = loadUnlinkedAssets;
+ }
+
+ /**
+ * This method creates an object where loading results will be stores. Only those features will be allowed to store
+ * that were specified by features-to-load flag.
+ * @return an object to store loading results
+ */
+ public LoadingResults prepareLoadingResults() {
+ return new LoadingResults(featuresToLoad);
+ }
+
+ /**
+ * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
+ * is up axis.
+ * @param fixUpAxis
+ * the up axis state variable
+ */
+ public void setFixUpAxis(boolean fixUpAxis) {
+ this.fixUpAxis = fixUpAxis;
+ }
+
+ /**
+ * This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
+ * default Y is up axis.
+ * @return the up axis state variable
+ */
+ public boolean isFixUpAxis() {
+ return fixUpAxis;
+ }
+
+ /**
+ * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
+ * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
+ * during loading (assumin any exists in the file).
+ * @param usedWorld
+ * the name of the WORLD block used during loading
+ */
+ public void setUsedWorld(String usedWorld) {
+ this.usedWorld = usedWorld;
+ }
+
+ /**
+ * This mehtod returns the name of the WORLD data block taht should be used during file loading.
+ * @return the name of the WORLD block used during loading
+ */
+ public String getUsedWorld() {
+ return usedWorld;
+ }
+
+ /**
+ * This method sets the default material for objects.
+ * @param defaultMaterial
+ * the default material
+ */
+ public void setDefaultMaterial(Material defaultMaterial) {
+ this.defaultMaterial = defaultMaterial;
+ }
+
+ /**
+ * This method returns the default material.
+ * @return the default material
+ */
+ public Material getDefaultMaterial() {
+ return defaultMaterial;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule oc = e.getCapsule(this);
+ oc.write(fps, "fps", DEFAULT_FPS);
+ oc.write(generatedTextureWidth, "generated-texture-width", 20);
+ oc.write(generatedTextureHeight, "generated-texture-height", 20);
+ oc.write(generatedTextureDepth, "generated-texture-depth", 20);
+ oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
+ oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
+ oc.write(assetRootPath, "asset-root-path", null);
+ oc.write(fixUpAxis, "fix-up-axis", true);
+ oc.write(usedWorld, "used-world", null);
+ oc.write(defaultMaterial, "default-material", null);
+ oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
+ oc.write(layersToLoad, "layers-to-load", -1);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule ic = e.getCapsule(this);
+ fps = ic.readInt("fps", DEFAULT_FPS);
+ generatedTextureWidth = ic.readInt("generated-texture-width", 20);
+ generatedTextureHeight = ic.readInt("generated-texture-height", 20);
+ generatedTextureDepth = ic.readInt("generated-texture-depth", 20);
+ featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
+ loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
+ assetRootPath = ic.readString("asset-root-path", null);
+ fixUpAxis = ic.readBoolean("fix-up-axis", true);
+ usedWorld = ic.readString("used-world", null);
+ defaultMaterial = (Material) ic.readSavable("default-material", null);
+ faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
+ layersToLoad = ic.readInt("layers-to=load", -1);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
+ result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
+ result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
+ result = prime * result + featuresToLoad;
+ result = prime * result + (fixUpAxis ? 1231 : 1237);
+ result = prime * result + fps;
+ result = prime * result + generatedTextureDepth;
+ result = prime * result + generatedTextureHeight;
+ result = prime * result + generatedTextureWidth;
+ result = prime * result + layersToLoad;
+ result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
+ result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (this.getClass() != obj.getClass()) {
+ return false;
+ }
+ BlenderKey other = (BlenderKey) obj;
+ if (assetRootPath == null) {
+ if (other.assetRootPath != null) {
+ return false;
+ }
+ } else if (!assetRootPath.equals(other.assetRootPath)) {
+ return false;
+ }
+ if (defaultMaterial == null) {
+ if (other.defaultMaterial != null) {
+ return false;
+ }
+ } else if (!defaultMaterial.equals(other.defaultMaterial)) {
+ return false;
+ }
+ if (faceCullMode != other.faceCullMode) {
+ return false;
+ }
+ if (featuresToLoad != other.featuresToLoad) {
+ return false;
+ }
+ if (fixUpAxis != other.fixUpAxis) {
+ return false;
+ }
+ if (fps != other.fps) {
+ return false;
+ }
+ if (generatedTextureDepth != other.generatedTextureDepth) {
+ return false;
+ }
+ if (generatedTextureHeight != other.generatedTextureHeight) {
+ return false;
+ }
+ if (generatedTextureWidth != other.generatedTextureWidth) {
+ return false;
+ }
+ if (layersToLoad != other.layersToLoad) {
+ return false;
+ }
+ if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
+ return false;
+ }
+ if (usedWorld == null) {
+ if (other.usedWorld != null) {
+ return false;
+ }
+ } else if (!usedWorld.equals(other.usedWorld)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This interface describes the features of the scene that are to be loaded.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static interface FeaturesToLoad {
+
+ int SCENES = 0x0000FFFF;
+ int OBJECTS = 0x0000000B;
+ int ANIMATIONS = 0x00000004;
+ int MATERIALS = 0x00000003;
+ int TEXTURES = 0x00000001;
+ int CAMERAS = 0x00000020;
+ int LIGHTS = 0x00000010;
+ int ALL = 0xFFFFFFFF;
+ }
+
+ /**
+ * This class holds the loading results according to the given loading flag.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static class LoadingResults extends Spatial {
+
+ /** Bitwise mask of features that are to be loaded. */
+ private final int featuresToLoad;
+ /** The scenes from the file. */
+ private List<Node> scenes;
+ /** Objects from all scenes. */
+ private List<Node> objects;
+ /** Materials from all objects. */
+ private List<Material> materials;
+ /** Textures from all objects. */
+ private List<Texture> textures;
+ /** Animations of all objects. */
+ private List<AnimData> animations;
+ /** All cameras from the file. */
+ private List<Camera> cameras;
+ /** All lights from the file. */
+ private List<Light> lights;
+
+ /**
+ * Private constructor prevents users to create an instance of this class from outside the
+ * @param featuresToLoad
+ * bitwise mask of features that are to be loaded
+ * @see FeaturesToLoad FeaturesToLoad
+ */
+ private LoadingResults(int featuresToLoad) {
+ this.featuresToLoad = featuresToLoad;
+ if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) {
+ scenes = new ArrayList<Node>();
+ }
+ if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
+ objects = new ArrayList<Node>();
+ if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
+ materials = new ArrayList<Material>();
+ if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
+ textures = new ArrayList<Texture>();
+ }
+ }
+ if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
+ animations = new ArrayList<AnimData>();
+ }
+ }
+ if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
+ cameras = new ArrayList<Camera>();
+ }
+ if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
+ lights = new ArrayList<Light>();
+ }
+ }
+
+ /**
+ * This method returns a bitwise flag describing what features of the blend file will be included in the result.
+ * @return bitwise mask of features that are to be loaded
+ * @see FeaturesToLoad FeaturesToLoad
+ */
+ public int getLoadedFeatures() {
+ return featuresToLoad;
+ }
+
+ /**
+ * This method adds a scene to the result set.
+ * @param scene
+ * scene to be added to the result set
+ */
+ public void addScene(Node scene) {
+ if (scenes != null) {
+ scenes.add(scene);
+ }
+ }
+
+ /**
+ * This method adds an object to the result set.
+ * @param object
+ * object to be added to the result set
+ */
+ public void addObject(Node object) {
+ if (objects != null) {
+ objects.add(object);
+ }
+ }
+
+ /**
+ * This method adds a material to the result set.
+ * @param material
+ * material to be added to the result set
+ */
+ public void addMaterial(Material material) {
+ if (materials != null) {
+ materials.add(material);
+ }
+ }
+
+ /**
+ * This method adds a texture to the result set.
+ * @param texture
+ * texture to be added to the result set
+ */
+ public void addTexture(Texture texture) {
+ if (textures != null) {
+ textures.add(texture);
+ }
+ }
+
+ /**
+ * This method adds a camera to the result set.
+ * @param camera
+ * camera to be added to the result set
+ */
+ public void addCamera(Camera camera) {
+ if (cameras != null) {
+ cameras.add(camera);
+ }
+ }
+
+ /**
+ * This method adds a light to the result set.
+ * @param light
+ * light to be added to the result set
+ */
+ @Override
+ public void addLight(Light light) {
+ if (lights != null) {
+ lights.add(light);
+ }
+ }
+
+ /**
+ * This method returns all loaded scenes.
+ * @return all loaded scenes
+ */
+ public List<Node> getScenes() {
+ return scenes;
+ }
+
+ /**
+ * This method returns all loaded objects.
+ * @return all loaded objects
+ */
+ public List<Node> getObjects() {
+ return objects;
+ }
+
+ /**
+ * This method returns all loaded materials.
+ * @return all loaded materials
+ */
+ public List<Material> getMaterials() {
+ return materials;
+ }
+
+ /**
+ * This method returns all loaded textures.
+ * @return all loaded textures
+ */
+ public List<Texture> getTextures() {
+ return textures;
+ }
+
+ /**
+ * This method returns all loaded animations.
+ * @return all loaded animations
+ */
+ public List<AnimData> getAnimations() {
+ return animations;
+ }
+
+ /**
+ * This method returns all loaded cameras.
+ * @return all loaded cameras
+ */
+ public List<Camera> getCameras() {
+ return cameras;
+ }
+
+ /**
+ * This method returns all loaded lights.
+ * @return all loaded lights
+ */
+ public List<Light> getLights() {
+ return lights;
+ }
+
+ @Override
+ public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
+ return 0;
+ }
+
+ @Override
+ public void updateModelBound() {}
+
+ @Override
+ public void setModelBound(BoundingVolume modelBound) {}
+
+ @Override
+ public int getVertexCount() {
+ return 0;
+ }
+
+ @Override
+ public int getTriangleCount() {
+ return 0;
+ }
+
+ @Override
+ public Spatial deepClone() {
+ return null;
+ }
+
+ @Override
+ public void depthFirstTraversal(SceneGraphVisitor visitor) {}
+
+ @Override
+ protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {}
+ }
+
+ /**
+ * The WORLD file block contains various data that could be added to the scene. The contained data includes: ambient
+ * light.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static class WorldData {
+
+ /** The ambient light. */
+ private AmbientLight ambientLight;
+
+ /**
+ * This method returns the world's ambient light.
+ * @return the world's ambient light
+ */
+ public AmbientLight getAmbientLight() {
+ return ambientLight;
+ }
+
+ /**
+ * This method sets the world's ambient light.
+ * @param ambientLight
+ * the world's ambient light
+ */
+ public void setAmbientLight(AmbientLight ambientLight) {
+ this.ambientLight = ambientLight;
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java b/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java
new file mode 100644
index 0000000..70f9f9e
--- /dev/null
+++ b/engine/src/blender/com/jme3/asset/GeneratedTextureKey.java
@@ -0,0 +1,37 @@
+package com.jme3.asset;
+
+
+/**
+ * This key is mostly used to distinguish between textures that are loaded from
+ * the given assets and those being generated automatically. Every generated
+ * texture will have this kind of key attached.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class GeneratedTextureKey extends TextureKey {
+ /**
+ * Constructor. Stores the name. Extension and folder name are empty
+ * strings.
+ *
+ * @param name
+ * the name of the texture
+ */
+ public GeneratedTextureKey(String name) {
+ super(name);
+ }
+
+ @Override
+ public String getExtension() {
+ return "";
+ }
+
+ @Override
+ public String getFolder() {
+ return "";
+ }
+
+ @Override
+ public String toString() {
+ return "Generated texture [" + name + "]";
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
new file mode 100644
index 0000000..8c49804
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.Properties;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.List;
+
+/**
+ * A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
+ * hold the state of the calculations.
+ * @author Marcin Roguski
+ */
+public abstract class AbstractBlenderHelper {
+
+ /** The version of the blend file. */
+ protected final int blenderVersion;
+ /** This variable indicates if the Y asxis is the UP axis or not. */
+ protected boolean fixUpAxis;
+ /** Quaternion used to rotate data when Y is up axis. */
+ protected Quaternion upAxisRotationQuaternion;
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
+ * versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public AbstractBlenderHelper(String blenderVersion, boolean fixUpAxis) {
+ this.blenderVersion = Integer.parseInt(blenderVersion);
+ this.fixUpAxis = fixUpAxis;
+ if(fixUpAxis) {
+ upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
+ }
+ }
+
+ /**
+ * This method clears the state of the helper so that it can be used for different calculations of another feature.
+ */
+ public void clearState() {}
+
+ /**
+ * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
+ * being created and stored in the memory. It can be unwise especially inside loops.
+ * @param text
+ * the text to be checked
+ * @return <b>true</b> if the text is blank and <b>false</b> otherwise
+ */
+ protected boolean isBlank(String text) {
+ if (text != null) {
+ for (int i = 0; i < text.length(); ++i) {
+ if (!Character.isWhitespace(text.charAt(i))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Generate a new ByteBuffer using the given array of byte[4] objects. The ByteBuffer will be 4 * data.length
+ * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
+ * @param data
+ * list of byte[4] objects to place into a new ByteBuffer
+ */
+ protected ByteBuffer createByteBuffer(List<byte[]> data) {
+ if (data == null) {
+ return null;
+ }
+ ByteBuffer buff = BufferUtils.createByteBuffer(4 * data.size());
+ for (byte[] v : data) {
+ if (v != null) {
+ buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
+ } else {
+ buff.put((byte)0).put((byte)0).put((byte)0).put((byte)0);
+ }
+ }
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length
+ * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
+ * @param data
+ * list of float[4] objects to place into a new FloatBuffer
+ */
+ protected FloatBuffer createFloatBuffer(List<float[]> data) {
+ if (data == null) {
+ return null;
+ }
+ FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());
+ for (float[] v : data) {
+ if (v != null) {
+ buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
+ } else {
+ buff.put(0).put(0).put(0).put(0);
+ }
+ }
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * This method loads the properties if they are available and defined for the structure.
+ * @param structure
+ * the structure we read the properties from
+ * @param blenderContext
+ * the blender context
+ * @return loaded properties or null if they are not available
+ * @throws BlenderFileException
+ * an exception is thrown when the blend file is somehow corrupted
+ */
+ protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+ Properties properties = null;
+ Structure id = (Structure) structure.getFieldValue("ID");
+ if (id != null) {
+ Pointer pProperties = (Pointer) id.getFieldValue("properties");
+ if (pProperties.isNotNull()) {
+ Structure propertiesStructure = pProperties.fetchData(blenderContext.getInputStream()).get(0);
+ properties = new Properties();
+ properties.load(propertiesStructure, blenderContext);
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * This method analyzes the given structure and the data contained within
+ * blender context and decides if the feature should be loaded.
+ * @param structure
+ * structure to be analyzed
+ * @param blenderContext
+ * the blender context
+ * @return <b>true</b> if the feature should be loaded and false otherwise
+ */
+ public abstract boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext);
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
new file mode 100644
index 0000000..b42f760
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderLoader.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender;
+
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.asset.BlenderKey.WorldData;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.Light;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.cameras.CameraHelper;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.lights.LightHelper;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+import com.jme3.scene.plugins.blender.meshes.MeshHelper;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class converts blender file blocks into jMonkeyEngine data structures.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */ abstract class AbstractBlenderLoader implements AssetLoader {
+ private static final Logger LOGGER = Logger.getLogger(AbstractBlenderLoader.class.getName());
+
+ protected BlenderContext blenderContext;
+
+ /**
+ * This method converts the given structure to a scene node.
+ * @param structure
+ * structure of a scene
+ * @return scene's node
+ */
+ public Node toScene(Structure structure) {
+ if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.SCENES) == 0) {
+ return null;
+ }
+ Node result = new Node(structure.getName());
+ try {
+ List<Structure> base = ((Structure)structure.getFieldValue("base")).evaluateListBase(blenderContext);
+ for(Structure b : base) {
+ Pointer pObject = (Pointer) b.getFieldValue("object");
+ if(pObject.isNotNull()) {
+ Structure objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
+ Object object = this.toObject(objectStructure);
+ if(object instanceof Spatial && ((Spatial) object).getParent()==null) {
+ result.attachChild((Spatial) object);
+ } else if(object instanceof Light) {
+ result.addLight((Light)object);
+ }
+ }
+ }
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ }
+ return result;
+ }
+
+ /**
+ * This method converts the given structure to a camera.
+ * @param structure
+ * structure of a camera
+ * @return camera's node
+ */
+ public Camera toCamera(Structure structure) throws BlenderFileException {
+ CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
+ if (cameraHelper.shouldBeLoaded(structure, blenderContext)) {
+ return cameraHelper.toCamera(structure);
+ }
+ return null;
+ }
+
+ /**
+ * This method converts the given structure to a light.
+ * @param structure
+ * structure of a light
+ * @return light's node
+ */
+ public Light toLight(Structure structure) throws BlenderFileException {
+ LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
+ if (lightHelper.shouldBeLoaded(structure, blenderContext)) {
+ return lightHelper.toLight(structure, blenderContext);
+ }
+ return null;
+ }
+
+ /**
+ * This method converts the given structure to a node.
+ * @param structure
+ * structure of an object
+ * @return object's node
+ */
+ public Object toObject(Structure structure) throws BlenderFileException {
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ if (objectHelper.shouldBeLoaded(structure, blenderContext)) {
+ return objectHelper.toObject(structure, blenderContext);
+ }
+ return null;
+ }
+
+ /**
+ * This method converts the given structure to a list of geometries.
+ * @param structure
+ * structure of a mesh
+ * @return list of geometries
+ */
+ public List<Geometry> toMesh(Structure structure) throws BlenderFileException {
+ MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
+ if (meshHelper.shouldBeLoaded(structure, blenderContext)) {
+ return meshHelper.toMesh(structure, blenderContext);
+ }
+ return null;
+ }
+
+ /**
+ * This method converts the given structure to a material.
+ * @param structure
+ * structure of a material
+ * @return material's node
+ */
+ public Material toMaterial(Structure structure) throws BlenderFileException {
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ if (materialHelper.shouldBeLoaded(structure, blenderContext)) {
+ return materialHelper.toMaterial(structure, blenderContext);
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the data read from the WORLD file block. The block contains data that can be stored as
+ * separate jme features and therefore cannot be returned as a single jME scene feature.
+ * @param structure
+ * the structure with WORLD block data
+ * @return data read from the WORLD block that can be added to the scene
+ */
+ public WorldData toWorldData(Structure structure) {
+ WorldData result = new WorldData();
+
+ // reading ambient light
+ AmbientLight ambientLight = new AmbientLight();
+ float ambr = ((Number) structure.getFieldValue("ambr")).floatValue();
+ float ambg = ((Number) structure.getFieldValue("ambg")).floatValue();
+ float ambb = ((Number) structure.getFieldValue("ambb")).floatValue();
+ ambientLight.setColor(new ColorRGBA(ambr, ambg, ambb, 0.0f));
+ result.setAmbientLight(ambientLight);
+
+ return result;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
new file mode 100644
index 0000000..1ed1c1c
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.animation.Skeleton;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.BlenderKey;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.constraints.Constraint;
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.scene.plugins.blender.file.DnaBlockData;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.materials.MaterialContext;
+import com.jme3.scene.plugins.blender.meshes.MeshContext;
+import com.jme3.scene.plugins.blender.modifiers.Modifier;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * The class that stores temporary data and manages it during loading the belnd
+ * file. This class is intended to be used in a single loading thread. It holds
+ * the state of loading operations.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class BlenderContext {
+ private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName());
+
+ /** The blender file version. */
+ private int blenderVersion;
+ /** The blender key. */
+ private BlenderKey blenderKey;
+ /** The header of the file block. */
+ private DnaBlockData dnaBlockData;
+ /** The input stream of the blend file. */
+ private BlenderInputStream inputStream;
+ /** The asset manager. */
+ private AssetManager assetManager;
+ /**
+ * A map containing the file block headers. The key is the old pointer
+ * address.
+ */
+ private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
+ /** A map containing the file block headers. The key is the block code. */
+ private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();
+ /**
+ * This map stores the loaded features by their old memory address. The
+ * first object in the value table is the loaded structure and the second -
+ * the structure already converted into proper data.
+ */
+ private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();
+ /**
+ * This map stores the loaded features by their name. Only features with ID
+ * structure can be stored here. The first object in the value table is the
+ * loaded structure and the second - the structure already converted into
+ * proper data.
+ */
+ private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
+ /** A stack that hold the parent structure of currently loaded feature. */
+ private Stack<Structure> parentStack = new Stack<Structure>();
+ /**
+ * A map storing loaded ipos. The key is the ipo's owner old memory address
+ * and the value is the ipo.
+ */
+ private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();
+ /** A list of modifiers for the specified object. */
+ protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
+ /** A list of constraints for the specified object. */
+ protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
+ /** Anim data loaded for features. */
+ private Map<Long, AnimData> animData = new HashMap<Long, AnimData>();
+ /** Loaded skeletons. */
+ private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
+ /** A map of mesh contexts. */
+ protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
+ /** A map of bone contexts. */
+ protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>();
+ /** A map of material contexts. */
+ protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
+ /** A map og helpers that perform loading. */
+ private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
+
+ /**
+ * This method sets the blender file version.
+ *
+ * @param blenderVersion
+ * the blender file version
+ */
+ public void setBlenderVersion(String blenderVersion) {
+ this.blenderVersion = Integer.parseInt(blenderVersion);
+ }
+
+ /**
+ * @return the blender file version
+ */
+ public int getBlenderVersion() {
+ return blenderVersion;
+ }
+
+ /**
+ * This method sets the blender key.
+ *
+ * @param blenderKey
+ * the blender key
+ */
+ public void setBlenderKey(BlenderKey blenderKey) {
+ this.blenderKey = blenderKey;
+ }
+
+ /**
+ * This method returns the blender key.
+ *
+ * @return the blender key
+ */
+ public BlenderKey getBlenderKey() {
+ return blenderKey;
+ }
+
+ /**
+ * This method sets the dna block data.
+ *
+ * @param dnaBlockData
+ * the dna block data
+ */
+ public void setBlockData(DnaBlockData dnaBlockData) {
+ this.dnaBlockData = dnaBlockData;
+ }
+
+ /**
+ * This method returns the dna block data.
+ *
+ * @return the dna block data
+ */
+ public DnaBlockData getDnaBlockData() {
+ return dnaBlockData;
+ }
+
+ /**
+ * This method returns the asset manager.
+ *
+ * @return the asset manager
+ */
+ public AssetManager getAssetManager() {
+ return assetManager;
+ }
+
+ /**
+ * This method sets the asset manager.
+ *
+ * @param assetManager
+ * the asset manager
+ */
+ public void setAssetManager(AssetManager assetManager) {
+ this.assetManager = assetManager;
+ }
+
+ /**
+ * This method returns the input stream of the blend file.
+ *
+ * @return the input stream of the blend file
+ */
+ public BlenderInputStream getInputStream() {
+ return inputStream;
+ }
+
+ /**
+ * This method sets the input stream of the blend file.
+ *
+ * @param inputStream
+ * the input stream of the blend file
+ */
+ public void setInputStream(BlenderInputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ /**
+ * This method adds a file block header to the map. Its old memory address
+ * is the key.
+ *
+ * @param oldMemoryAddress
+ * the address of the block header
+ * @param fileBlockHeader
+ * the block header to store
+ */
+ public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
+ fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
+ List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
+ if (headers == null) {
+ headers = new ArrayList<FileBlockHeader>();
+ fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
+ }
+ headers.add(fileBlockHeader);
+ }
+
+ /**
+ * This method returns the block header of a given memory address. If the
+ * header is not present then null is returned.
+ *
+ * @param oldMemoryAddress
+ * the address of the block header
+ * @return loaded header or null if it was not yet loaded
+ */
+ public FileBlockHeader getFileBlock(Long oldMemoryAddress) {
+ return fileBlockHeadersByOma.get(oldMemoryAddress);
+ }
+
+ /**
+ * This method returns a list of file blocks' headers of a specified code.
+ *
+ * @param code
+ * the code of file blocks
+ * @return a list of file blocks' headers of a specified code
+ */
+ public List<FileBlockHeader> getFileBlocks(Integer code) {
+ return fileBlockHeadersByCode.get(code);
+ }
+
+ /**
+ * This method clears the saved block headers stored in the features map.
+ */
+ public void clearFileBlocks() {
+ fileBlockHeadersByOma.clear();
+ fileBlockHeadersByCode.clear();
+ }
+
+ /**
+ * This method adds a helper instance to the helpers' map.
+ *
+ * @param <T>
+ * the type of the helper
+ * @param clazz
+ * helper's class definition
+ * @param helper
+ * the helper instance
+ */
+ public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
+ helpers.put(clazz.getSimpleName(), helper);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getHelper(Class<?> clazz) {
+ return (T) helpers.get(clazz.getSimpleName());
+ }
+
+ /**
+ * This method adds a loaded feature to the map. The key is its unique old
+ * memory address.
+ *
+ * @param oldMemoryAddress
+ * the address of the feature
+ * @param featureName
+ * the name of the feature
+ * @param structure
+ * the filled structure of the feature
+ * @param feature
+ * the feature we want to store
+ */
+ public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {
+ if (oldMemoryAddress == null || structure == null || feature == null) {
+ throw new IllegalArgumentException("One of the given arguments is null!");
+ }
+ Object[] storedData = new Object[] { structure, feature };
+ loadedFeatures.put(oldMemoryAddress, storedData);
+ if (featureName != null) {
+ loadedFeaturesByName.put(featureName, storedData);
+ }
+ }
+
+ /**
+ * This method returns the feature of a given memory address. If the feature
+ * is not yet loaded then null is returned.
+ *
+ * @param oldMemoryAddress
+ * the address of the feature
+ * @param loadedFeatureDataType
+ * the type of data we want to retreive it can be either filled
+ * structure or already converted feature
+ * @return loaded feature or null if it was not yet loaded
+ */
+ public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {
+ Object[] result = loadedFeatures.get(oldMemoryAddress);
+ if (result != null) {
+ return result[loadedFeatureDataType.getIndex()];
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the feature of a given name. If the feature is not
+ * yet loaded then null is returned.
+ *
+ * @param featureName
+ * the name of the feature
+ * @param loadedFeatureDataType
+ * the type of data we want to retreive it can be either filled
+ * structure or already converted feature
+ * @return loaded feature or null if it was not yet loaded
+ */
+ public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {
+ Object[] result = loadedFeaturesByName.get(featureName);
+ if (result != null) {
+ return result[loadedFeatureDataType.getIndex()];
+ }
+ return null;
+ }
+
+ /**
+ * This method clears the saved features stored in the features map.
+ */
+ public void clearLoadedFeatures() {
+ loadedFeatures.clear();
+ }
+
+ /**
+ * This method adds the structure to the parent stack.
+ *
+ * @param parent
+ * the structure to be added to the stack
+ */
+ public void pushParent(Structure parent) {
+ parentStack.push(parent);
+ }
+
+ /**
+ * This method removes the structure from the top of the parent's stack.
+ *
+ * @return the structure that was removed from the stack
+ */
+ public Structure popParent() {
+ try {
+ return parentStack.pop();
+ } catch (EmptyStackException e) {
+ return null;
+ }
+ }
+
+ /**
+ * This method retreives the structure at the top of the parent's stack but
+ * does not remove it.
+ *
+ * @return the structure from the top of the stack
+ */
+ public Structure peekParent() {
+ try {
+ return parentStack.peek();
+ } catch (EmptyStackException e) {
+ return null;
+ }
+ }
+
+ /**
+ * This method adds new ipo curve for the feature.
+ *
+ * @param ownerOMA
+ * the OMA of blender feature that owns the ipo
+ * @param ipo
+ * the ipo to be added
+ */
+ public void addIpo(Long ownerOMA, Ipo ipo) {
+ loadedIpos.put(ownerOMA, ipo);
+ }
+
+ /**
+ * This method removes the ipo curve from the feature.
+ *
+ * @param ownerOma
+ * the OMA of blender feature that owns the ipo
+ */
+ public Ipo removeIpo(Long ownerOma) {
+ return loadedIpos.remove(ownerOma);
+ }
+
+ /**
+ * This method returns the ipo curve of the feature.
+ *
+ * @param ownerOMA
+ * the OMA of blender feature that owns the ipo
+ */
+ public Ipo getIpo(Long ownerOMA) {
+ return loadedIpos.get(ownerOMA);
+ }
+
+ /**
+ * This method adds a new modifier to the list.
+ *
+ * @param ownerOMA
+ * the owner's old memory address
+ * @param modifier
+ * the object's modifier
+ */
+ public void addModifier(Long ownerOMA, Modifier modifier) {
+ List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
+ if (objectModifiers == null) {
+ objectModifiers = new ArrayList<Modifier>();
+ this.modifiers.put(ownerOMA, objectModifiers);
+ }
+ objectModifiers.add(modifier);
+ }
+
+ /**
+ * This method returns modifiers for the object specified by its old memory
+ * address and the modifier type. If no modifiers are found - empty list is
+ * returned. If the type is null - all modifiers for the object are
+ * returned.
+ *
+ * @param objectOMA
+ * object's old memory address
+ * @param type
+ * the type of the modifier
+ * @return the list of object's modifiers
+ */
+ public List<Modifier> getModifiers(Long objectOMA, String type) {
+ List<Modifier> result = new ArrayList<Modifier>();
+ List<Modifier> readModifiers = modifiers.get(objectOMA);
+ if (readModifiers != null && readModifiers.size() > 0) {
+ for (Modifier modifier : readModifiers) {
+ if (type == null || type.isEmpty() || modifier.getType().equals(type)) {
+ result.add(modifier);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method adds a new modifier to the list.
+ *
+ * @param ownerOMA
+ * the owner's old memory address
+ * @param constraints
+ * the object's constraints
+ */
+ public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
+ List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
+ if (objectConstraints == null) {
+ objectConstraints = new ArrayList<Constraint>();
+ this.constraints.put(ownerOMA, objectConstraints);
+ }
+ objectConstraints.addAll(constraints);
+ }
+
+ /**
+ * This method returns constraints for the object specified by its old
+ * memory address. If no modifiers are found - <b>null</b> is returned.
+ *
+ * @param objectOMA
+ * object's old memory address
+ * @return the list of object's modifiers or null
+ */
+ public List<Constraint> getConstraints(Long objectOMA) {
+ return objectOMA == null ? null : constraints.get(objectOMA);
+ }
+
+ /**
+ * This method sets the anim data for the specified OMA of its owner.
+ *
+ * @param ownerOMA
+ * the owner's old memory address
+ * @param animData
+ * the animation data for the feature specified by ownerOMA
+ */
+ public void setAnimData(Long ownerOMA, AnimData animData) {
+ this.animData.put(ownerOMA, animData);
+ }
+
+ /**
+ * This method returns the animation data for the specified owner.
+ *
+ * @param ownerOMA
+ * the old memory address of the animation data owner
+ * @return the animation data or null if none exists
+ */
+ public AnimData getAnimData(Long ownerOMA) {
+ return this.animData.get(ownerOMA);
+ }
+
+ /**
+ * This method sets the skeleton for the specified OMA of its owner.
+ *
+ * @param skeletonOMA
+ * the skeleton's old memory address
+ * @param skeleton
+ * the skeleton specified by the given OMA
+ */
+ public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
+ this.skeletons.put(skeletonOMA, skeleton);
+ }
+
+ /**
+ * This method returns the skeleton for the specified OMA of its owner.
+ *
+ * @param skeletonOMA
+ * the skeleton's old memory address
+ * @return the skeleton specified by the given OMA
+ */
+ public Skeleton getSkeleton(Long skeletonOMA) {
+ return this.skeletons.get(skeletonOMA);
+ }
+
+ /**
+ * This method sets the mesh context for the given mesh old memory address.
+ * If the context is already set it will be replaced.
+ *
+ * @param meshOMA
+ * the mesh's old memory address
+ * @param meshContext
+ * the mesh's context
+ */
+ public void setMeshContext(Long meshOMA, MeshContext meshContext) {
+ this.meshContexts.put(meshOMA, meshContext);
+ }
+
+ /**
+ * This method returns the mesh context for the given mesh old memory
+ * address. If no context exists then <b>null</b> is returned.
+ *
+ * @param meshOMA
+ * the mesh's old memory address
+ * @return mesh's context
+ */
+ public MeshContext getMeshContext(Long meshOMA) {
+ return this.meshContexts.get(meshOMA);
+ }
+
+ /**
+ * This method sets the bone context for the given bone old memory address.
+ * If the context is already set it will be replaced.
+ *
+ * @param boneOMA
+ * the bone's old memory address
+ * @param boneContext
+ * the bones's context
+ */
+ public void setBoneContext(Long boneOMA, BoneContext boneContext) {
+ this.boneContexts.put(boneOMA, boneContext);
+ }
+
+ /**
+ * This method returns the bone context for the given bone old memory
+ * address. If no context exists then <b>null</b> is returned.
+ *
+ * @param boneOMA
+ * the bone's old memory address
+ * @return bone's context
+ */
+ public BoneContext getBoneContext(Long boneOMA) {
+ return boneContexts.get(boneOMA);
+ }
+
+ /**
+ * This method sets the material context for the given material. If the
+ * context is already set it will be replaced.
+ *
+ * @param material
+ * the material
+ * @param materialContext
+ * the material's context
+ */
+ public void setMaterialContext(Material material, MaterialContext materialContext) {
+ this.materialContexts.put(material, materialContext);
+ }
+
+ /**
+ * This method returns the material context for the given material. If no
+ * context exists then <b>null</b> is returned.
+ *
+ * @param material
+ * the material
+ * @return material's context
+ */
+ public MaterialContext getMaterialContext(Material material) {
+ return materialContexts.get(material);
+ }
+
+ /**
+ * This metod returns the default material.
+ *
+ * @return the default material
+ */
+ public synchronized Material getDefaultMaterial() {
+ if (blenderKey.getDefaultMaterial() == null) {
+ Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
+ blenderKey.setDefaultMaterial(defaultMaterial);
+ }
+ return blenderKey.getDefaultMaterial();
+ }
+
+ public void dispose() {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ }
+ loadedFeatures.clear();
+ loadedFeaturesByName.clear();
+ }
+
+ /**
+ * This enum defines what loaded data type user wants to retreive. It can be
+ * either filled structure or already converted data.
+ *
+ * @author Marcin Roguski
+ */
+ public static enum LoadedFeatureDataType {
+
+ LOADED_STRUCTURE(0), LOADED_FEATURE(1);
+ private int index;
+
+ private LoadedFeatureDataType(int index) {
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
new file mode 100644
index 0000000..3c81ba6
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.BlenderKey;
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.asset.BlenderKey.LoadingResults;
+import com.jme3.asset.BlenderKey.WorldData;
+import com.jme3.asset.ModelKey;
+import com.jme3.light.Light;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
+import com.jme3.scene.plugins.blender.animations.IpoHelper;
+import com.jme3.scene.plugins.blender.cameras.CameraHelper;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
+import com.jme3.scene.plugins.blender.curves.CurvesHelper;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.lights.LightHelper;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+import com.jme3.scene.plugins.blender.meshes.MeshHelper;
+import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
+import com.jme3.scene.plugins.blender.textures.TextureHelper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class BlenderLoader extends AbstractBlenderLoader {
+
+ private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
+
+ /** The blocks read from the file. */
+ protected List<FileBlockHeader> blocks;
+
+ @Override
+ public Spatial load(AssetInfo assetInfo) throws IOException {
+ try {
+ this.setup(assetInfo);
+
+ BlenderKey blenderKey = blenderContext.getBlenderKey();
+ LoadingResults loadingResults = blenderKey.prepareLoadingResults();
+ WorldData worldData = null;// a set of data used in different scene aspects
+ for (FileBlockHeader block : blocks) {
+ switch (block.getCode()) {
+ case FileBlockHeader.BLOCK_OB00:// Object
+ Object object = this.toObject(block.getStructure(blenderContext));
+ if (object instanceof Node) {
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
+ LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
+ if (this.isRootObject(loadingResults, (Node)object)) {
+ loadingResults.addObject((Node) object);
+ }
+ }
+ } else if (object instanceof Camera) {
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0) {
+ loadingResults.addCamera((Camera) object);
+ }
+ } else if (object instanceof Light) {
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+ loadingResults.addLight((Light) object);
+ }
+ }
+ break;
+ case FileBlockHeader.BLOCK_MA00:// Material
+ if (blenderKey.isLoadUnlinkedAssets() && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
+ loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
+ }
+ break;
+ case FileBlockHeader.BLOCK_SC00:// Scene
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.SCENES) != 0) {
+ loadingResults.addScene(this.toScene(block.getStructure(blenderContext)));
+ }
+ break;
+ case FileBlockHeader.BLOCK_WO00:// World
+ if (blenderKey.isLoadUnlinkedAssets() && worldData == null) {// onlu one world data is used
+ Structure worldStructure = block.getStructure(blenderContext);
+ String worldName = worldStructure.getName();
+ if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
+ worldData = this.toWorldData(worldStructure);
+ if ((blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
+ loadingResults.addLight(worldData.getAmbientLight());
+ }
+ }
+ }
+ break;
+ }
+ }
+ blenderContext.dispose();
+ return loadingResults;
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ }
+ return null;
+ }
+
+ /**
+ * This method indicates if the given spatial is a root object. It means it
+ * has no parent or is directly attached to one of the already loaded scene
+ * nodes.
+ *
+ * @param loadingResults
+ * loading results containing the scene nodes
+ * @param spatial
+ * spatial object
+ * @return <b>true</b> if the given spatial is a root object and
+ * <b>false</b> otherwise
+ */
+ protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) {
+ if(spatial.getParent() == null) {
+ return true;
+ }
+ for(Node scene : loadingResults.getScenes()) {
+ if(spatial.getParent().equals(scene)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method sets up the loader.
+ * @param assetInfo
+ * the asset info
+ * @throws BlenderFileException
+ * an exception is throw when something wrong happens with blender file
+ */
+ protected void setup(AssetInfo assetInfo) throws BlenderFileException {
+ // registering loaders
+ ModelKey modelKey = (ModelKey) assetInfo.getKey();
+ BlenderKey blenderKey;
+ if (modelKey instanceof BlenderKey) {
+ blenderKey = (BlenderKey) modelKey;
+ } else {
+ blenderKey = new BlenderKey(modelKey.getName());
+ blenderKey.setAssetRootPath(modelKey.getFolder());
+ }
+
+ // opening stream
+ BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream(), assetInfo.getManager());
+
+ // reading blocks
+ blocks = new ArrayList<FileBlockHeader>();
+ FileBlockHeader fileBlock;
+ blenderContext = new BlenderContext();
+ blenderContext.setBlenderVersion(inputStream.getVersionNumber());
+ blenderContext.setAssetManager(assetInfo.getManager());
+ blenderContext.setInputStream(inputStream);
+ blenderContext.setBlenderKey(blenderKey);
+
+ // creating helpers
+ blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext, blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+ blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
+
+ // setting additional data to helpers
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());
+
+ // reading the blocks (dna block is automatically saved in the blender context when found)//TODO: zmienić to
+ FileBlockHeader sceneFileBlock = null;
+ do {
+ fileBlock = new FileBlockHeader(inputStream, blenderContext);
+ if (!fileBlock.isDnaBlock()) {
+ blocks.add(fileBlock);
+ // save the scene's file block
+ if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00 && blenderKey.getLayersToLoad() < 0) {
+ sceneFileBlock = fileBlock;
+ }
+ }
+ } while (!fileBlock.isLastBlock());
+ // VERIFY LAYERS TO BE LOADED BEFORE LOADING FEATURES
+ if (sceneFileBlock != null) {
+ int lay = ((Number) sceneFileBlock.getStructure(blenderContext).getFieldValue("lay")).intValue();
+ blenderContext.getBlenderKey().setLayersToLoad(lay);// load only current layer
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
new file mode 100644
index 0000000..ec117d8
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/BlenderModelLoader.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.BlenderKey;
+import com.jme3.light.Light;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
+ * @author Marcin Roguski
+ */
+public class BlenderModelLoader extends BlenderLoader {
+
+ private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName());
+
+ @Override
+ public Spatial load(AssetInfo assetInfo) throws IOException {
+ try {
+ this.setup(assetInfo);
+
+ BlenderKey blenderKey = blenderContext.getBlenderKey();
+ Node modelRoot = new Node(blenderKey.getName());
+
+ for (FileBlockHeader block : blocks) {
+ if (block.getCode() == FileBlockHeader.BLOCK_OB00) {
+ Object object = this.toObject(block.getStructure(blenderContext));
+ if (object instanceof Node) {
+ LOGGER.log(Level.INFO, "{0}: {1}--> {2}", new Object[]{((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName()});
+ if (((Node) object).getParent() == null) {
+ modelRoot.attachChild( (Node) object );
+ }
+ }else if (object instanceof Light){
+ modelRoot.addLight( (Light) object );
+ }
+ }
+ }
+ blenderContext.dispose();
+ return modelRoot;
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ }
+ return null;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
new file mode 100644
index 0000000..120e556
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.animations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.math.Matrix4f;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.curves.BezierCurve;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * This class defines the methods to calculate certain aspects of animation and
+ * armature functionalities.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class ArmatureHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
+
+ /** A map of bones and their old memory addresses. */
+ private Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();
+
+ /**
+ * This constructor parses the given blender version and stores the result.
+ * Some functionalities may differ in different blender versions.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method builds the object's bones structure.
+ *
+ * @param boneStructure
+ * the structure containing the bones' data
+ * @param parent
+ * the parent bone
+ * @param result
+ * the list where the newly created bone will be added
+ * @param bonesPoseChannels
+ * a map of bones poses channels
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when there is problem with the blender
+ * file
+ */
+ public void buildBones(Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+ BoneContext bc = new BoneContext(boneStructure, arbt, bonesPoseChannels, blenderContext);
+ bc.buildBone(result, bonesOMAs, blenderContext);
+ }
+
+ /**
+ * This method returns the old memory address of a bone. If the bone does
+ * not exist in the blend file - zero is returned.
+ *
+ * @param bone
+ * the bone whose old memory address we seek
+ * @return the old memory address of the given bone
+ */
+ public Long getBoneOMA(Bone bone) {
+ Long result = bonesOMAs.get(bone);
+ if (result == null) {
+ result = Long.valueOf(0);
+ }
+ return result;
+ }
+
+ /**
+ * This method returns a map where the key is the object's group index that
+ * is used by a bone and the key is the bone index in the armature.
+ *
+ * @param defBaseStructure
+ * a bPose structure of the object
+ * @return bone group-to-index map
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ Map<Integer, Integer> result = null;
+ if (skeleton.getBoneCount() != 0) {
+ result = new HashMap<Integer, Integer>();
+ List<Structure> deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
+ int groupIndex = 0;
+ for (Structure deformGroup : deformGroups) {
+ String deformGroupName = deformGroup.getFieldValue("name").toString();
+ Integer boneIndex = this.getBoneIndex(skeleton, deformGroupName);
+ if (boneIndex != null) {
+ result.put(Integer.valueOf(groupIndex), boneIndex);
+ }
+ ++groupIndex;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+
+ /**
+ * This method retuns the bone tracks for animation.
+ *
+ * @param actionStructure
+ * the structure containing the tracks
+ * @param blenderContext
+ * the blender context
+ * @return a list of tracks for the specified animation
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blend
+ * file
+ */
+ public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ if (blenderVersion < 250) {
+ return this.getTracks249(actionStructure, skeleton, blenderContext);
+ } else {
+ return this.getTracks250(actionStructure, skeleton, blenderContext);
+ }
+ }
+
+ /**
+ * This method retuns the bone tracks for animation for blender version 2.50
+ * and higher.
+ *
+ * @param actionStructure
+ * the structure containing the tracks
+ * @param blenderContext
+ * the blender context
+ * @return a list of tracks for the specified animation
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blend
+ * file
+ */
+ private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.INFO, "Getting tracks!");
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ int fps = blenderContext.getBlenderKey().getFps();
+ Structure groups = (Structure) actionStructure.getFieldValue("groups");
+ List<Structure> actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
+ List<BoneTrack> tracks = new ArrayList<BoneTrack>();
+ for (Structure actionGroup : actionGroups) {
+ String name = actionGroup.getFieldValue("name").toString();
+ int boneIndex = this.getBoneIndex(skeleton, name);
+ if (boneIndex >= 0) {
+ List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
+ BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
+ int channelCounter = 0;
+ for (Structure c : channels) {
+ int type = ipoHelper.getCurveType(c, blenderContext);
+ Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
+ List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+ bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
+ }
+
+ Ipo ipo = new Ipo(bezierCurves, fixUpAxis);
+ tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, 0, ipo.getLastFrame(), fps, false));
+ }
+ }
+ return tracks.toArray(new BoneTrack[tracks.size()]);
+ }
+
+ /**
+ * This method retuns the bone tracks for animation for blender version 2.49
+ * (and probably several lower versions too).
+ *
+ * @param actionStructure
+ * the structure containing the tracks
+ * @param blenderContext
+ * the blender context
+ * @return a list of tracks for the specified animation
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blend
+ * file
+ */
+ private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.INFO, "Getting tracks!");
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ int fps = blenderContext.getBlenderKey().getFps();
+ Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
+ List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);// bActionChannel
+ List<BoneTrack> tracks = new ArrayList<BoneTrack>();
+ for (Structure bActionChannel : actionChannels) {
+ String name = bActionChannel.getFieldValue("name").toString();
+ int boneIndex = this.getBoneIndex(skeleton, name);
+ if (boneIndex >= 0) {
+ Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
+ if (!p.isNull()) {
+ Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
+ Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
+ tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, 0, ipo.getLastFrame(), fps, false));
+ }
+ }
+ }
+ return tracks.toArray(new BoneTrack[tracks.size()]);
+ }
+
+ /**
+ * This method returns the index of the bone in the given skeleton.
+ *
+ * @param skeleton
+ * the skeleton
+ * @param boneName
+ * the name of the bone
+ * @return the index of the bone
+ */
+ private int getBoneIndex(Skeleton skeleton, String boneName) {
+ int result = -1;
+ for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
+ if (boneName.equals(skeleton.getBone(i).getName())) {
+ result = i;
+ }
+ }
+ return result;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
new file mode 100644
index 0000000..1ef9bae
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
@@ -0,0 +1,204 @@
+package com.jme3.scene.plugins.blender.animations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.jme3.animation.Bone;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+
+/**
+ * This class holds the basic data that describes a bone.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class BoneContext {
+ /** The structure of the bone. */
+ private Structure boneStructure;
+ /** Bone's pose channel structure. */
+ private Structure poseChannel;
+ /** Bone's name. */
+ private String boneName;
+ /** This variable indicates if the Y axis should be the UP axis. */
+ private boolean fixUpAxis;
+ /** The bone's armature matrix. */
+ private Matrix4f armatureMatrix;
+ /** The parent context. */
+ private BoneContext parent;
+ /** The children of this context. */
+ private List<BoneContext> children = new ArrayList<BoneContext>();
+ /** Created bone (available after calling 'buildBone' method). */
+ private Bone bone;
+ /** Bone's pose transform (available after calling 'buildBone' method). */
+ private Transform poseTransform = new Transform();
+ /** The bone's rest matrix. */
+ private Matrix4f restMatrix;
+ /** Bone's total inverse transformation. */
+ private Matrix4f inverseTotalTransformation;
+ /** Bone's parent inverse matrix. */
+ private Matrix4f inverseParentMatrix;
+
+ /**
+ * Constructor. Creates the basic set of bone's data.
+ *
+ * @param boneStructure
+ * the bone's structure
+ * @param objectToArmatureMatrix
+ * object-to-armature transformation matrix
+ * @param bonesPoseChannels
+ * a map of pose channels for each bone OMA
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when problem with blender data reading
+ * occurs
+ */
+ public BoneContext(Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+ this(boneStructure, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext);
+ }
+
+ /**
+ * Constructor. Creates the basic set of bone's data.
+ *
+ * @param boneStructure
+ * the bone's structure
+ * @param parent
+ * bone's parent (null if the bone is the root bone)
+ * @param objectToArmatureMatrix
+ * object-to-armature transformation matrix
+ * @param bonesPoseChannels
+ * a map of pose channels for each bone OMA
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when problem with blender data reading
+ * occurs
+ */
+ private BoneContext(Structure boneStructure, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
+ this.parent = parent;
+ this.boneStructure = boneStructure;
+ boneName = boneStructure.getFieldValue("name").toString();
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true);
+
+ fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
+ this.computeRestMatrix(objectToArmatureMatrix);
+ List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
+ for (Structure child : childbase) {
+ this.children.add(new BoneContext(child, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext));
+ }
+
+ poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
+
+ blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
+ }
+
+ /**
+ * This method computes the rest matrix for the bone.
+ *
+ * @param objectToArmatureMatrix
+ * object-to-armature transformation matrix
+ */
+ private void computeRestMatrix(Matrix4f objectToArmatureMatrix) {
+ if (parent != null) {
+ inverseParentMatrix = parent.inverseTotalTransformation.clone();
+ } else if (fixUpAxis) {
+ inverseParentMatrix = objectToArmatureMatrix.clone();
+ } else {
+ inverseParentMatrix = Matrix4f.IDENTITY.clone();
+ }
+
+ restMatrix = armatureMatrix.clone();
+ inverseTotalTransformation = restMatrix.invert();
+
+ restMatrix = inverseParentMatrix.mult(restMatrix);
+
+ for (BoneContext child : this.children) {
+ child.computeRestMatrix(objectToArmatureMatrix);
+ }
+ }
+
+ /**
+ * This method computes the pose transform for the bone.
+ */
+ @SuppressWarnings("unchecked")
+ private void computePoseTransform() {
+ DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc");
+ DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size");
+ DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat");
+ if (fixUpAxis) {
+ poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
+ poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue()));
+ poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
+ } else {
+ poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
+ poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
+ poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
+ }
+
+ Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+ localTransform.setScale(bone.getLocalScale());
+ localTransform.getTranslation().addLocal(poseTransform.getTranslation());
+ localTransform.getRotation().multLocal(poseTransform.getRotation());
+ localTransform.getScale().multLocal(poseTransform.getScale());
+
+ poseTransform.set(localTransform);
+ }
+
+ /**
+ * This method builds the bone. It recursively builds the bone's children.
+ *
+ * @param bones
+ * a list of bones where the newly created bone will be added
+ * @param boneOMAs
+ * the map between bone and its old memory address
+ * @param blenderContext
+ * the blender context
+ * @return newly created bone
+ */
+ public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) {
+ Long boneOMA = boneStructure.getOldMemoryAddress();
+ bone = new Bone(boneName);
+ bones.add(bone);
+ boneOMAs.put(bone, boneOMA);
+ blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
+
+ Matrix4f pose = this.restMatrix.clone();
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+
+ Vector3f poseLocation = pose.toTranslationVector();
+ Quaternion rotation = pose.toRotationQuat();
+ Vector3f scale = objectHelper.getScale(pose);
+
+ bone.setBindTransforms(poseLocation, rotation, scale);
+ for (BoneContext child : children) {
+ bone.addChild(child.buildBone(bones, boneOMAs, blenderContext));
+ }
+
+ this.computePoseTransform();
+
+ return bone;
+ }
+
+ /**
+ * @return bone's pose transformation
+ */
+ public Transform getPoseTransform() {
+ return poseTransform;
+ }
+
+ /**
+ * @return built bone (available after calling 'buildBone' method)
+ */
+ public Bone getBone() {
+ return bone;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java
new file mode 100644
index 0000000..90994cc
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/CalculationBone.java
@@ -0,0 +1,128 @@
+package com.jme3.scene.plugins.blender.animations;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.util.Arrays;
+
+/**
+ * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class CalculationBone extends Node {
+ private Bone bone;
+ /** The bone's tracks. Will be altered at the end of calculation process. */
+ private BoneTrack track;
+ /** The starting position of the bone. */
+ private Vector3f startTranslation;
+ /** The starting rotation of the bone. */
+ private Quaternion startRotation;
+ /** The starting scale of the bone. */
+ private Vector3f startScale;
+ private Vector3f[] translations;
+ private Quaternion[] rotations;
+ private Vector3f[] scales;
+
+ public CalculationBone(Bone bone, int boneFramesCount) {
+ this.bone = bone;
+ this.startRotation = bone.getModelSpaceRotation().clone();
+ this.startTranslation = bone.getModelSpacePosition().clone();
+ this.startScale = bone.getModelSpaceScale().clone();
+ this.reset();
+ if(boneFramesCount > 0) {
+ this.translations = new Vector3f[boneFramesCount];
+ this.rotations = new Quaternion[boneFramesCount];
+ this.scales = new Vector3f[boneFramesCount];
+
+ Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
+ Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
+ Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
+ }
+ }
+
+ /**
+ * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
+ * @param bone
+ * the bone this class will imitate
+ * @param track
+ * the bone's tracks
+ */
+ public CalculationBone(Bone bone, BoneTrack track) {
+ this(bone, 0);
+ this.track = track;
+ this.translations = track.getTranslations();
+ this.rotations = track.getRotations();
+ this.scales = track.getScales();
+ }
+
+ public int getBoneFramesCount() {
+ return this.translations==null ? 0 : this.translations.length;
+ }
+
+ /**
+ * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
+ * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
+ * to be 1 point up along Y axis (scale is applied if set to != 1.0);
+ * @return the end point of this bone
+ */
+ //TODO: set to Z axis if user defined it this way
+ public Vector3f getEndPoint() {
+ if (this.getParent() == null) {
+ return new Vector3f(0, this.getLocalScale().y, 0);
+ } else {
+ Node parent = this.getParent();
+ return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
+ }
+ }
+
+ /**
+ * This method resets the calculation bone to the starting position.
+ */
+ public void reset() {
+ this.setLocalTranslation(startTranslation);
+ this.setLocalRotation(startRotation);
+ this.setLocalScale(startScale);
+ }
+
+ @Override
+ public int attachChild(Spatial child) {
+ if (this.getChildren() != null && this.getChildren().size() > 1) {
+ throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
+ }
+ return super.attachChild(child);
+ }
+
+ public Spatial rotate(Quaternion rot, int frame) {
+ Spatial spatial = super.rotate(rot);
+ this.updateWorldTransforms();
+ if (this.getChildren() != null && this.getChildren().size() > 0) {
+ CalculationBone child = (CalculationBone) this.getChild(0);
+ child.updateWorldTransforms();
+ }
+ rotations[frame].set(this.getLocalRotation());
+ translations[frame].set(this.getLocalTranslation());
+ if (scales != null) {
+ scales[frame].set(this.getLocalScale());
+ }
+ return spatial;
+ }
+
+ public void applyCalculatedTracks() {
+ if(track != null) {
+ track.setKeyframes(track.getTimes(), translations, rotations, scales);
+ } else {
+ bone.setUserControl(true);
+ bone.setUserTransforms(translations[0], rotations[0], scales[0]);
+ bone.setUserControl(false);
+ bone.updateWorldVectors();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
new file mode 100644
index 0000000..58ce48c
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
@@ -0,0 +1,236 @@
+package com.jme3.scene.plugins.blender.animations;
+
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.SpatialTrack;
+import com.jme3.animation.Track;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.curves.BezierCurve;
+
+/**
+ * This class is used to calculate bezier curves value for the given frames. The
+ * Ipo (interpolation object) consists of several b-spline curves (connected 3rd
+ * degree bezier curves) of a different type.
+ *
+ * @author Marcin Roguski
+ */
+public class Ipo {
+
+ public static final int AC_LOC_X = 1;
+ public static final int AC_LOC_Y = 2;
+ public static final int AC_LOC_Z = 3;
+ public static final int OB_ROT_X = 7;
+ public static final int OB_ROT_Y = 8;
+ public static final int OB_ROT_Z = 9;
+ public static final int AC_SIZE_X = 13;
+ public static final int AC_SIZE_Y = 14;
+ public static final int AC_SIZE_Z = 15;
+ public static final int AC_QUAT_W = 25;
+ public static final int AC_QUAT_X = 26;
+ public static final int AC_QUAT_Y = 27;
+ public static final int AC_QUAT_Z = 28;
+
+ /** A list of bezier curves for this interpolation object. */
+ private BezierCurve[] bezierCurves;
+ /** Each ipo contains one bone track. */
+ private Track calculatedTrack;
+ /** This variable indicates if the Y asxis is the UP axis or not. */
+ protected boolean fixUpAxis;
+
+ /**
+ * Constructor. Stores the bezier curves.
+ *
+ * @param bezierCurves
+ * a table of bezier curves
+ */
+ public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis) {
+ this.bezierCurves = bezierCurves;
+ this.fixUpAxis = fixUpAxis;
+ }
+
+ /**
+ * This method calculates the ipo value for the first curve.
+ *
+ * @param frame
+ * the frame for which the value is calculated
+ * @return calculated ipo value
+ */
+ public float calculateValue(int frame) {
+ return this.calculateValue(frame, 0);
+ }
+
+ /**
+ * This method calculates the ipo value for the curve of the specified
+ * index. Make sure you do not exceed the curves amount. Alway chech the
+ * amount of curves before calling this method.
+ *
+ * @param frame
+ * the frame for which the value is calculated
+ * @param curveIndex
+ * the index of the curve
+ * @return calculated ipo value
+ */
+ public float calculateValue(int frame, int curveIndex) {
+ return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
+ }
+
+ /**
+ * This method returns the curves amount.
+ *
+ * @return the curves amount
+ */
+ public int getCurvesAmount() {
+ return bezierCurves.length;
+ }
+
+ /**
+ * This method returns the frame where last bezier triple center point of
+ * the specified bezier curve is located.
+ *
+ * @return the frame number of the last defined bezier triple point for the
+ * specified ipo
+ */
+ public int getLastFrame() {
+ int result = 1;
+ for (int i = 0; i < bezierCurves.length; ++i) {
+ int tempResult = bezierCurves[i].getLastFrame();
+ if (tempResult > result) {
+ result = tempResult;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method calculates the value of the curves as a bone track between
+ * the specified frames.
+ *
+ * @param targetIndex
+ * the index of the target for which the method calculates the
+ * tracks IMPORTANT! Aet to -1 (or any negative number) if you
+ * want to load spatial animation.
+ * @param startFrame
+ * the firs frame of tracks (inclusive)
+ * @param stopFrame
+ * the last frame of the tracks (inclusive)
+ * @param fps
+ * frame rate (frames per second)
+ * @param spatialTrack
+ * this flag indicates if the track belongs to a spatial or to a
+ * bone; the diference is important because it appears that bones
+ * in blender have the same type of coordinate system (Y as UP)
+ * as jme while other features have different one (Z is UP)
+ * @return bone track for the specified bone
+ */
+ public Track calculateTrack(int targetIndex, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
+ if (calculatedTrack == null) {
+ // preparing data for track
+ int framesAmount = stopFrame - startFrame;
+ float start = (startFrame - 1.0f) / fps;
+ float timeBetweenFrames = 1.0f / fps;
+
+ float[] times = new float[framesAmount + 1];
+ Vector3f[] translations = new Vector3f[framesAmount + 1];
+ float[] translation = new float[3];
+ Quaternion[] rotations = new Quaternion[framesAmount + 1];
+ float[] quaternionRotation = new float[4];
+ float[] objectRotation = new float[3];
+ Vector3f[] scales = new Vector3f[framesAmount + 1];
+ float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
+ float degreeToRadiansFactor = FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
+
+ // calculating track data
+ for (int frame = startFrame; frame <= stopFrame; ++frame) {
+ int index = frame - startFrame;
+ times[index] = start + (frame - 1) * timeBetweenFrames;
+ for (int j = 0; j < bezierCurves.length; ++j) {
+ double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
+ switch (bezierCurves[j].getType()) {
+ // LOCATION
+ case AC_LOC_X:
+ translation[0] = (float) value;
+ break;
+ case AC_LOC_Y:
+ if (fixUpAxis && spatialTrack) {
+ translation[2] = (float) -value;
+ } else {
+ translation[1] = (float) value;
+ }
+ break;
+ case AC_LOC_Z:
+ translation[fixUpAxis && spatialTrack ? 1 : 2] = (float) value;
+ break;
+
+ // ROTATION (used with object animation)
+ // the value here is in degrees divided by 10 (so in
+ // example: 9 = PI/2)
+ case OB_ROT_X:
+ objectRotation[0] = (float) value * degreeToRadiansFactor;
+ break;
+ case OB_ROT_Y:
+ if (fixUpAxis) {
+ objectRotation[2] = (float) -value * degreeToRadiansFactor;
+ } else {
+ objectRotation[1] = (float) value * degreeToRadiansFactor;
+ }
+ break;
+ case OB_ROT_Z:
+ objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
+ break;
+
+ // SIZE
+ case AC_SIZE_X:
+ scale[0] = (float) value;
+ break;
+ case AC_SIZE_Y:
+ if (fixUpAxis && spatialTrack) {
+ scale[2] = (float) value;
+ } else {
+ scale[1] = (float) value;
+ }
+ break;
+ case AC_SIZE_Z:
+ scale[fixUpAxis && spatialTrack ? 1 : 2] = (float) value;
+ break;
+
+ // QUATERNION ROTATION (used with bone animation), dunno
+ // why but here we shouldn't check the
+ // spatialTrack flag value
+ case AC_QUAT_W:
+ quaternionRotation[3] = (float) value;
+ break;
+ case AC_QUAT_X:
+ quaternionRotation[0] = (float) value;
+ break;
+ case AC_QUAT_Y:
+ if (fixUpAxis) {
+ quaternionRotation[2] = -(float) value;
+ } else {
+ quaternionRotation[1] = (float) value;
+ }
+ break;
+ case AC_QUAT_Z:
+ if (fixUpAxis) {
+ quaternionRotation[1] = (float) value;
+ } else {
+ quaternionRotation[2] = (float) value;
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown ipo curve type: " + bezierCurves[j].getType());
+ }
+ }
+ translations[index] = new Vector3f(translation[0], translation[1], translation[2]);
+ rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
+ scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
+ }
+ if (spatialTrack) {
+ calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
+ } else {
+ calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
+ }
+ }
+ return calculatedTrack;
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
new file mode 100644
index 0000000..ea0a207
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
@@ -0,0 +1,199 @@
+package com.jme3.scene.plugins.blender.animations;
+
+import java.util.List;
+
+import com.jme3.animation.BoneTrack;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.curves.BezierCurve;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * This class helps to compute values from interpolation curves for features
+ * like animation or constraint influence. The curves are 3rd degree bezier
+ * curves.
+ *
+ * @author Marcin Roguski
+ */
+public class IpoHelper extends AbstractBlenderHelper {
+
+ /**
+ * This constructor parses the given blender version and stores the result.
+ * Some functionalities may differ in different blender versions.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public IpoHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method creates an ipo object used for interpolation calculations.
+ *
+ * @param ipoStructure
+ * the structure with ipo definition
+ * @param blenderContext
+ * the blender context
+ * @return the ipo object
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
+
+ // preparing bezier curves
+ Ipo result = null;
+ List<Structure> curves = curvebase.evaluateListBase(blenderContext);// IpoCurve
+ if (curves.size() > 0) {
+ BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+ int frame = 0;
+ for (Structure curve : curves) {
+ Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+ List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+ int type = ((Number) curve.getFieldValue("adrcode")).intValue();
+ bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+ }
+ curves.clear();
+ result = new Ipo(bezierCurves, fixUpAxis);
+ blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
+ }
+ return result;
+ }
+
+ /**
+ * This method creates an ipo object used for interpolation calculations. It
+ * should be called for blender version 2.50 and higher.
+ *
+ * @param actionStructure
+ * the structure with action definition
+ * @param blenderContext
+ * the blender context
+ * @return the ipo object
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Ipo result = null;
+ List<Structure> curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase(blenderContext);// FCurve
+ if (curves.size() > 0) {
+ BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+ int frame = 0;
+ for (Structure curve : curves) {
+ Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+ List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
+ int type = this.getCurveType(curve, blenderContext);
+ bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+ }
+ curves.clear();
+ result = new Ipo(bezierCurves, fixUpAxis);
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the type of the ipo curve.
+ *
+ * @param structure
+ * the structure must contain the 'rna_path' field and
+ * 'array_index' field (the type is not important here)
+ * @param blenderContext
+ * the blender context
+ * @return the type of the curve
+ */
+ public int getCurveType(Structure structure, BlenderContext blenderContext) {
+ // reading rna path first
+ BlenderInputStream bis = blenderContext.getInputStream();
+ int currentPosition = bis.getPosition();
+ Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
+ bis.setPosition(dataFileBlock.getBlockPosition());
+ String rnaPath = bis.readString();
+ bis.setPosition(currentPosition);
+ int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
+
+ // determining the curve type
+ if (rnaPath.endsWith("location")) {
+ return Ipo.AC_LOC_X + arrayIndex;
+ }
+ if (rnaPath.endsWith("rotation_quaternion")) {
+ return Ipo.AC_QUAT_W + arrayIndex;
+ }
+ if (rnaPath.endsWith("scale")) {
+ return Ipo.AC_SIZE_X + arrayIndex;
+ }
+ if (rnaPath.endsWith("rotation")) {
+ return Ipo.OB_ROT_X + arrayIndex;
+ }
+ throw new IllegalStateException("Unknown curve rna path: " + rnaPath);
+ }
+
+ /**
+ * This method creates an ipo with only a single value. No track type is
+ * specified so do not use it for calculating tracks.
+ *
+ * @param constValue
+ * the value of this ipo
+ * @return constant ipo
+ */
+ public Ipo fromValue(float constValue) {
+ return new ConstIpo(constValue);
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+
+ /**
+ * Ipo constant curve. This is a curve with only one value and no specified
+ * type. This type of ipo cannot be used to calculate tracks. It should only
+ * be used to calculate single value for a given frame.
+ *
+ * @author Marcin Roguski
+ */
+ private class ConstIpo extends Ipo {
+
+ /** The constant value of this ipo. */
+ private float constValue;
+
+ /**
+ * Constructor. Stores the constant value of this ipo.
+ *
+ * @param constValue
+ * the constant value of this ipo
+ */
+ public ConstIpo(float constValue) {
+ super(null, false);
+ this.constValue = constValue;
+ }
+
+ @Override
+ public float calculateValue(int frame) {
+ return constValue;
+ }
+
+ @Override
+ public float calculateValue(int frame, int curveIndex) {
+ return constValue;
+ }
+
+ @Override
+ public int getCurvesAmount() {
+ return 0;
+ }
+
+ @Override
+ public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps, boolean boneTrack) {
+ throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
new file mode 100644
index 0000000..e69eb3b
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
@@ -0,0 +1,118 @@
+package com.jme3.scene.plugins.blender.cameras;
+
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A class that is used to load cameras into the scene.
+ * @author Marcin Roguski
+ */
+public class CameraHelper extends AbstractBlenderHelper {
+
+ private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
+ protected static final int DEFAULT_CAM_WIDTH = 640;
+ protected static final int DEFAULT_CAM_HEIGHT = 480;
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+ * different blender versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public CameraHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method converts the given structure to jme camera.
+ *
+ * @param structure
+ * camera structure
+ * @return jme camera object
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the
+ * blender file
+ */
+ public Camera toCamera(Structure structure) throws BlenderFileException {
+ if (blenderVersion >= 250) {
+ return this.toCamera250(structure);
+ } else {
+ return this.toCamera249(structure);
+ }
+ }
+
+ /**
+ * This method converts the given structure to jme camera. Should be used form blender 2.5+.
+ *
+ * @param structure
+ * camera structure
+ * @return jme camera object
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the
+ * blender file
+ */
+ public Camera toCamera250(Structure structure) throws BlenderFileException {
+ Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
+ int type = ((Number) structure.getFieldValue("type")).intValue();
+ if (type != 0 && type != 1) {
+ LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
+ type = 0;
+ }
+ //type==0 - perspective; type==1 - orthographic; perspective is used as default
+ result.setParallelProjection(type == 1);
+ float aspect = 0;
+ float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
+ float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
+ if (type == 0) {
+ aspect = ((Number) structure.getFieldValue("lens")).floatValue();
+ } else {
+ aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
+ }
+ result.setFrustumPerspective(45, aspect, clipsta, clipend);
+ return result;
+ }
+
+ /**
+ * This method converts the given structure to jme camera. Should be used form blender 2.49.
+ *
+ * @param structure
+ * camera structure
+ * @return jme camera object
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the
+ * blender file
+ */
+ public Camera toCamera249(Structure structure) throws BlenderFileException {
+ Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
+ int type = ((Number) structure.getFieldValue("type")).intValue();
+ if (type != 0 && type != 1) {
+ LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
+ type = 0;
+ }
+ //type==0 - perspective; type==1 - orthographic; perspective is used as default
+ result.setParallelProjection(type == 1);
+ float aspect = 0;
+ float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
+ float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
+ if (type == 0) {
+ aspect = ((Number) structure.getFieldValue("lens")).floatValue();
+ } else {
+ aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
+ }
+ result.setFrustumPerspective(aspect, result.getWidth() / result.getHeight(), clipsta, clipend);
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BlenderTrack.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BlenderTrack.java
new file mode 100644
index 0000000..40611a1
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/BlenderTrack.java
@@ -0,0 +1,147 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import java.io.IOException;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.SpatialTrack;
+import com.jme3.animation.Track;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.util.TempVars;
+
+/**
+ * This class holds either the bone track or spatial track. Is made to improve
+ * code readability.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */final class BlenderTrack implements Track {
+ /** The spatial track. */
+ private SpatialTrack spatialTrack;
+ /** The bone track. */
+ private BoneTrack boneTrack;
+
+ /**
+ * Constructs the object using spatial track (bone track is null).
+ *
+ * @param spatialTrack
+ * the spatial track
+ */
+ public BlenderTrack(SpatialTrack spatialTrack) {
+ this.spatialTrack = spatialTrack;
+ }
+
+ /**
+ * Constructs the object using bone track (spatial track is null).
+ *
+ * @param spatialTrack
+ * the spatial track
+ */
+ public BlenderTrack(BoneTrack boneTrack) {
+ this.boneTrack = boneTrack;
+ }
+
+ /**
+ * @return the stored track (either bone or spatial)
+ */
+ public Track getTrack() {
+ return boneTrack != null ? boneTrack : spatialTrack;
+ }
+
+ /**
+ * @return the array of rotations of this track
+ */
+ public Quaternion[] getRotations() {
+ if (boneTrack != null) {
+ return boneTrack.getRotations();
+ }
+ return spatialTrack.getRotations();
+ }
+
+ /**
+ * @return the array of scales for this track
+ */
+ public Vector3f[] getScales() {
+ if (boneTrack != null) {
+ return boneTrack.getScales();
+ }
+ return spatialTrack.getScales();
+ }
+
+ /**
+ * @return the arrays of time for this track
+ */
+ public float[] getTimes() {
+ if (boneTrack != null) {
+ return boneTrack.getTimes();
+ }
+ return spatialTrack.getTimes();
+ }
+
+ /**
+ * @return the array of translations of this track
+ */
+ public Vector3f[] getTranslations() {
+ if (boneTrack != null) {
+ return boneTrack.getTranslations();
+ }
+ return spatialTrack.getTranslations();
+ }
+
+ /**
+ * Set the translations, rotations and scales for this bone track
+ *
+ * @param times
+ * a float array with the time of each frame
+ * @param translations
+ * the translation of the bone for each frame
+ * @param rotations
+ * the rotation of the bone for each frame
+ * @param scales
+ * the scale of the bone for each frame
+ */
+ public void setKeyframes(float[] times, Vector3f[] translations,
+ Quaternion[] rotations, Vector3f[] scales) {
+ if (boneTrack != null) {
+ boneTrack.setKeyframes(times, translations, rotations, scales);
+ } else {
+ spatialTrack.setKeyframes(times, translations, rotations, scales);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ }
+
+ @Override
+ public void setTime(float time, float weight, AnimControl control,
+ AnimChannel channel, TempVars vars) {
+ if (boneTrack != null) {
+ boneTrack.setTime(time, weight, control, channel, vars);
+ } else {
+ spatialTrack.setTime(time, weight, control, channel, vars);
+ }
+ }
+
+ @Override
+ public float getLength() {
+ return spatialTrack == null ? boneTrack.getLength() : spatialTrack
+ .getLength();
+ }
+
+ @Override
+ public BlenderTrack clone() {
+ if (boneTrack != null) {
+ return new BlenderTrack(boneTrack.clone());
+ }
+ return new BlenderTrack(spatialTrack.clone());
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
new file mode 100644
index 0000000..6846d14
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
@@ -0,0 +1,147 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.SpatialTrack;
+import com.jme3.animation.Track;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+
+/**
+ * The implementation of a constraint.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public abstract class Constraint {
+ /** The name of this constraint. */
+ protected final String name;
+ /** The constraint's owner. */
+ protected final Feature owner;
+ /** The constraint's target. */
+ protected final Feature target;
+ /** The structure with constraint's data. */
+ protected final Structure data;
+ /** The ipo object defining influence. */
+ protected final Ipo ipo;
+ /** The blender context. */
+ protected final BlenderContext blenderContext;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Constraint(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ this.blenderContext = blenderContext;
+ this.name = constraintStructure.getFieldValue("name").toString();
+ Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
+ if (pData.isNotNull()) {
+ data = pData.fetchData(blenderContext.getInputStream()).get(0);
+ Pointer pTar = (Pointer)data.getFieldValue("tar");
+ if(pTar!= null && pTar.isNotNull()) {
+ Structure targetStructure = pTar.fetchData(blenderContext.getInputStream()).get(0);
+ Long targetOMA = pTar.getOldMemoryAddress();
+ Space targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ Spatial target = (Spatial) objectHelper.toObject(targetStructure, blenderContext);
+ this.target = new Feature(target, targetSpace, targetOMA, blenderContext);
+ } else {
+ this.target = null;
+ }
+ } else {
+ throw new BlenderFileException("The constraint has no data specified!");
+ }
+ Space ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
+ this.owner = new Feature(ownerSpace, ownerOMA, blenderContext);
+ this.ipo = influenceIpo;
+ }
+
+ /**
+ * This method bakes the required sontraints into its owner.
+ */
+ public void bake() {
+ this.owner.update();
+ if(this.target != null) {
+ this.target.update();
+ }
+ this.bakeConstraint();
+ }
+
+ /**
+ * Bake the animation's constraints into its owner.
+ */
+ protected abstract void bakeConstraint();
+
+ /**
+ * This method returns the bone traces for the bone that is affected by the given constraint.
+ * @param skeleton
+ * the skeleton containing bones
+ * @param boneAnimation
+ * the bone animation that affects the skeleton
+ * @return the bone track for the bone that is being affected by the constraint
+ */
+ protected BlenderTrack getTrack(Object owner, Skeleton skeleton, Animation animation) {
+ if(owner instanceof Bone) {
+ int boneIndex = skeleton.getBoneIndex((Bone) owner);
+ for (Track track : animation.getTracks()) {
+ if (((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
+ return new BlenderTrack(((BoneTrack) track));
+ }
+ }
+ throw new IllegalStateException("Cannot find track for: " + owner);
+ } else {
+ return new BlenderTrack((SpatialTrack)animation.getTracks()[0]);
+ }
+ }
+
+ /**
+ * The space of target or owner transformation.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ public static enum Space {
+
+ CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;
+
+ /**
+ * This method returns the enum instance when given the appropriate
+ * value from the blend file.
+ *
+ * @param c
+ * the blender's value of the space modifier
+ * @return the scape enum instance
+ */
+ public static Space valueOf(byte c) {
+ switch (c) {
+ case 0:
+ return CONSTRAINT_SPACE_WORLD;
+ case 1:
+ return CONSTRAINT_SPACE_LOCAL;
+ case 2:
+ return CONSTRAINT_SPACE_POSE;
+ case 3:
+ return CONSTRAINT_SPACE_PARLOCAL;
+ default:
+ return CONSTRAINT_SPACE_INVALID;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java
new file mode 100644
index 0000000..6c58ac0
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Action' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintAction extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintAction.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintAction(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Action' constraint
+ LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java
new file mode 100644
index 0000000..592d740
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'ChildOf' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintChildOf extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintChildOf.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintChildOf(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement ChildOf constraint
+ LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java
new file mode 100644
index 0000000..5c265c7
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java
@@ -0,0 +1,43 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Clamp to' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintClampTo extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintClampTo.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintClampTo(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext)
+ throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ //TODO: implement when curves are implemented
+ LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", name);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java
new file mode 100644
index 0000000..cf2784f
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java
@@ -0,0 +1,43 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * The damp track constraint. Available for blender 2.50+.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintDampTrack extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintDampTrack.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintDampTrack(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO Auto-generated method stub
+ LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java
new file mode 100644
index 0000000..ff3b99a
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java
@@ -0,0 +1,120 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Dist limit' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintDistLimit extends Constraint {
+ private static final int LIMITDIST_INSIDE = 0;
+ private static final int LIMITDIST_OUTSIDE = 1;
+ private static final int LIMITDIST_ONSURFACE = 2;
+
+ protected int mode;
+ protected float dist;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintDistLimit(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ mode = ((Number) data.getFieldValue("mode")).intValue();
+ dist = ((Number) data.getFieldValue("dist")).floatValue();
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ if(owner instanceof Spatial) {
+ Vector3f targetLocation = ((Spatial) owner).getWorldTranslation();
+ for(Animation animation : animData.anims) {
+ BlenderTrack blenderTrack = this.getTrack(owner, animData.skeleton, animation);
+ int maxFrames = blenderTrack.getTimes().length;
+ Vector3f[] translations = blenderTrack.getTranslations();
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ Vector3f v = translations[frame].subtract(targetLocation);
+ this.distLimit(v, targetLocation, ipo.calculateValue(frame));
+ translations[frame].addLocal(v);
+ }
+ blenderTrack.setKeyframes(blenderTrack.getTimes(), translations, blenderTrack.getRotations(), blenderTrack.getScales());
+ }
+ }
+ }
+
+ // apply static constraint only to spatials
+ if(owner instanceof Spatial) {
+ Matrix4f targetWorldMatrix = target.getWorldTransformMatrix();
+ Vector3f targetLocation = targetWorldMatrix.toTranslationVector();
+ Matrix4f m = this.owner.getParentWorldTransformMatrix();
+ m.invertLocal();
+ Matrix4f ownerWorldMatrix = this.owner.getWorldTransformMatrix();
+ Vector3f ownerLocation = ownerWorldMatrix.toTranslationVector();
+ this.distLimit(ownerLocation, targetLocation, ipo.calculateValue(0));
+ ((Spatial) owner).setLocalTranslation(m.mult(ownerLocation));
+ }
+ }
+
+ /**
+ *
+ * @param currentLocation
+ * @param targetLocation
+ * @param influence
+ */
+ private void distLimit(Vector3f currentLocation, Vector3f targetLocation, float influence) {
+ Vector3f v = currentLocation.subtract(targetLocation);
+ float currentDistance = v.length();
+
+ switch (mode) {
+ case LIMITDIST_INSIDE:
+ if (currentDistance >= dist) {
+ v.normalizeLocal();
+ v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
+ currentLocation.set(v.addLocal(targetLocation));
+ }
+ break;
+ case LIMITDIST_ONSURFACE:
+ if (currentDistance > dist) {
+ v.normalizeLocal();
+ v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
+ currentLocation.set(v.addLocal(targetLocation));
+ } else if(currentDistance < dist) {
+ v.normalizeLocal().multLocal(dist * influence);
+ currentLocation.set(targetLocation.add(v));
+ }
+ break;
+ case LIMITDIST_OUTSIDE:
+ if (currentDistance <= dist) {
+ v = targetLocation.subtract(currentLocation).normalizeLocal().multLocal(dist * influence);
+ currentLocation.set(targetLocation.add(v));
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java
new file mode 100644
index 0000000..5f8be2c
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Follow path' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintFollowPath extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintFollowPath.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintFollowPath(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ //TODO: implement when curves are implemented
+ LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
new file mode 100644
index 0000000..d75728f
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
@@ -0,0 +1,203 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.animations.IpoHelper;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * This class should be used for constraint calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class ConstraintHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
+
+ private static final Map<String, Class<? extends Constraint>> constraintClasses = new HashMap<String, Class<? extends Constraint>>(22);
+ static {
+ constraintClasses.put("bActionConstraint", ConstraintAction.class);
+ constraintClasses.put("bChildOfConstraint", ConstraintChildOf.class);
+ constraintClasses.put("bClampToConstraint", ConstraintClampTo.class);
+ constraintClasses.put("bDistLimitConstraint", ConstraintDistLimit.class);
+ constraintClasses.put("bFollowPathConstraint", ConstraintFollowPath.class);
+ constraintClasses.put("bKinematicConstraint", ConstraintInverseKinematics.class);
+ constraintClasses.put("bLockTrackConstraint", ConstraintLockTrack.class);
+ constraintClasses.put("bLocateLikeConstraint", ConstraintLocLike.class);
+ constraintClasses.put("bLocLimitConstraint", ConstraintLocLimit.class);
+ constraintClasses.put("bMinMaxConstraint", ConstraintMinMax.class);
+ constraintClasses.put("bNullConstraint", ConstraintNull.class);
+ constraintClasses.put("bPythonConstraint", ConstraintPython.class);
+ constraintClasses.put("bRigidBodyJointConstraint", ConstraintRigidBodyJoint.class);
+ constraintClasses.put("bRotateLikeConstraint", ConstraintRotLike.class);
+ constraintClasses.put("bShrinkWrapConstraint", ConstraintShrinkWrap.class);
+ constraintClasses.put("bSizeLikeConstraint", ConstraintSizeLike.class);
+ constraintClasses.put("bSizeLimitConstraint", ConstraintSizeLimit.class);
+ constraintClasses.put("bStretchToConstraint", ConstraintStretchTo.class);
+ constraintClasses.put("bTransformConstraint", ConstraintTransform.class);
+ constraintClasses.put("bRotLimitConstraint", ConstraintRotLimit.class);
+ //Blender 2.50+
+ constraintClasses.put("bSplineIKConstraint", ConstraintSplineInverseKinematic.class);
+ constraintClasses.put("bDampTrackConstraint", ConstraintDampTrack.class);
+ constraintClasses.put("bPivotConstraint", ConstraintDampTrack.class);
+ }
+
+ /**
+ * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
+ * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
+ * consider refactoring. The constructor parses the given blender version and stores the result. Some
+ * functionalities may differ in different blender versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method reads constraints for for the given structure. The
+ * constraints are loaded only once for object/bone.
+ *
+ * @param objectStructure
+ * the structure we read constraint's for
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ */
+ public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.fine("Loading constraints.");
+ // reading influence ipos for the constraints
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
+ Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
+ if (pActions.isNotNull()) {
+ List<Structure> actions = pActions.fetchData(blenderContext.getInputStream());
+ for (Structure action : actions) {
+ Structure chanbase = (Structure) action.getFieldValue("chanbase");
+ List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);
+ for (Structure actionChannel : actionChannels) {
+ Map<String, Ipo> ipos = new HashMap<String, Ipo>();
+ Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
+ List<Structure> constraintChannels = constChannels.evaluateListBase(blenderContext);
+ for (Structure constraintChannel : constraintChannels) {
+ Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
+ if (pIpo.isNotNull()) {
+ String constraintName = constraintChannel.getFieldValue("name").toString();
+ Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
+ ipos.put(constraintName, ipo);
+ }
+ }
+ String actionName = actionChannel.getFieldValue("name").toString();
+ constraintsIpos.put(actionName, ipos);
+ }
+ }
+ }
+
+ //loading constraints connected with the object's bones
+ Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
+ if (pPose.isNotNull()) {
+ List<Structure> poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
+ for (Structure poseChannel : poseChannels) {
+ List<Constraint> constraintsList = new ArrayList<Constraint>();
+ Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
+
+ //the name is read directly from structure because bone might not yet be loaded
+ String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
+ List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
+ for (Structure constraint : constraints) {
+ String constraintName = constraint.getFieldValue("name").toString();
+ Map<String, Ipo> ipoMap = constraintsIpos.get(name);
+ Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName);
+ if (ipo == null) {
+ float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+ ipo = ipoHelper.fromValue(enforce);
+ }
+ constraintsList.add(this.createConstraint(constraint, boneOMA, ipo, blenderContext));
+ }
+ blenderContext.addConstraints(boneOMA, constraintsList);
+ }
+ }
+
+ //loading constraints connected with the object itself
+ List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
+ List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
+
+ for(Structure constraint : constraints) {
+ String constraintName = constraint.getFieldValue("name").toString();
+ String objectName = objectStructure.getName();
+
+ Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
+ Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
+ if (ipo == null) {
+ float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+ ipo = ipoHelper.fromValue(enforce);
+ }
+ constraintsList.add(this.createConstraint(constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
+ }
+ blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
+ }
+
+ /**
+ * This method creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint's owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ protected Constraint createConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
+ BlenderContext blenderContext) throws BlenderFileException {
+ String constraintClassName = this.getConstraintClassName(constraintStructure, blenderContext);
+ Class<? extends Constraint> constraintClass = constraintClasses.get(constraintClassName);
+ if(constraintClass != null) {
+ try {
+ return (Constraint) constraintClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, influenceIpo,
+ blenderContext);
+ } catch (IllegalArgumentException e) {
+ throw new BlenderFileException(e.getLocalizedMessage(), e);
+ } catch (SecurityException e) {
+ throw new BlenderFileException(e.getLocalizedMessage(), e);
+ } catch (InstantiationException e) {
+ throw new BlenderFileException(e.getLocalizedMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new BlenderFileException(e.getLocalizedMessage(), e);
+ } catch (InvocationTargetException e) {
+ throw new BlenderFileException(e.getLocalizedMessage(), e);
+ }
+ } else {
+ throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
+ }
+ }
+
+ protected String getConstraintClassName(Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Pointer pData = (Pointer)constraintStructure.getFieldValue("data");
+ if(pData.isNotNull()) {
+ Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
+ return data.getType();
+
+ }
+ return constraintStructure.getType();
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java
new file mode 100644
index 0000000..ef85446
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java
@@ -0,0 +1,162 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Skeleton;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.CalculationBone;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Inverse kinematics' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintInverseKinematics extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintInverseKinematics.class.getName());
+ private static final float IK_SOLVER_ERROR = 0.5f;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintInverseKinematics(Structure constraintStructure,
+ Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+// try {
+ // IK solver is only attached to bones
+// Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
+// AnimData animData = blenderContext.getAnimData(ownerOMA);
+// if(animData == null) {
+ //TODO: to nie moxe byx null, utworzyx dane bez ruchu, w zalexnoxci czy target six rusza
+// }
+
+ //prepare a list of all parents of this bone
+// CalculationBone[] bones = this.getBonesToCalculate(skeleton, boneAnimation);
+
+ // get the target point
+// Object targetObject = this.getTarget(LoadedFeatureDataType.LOADED_FEATURE);
+// Vector3f pt = null;// Point Target
+// if (targetObject instanceof Bone) {
+// pt = ((Bone) targetObject).getModelSpacePosition();
+// } else if (targetObject instanceof Spatial) {
+// pt = ((Spatial) targetObject).getWorldTranslation();
+// } else if (targetObject instanceof Skeleton) {
+// Structure armatureNodeStructure = (Structure) this.getTarget(LoadedFeatureDataType.LOADED_STRUCTURE);
+// ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+// Transform transform = objectHelper.getTransformation(armatureNodeStructure, blenderContext);
+// pt = transform.getTranslation();
+// } else {
+// throw new IllegalStateException(
+// "Unknown target object type! Should be Node, Bone or Skeleton and there is: "
+// + targetObject.getClass().getName());
+// }
+
+ //fetching the owner's bone track
+// BoneTrack ownerBoneTrack = null;
+// int boneIndex = skeleton.getBoneIndex(ownerBone);
+// for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
+// if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
+// ownerBoneTrack = boneAnimation.getTracks()[i];
+// break;
+// }
+// }
+// int ownerBoneFramesCount = ownerBoneTrack==null ? 0 : ownerBoneTrack.getTimes().length;
+//
+// // preparing data
+// int maxIterations = ((Number) data.getFieldValue("iterations")).intValue();
+// CalculationBone[] bones = this.getBonesToCalculate(ownerBone, skeleton, boneAnimation);
+// for (int i = 0; i < bones.length; ++i) {
+// System.out.println(Arrays.toString(bones[i].track.getTranslations()));
+// System.out.println(Arrays.toString(bones[i].track.getRotations()));
+// System.out.println("===============================");
+// }
+// Quaternion rotation = new Quaternion();
+// //all tracks should have the same amount of frames
+// int framesCount = bones[0].getBoneFramesCount();
+// assert framesCount >=1;
+// for (int frame = 0; frame < framesCount; ++frame) {
+// float error = IK_SOLVER_ERROR;
+// int iteration = 0;
+// while (error >= IK_SOLVER_ERROR && iteration <= maxIterations) {
+// // rotating the bones
+// for (int i = 0; i < bones.length - 1; ++i) {
+// Vector3f pe = bones[i].getEndPoint();
+// Vector3f pc = bones[i + 1].getWorldTranslation().clone();
+//
+// Vector3f peSUBpc = pe.subtract(pc).normalizeLocal();
+// Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal();
+//
+// float theta = FastMath.acos(peSUBpc.dot(ptSUBpc));
+// Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal();
+// bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame);
+// }
+// error = pt.subtract(bones[0].getEndPoint()).length();
+// ++iteration;
+// }
+// }
+//
+// for (CalculationBone bone : bones) {
+// bone.applyCalculatedTracks();
+// }
+//
+// System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
+// for (int i = 0; i < bones.length; ++i) {
+// System.out.println(Arrays.toString(bones[i].track.getTranslations()));
+// System.out.println(Arrays.toString(bones[i].track.getRotations()));
+// System.out.println("===============================");
+// }
+// } catch(BlenderFileException e) {
+// LOGGER.severe(e.getLocalizedMessage());
+// }
+ }
+
+ /**
+ * This method returns bones used for rotation calculations.
+ * @param bone
+ * the bone to which the constraint is applied
+ * @param skeleton
+ * the skeleton owning the bone and its ancestors
+ * @param boneAnimation
+ * the bone animation data that stores the traces for the skeleton's bones
+ * @return a list of bones to imitate the bone's movement during IK solving
+ */
+ private CalculationBone[] getBonesToCalculate(Skeleton skeleton, Animation boneAnimation) {
+// Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
+// List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
+// do {
+// bonesList.add(new CalculationBone(ownerBone, 1));
+// int boneIndex = skeleton.getBoneIndex(ownerBone);
+// for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
+// if (((BoneTrack[])boneAnimation.getTracks())[i].getTargetBoneIndex() == boneIndex) {
+// bonesList.add(new CalculationBone(ownerBone, (BoneTrack)boneAnimation.getTracks()[i]));
+// break;
+// }
+// }
+// ownerBone = ownerBone.getParent();
+// } while (ownerBone != null);
+// //attaching children
+// CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]);
+// for (int i = result.length - 1; i > 0; --i) {
+// result[i].attachChild(result[i - 1]);
+// }
+// return result;
+ return null;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java
new file mode 100644
index 0000000..834dfb4
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java
@@ -0,0 +1,122 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Loc like' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintLocLike extends Constraint {
+ private static final int LOCLIKE_X = 0x01;
+ private static final int LOCLIKE_Y = 0x02;
+ private static final int LOCLIKE_Z = 0x04;
+ //protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in blender
+ private static final int LOCLIKE_X_INVERT = 0x10;
+ private static final int LOCLIKE_Y_INVERT = 0x20;
+ private static final int LOCLIKE_Z_INVERT = 0x40;
+ private static final int LOCLIKE_OFFSET = 0x80;
+
+ protected int flag;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintLocLike(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ flag = ((Number) data.getFieldValue("flag")).intValue();
+
+ if(blenderContext.getBlenderKey().isFixUpAxis()) {
+ //swapping Y and X limits flag in the bitwise flag
+ int y = flag & LOCLIKE_Y;
+ int invY = flag & LOCLIKE_Y_INVERT;
+ int z = flag & LOCLIKE_Z;
+ int invZ = flag & LOCLIKE_Z_INVERT;
+ flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;//clear the other flags to swap them
+ flag |= y << 2;
+ flag |= invY << 2;
+ flag |= z >> 2;
+ flag |= invZ >> 2;
+ }
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ Transform targetTransform = this.target.getTransform();
+ for(Animation animation : animData.anims) {
+ BlenderTrack blenderTrack = this.getTrack(owner, animData.skeleton, animation);
+ Vector3f[] translations = blenderTrack.getTranslations();
+ int maxFrames = translations.length;
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ this.locLike(translations[frame], targetTransform.getTranslation(), ipo.calculateValue(frame));
+ }
+ blenderTrack.setKeyframes(blenderTrack.getTimes(), translations, blenderTrack.getRotations(), blenderTrack.getScales());
+ }
+ }
+
+ if(owner instanceof Spatial) {
+ Transform targetTransform = this.target.getTransform();
+ Transform ownerTransform = this.owner.getTransform();
+ Vector3f ownerLocation = ownerTransform.getTranslation();
+ this.locLike(ownerLocation, targetTransform.getTranslation(), ipo.calculateValue(0));
+ this.owner.applyTransform(ownerTransform);
+ }
+ }
+
+ private void locLike(Vector3f ownerLocation, Vector3f targetLocation, float influence) {
+ Vector3f startLocation = ownerLocation.clone();
+ Vector3f offset = Vector3f.ZERO;
+ if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
+ offset = startLocation;
+ }
+
+ if ((flag & LOCLIKE_X) != 0) {
+ ownerLocation.x = targetLocation.x;
+ if ((flag & LOCLIKE_X_INVERT) != 0) {
+ ownerLocation.x = -ownerLocation.x;
+ }
+ }
+ if ((flag & LOCLIKE_Y) != 0) {
+ ownerLocation.y = targetLocation.y;
+ if ((flag & LOCLIKE_Y_INVERT) != 0) {
+ ownerLocation.y = -ownerLocation.y;
+ }
+ }
+ if ((flag & LOCLIKE_Z) != 0) {
+ ownerLocation.z = targetLocation.z;
+ if ((flag & LOCLIKE_Z_INVERT) != 0) {
+ ownerLocation.z = -ownerLocation.z;
+ }
+ }
+ ownerLocation.addLocal(offset);
+
+ if(influence < 1.0f) {
+ startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
+ ownerLocation.addLocal(startLocation);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java
new file mode 100644
index 0000000..74e2ec1
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java
@@ -0,0 +1,137 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Loc limit' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintLocLimit extends Constraint {
+ private static final int LIMIT_XMIN = 0x01;
+ private static final int LIMIT_XMAX = 0x02;
+ private static final int LIMIT_YMIN = 0x04;
+ private static final int LIMIT_YMAX = 0x08;
+ private static final int LIMIT_ZMIN = 0x10;
+ private static final int LIMIT_ZMAX = 0x20;
+
+ protected float[][] limits = new float[3][2];
+ protected int flag;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintLocLimit(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ flag = ((Number) data.getFieldValue("flag")).intValue();
+ if(blenderContext.getBlenderKey().isFixUpAxis()) {
+ limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
+ limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
+ limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
+ limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
+ limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
+ limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
+
+ //swapping Y and X limits flag in the bitwise flag
+ int ymin = flag & LIMIT_YMIN;
+ int ymax = flag & LIMIT_YMAX;
+ int zmin = flag & LIMIT_ZMIN;
+ int zmax = flag & LIMIT_ZMAX;
+ flag &= LIMIT_XMIN | LIMIT_XMAX;//clear the other flags to swap them
+ flag |= ymin << 2;
+ flag |= ymax << 2;
+ flag |= zmin >> 2;
+ flag |= zmax >> 2;
+ } else {
+ limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
+ limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
+ limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
+ limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
+ limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
+ limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
+ }
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ for(Animation animation : animData.anims) {
+ BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+ Vector3f[] translations = track.getTranslations();
+ int maxFrames = translations.length;
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ this.locLimit(translations[frame], ipo.calculateValue(frame));
+ }
+ track.setKeyframes(track.getTimes(), translations, track.getRotations(), track.getScales());
+ }
+ }
+
+ if(owner instanceof Spatial) {
+ Transform ownerTransform = this.owner.getTransform();
+ Vector3f ownerLocation = ownerTransform.getTranslation();
+ this.locLimit(ownerLocation, ipo.calculateValue(0));
+ this.owner.applyTransform(ownerTransform);
+ }
+ }
+
+ /**
+ * This method modifies the given translation.
+ * @param translation the translation to be modified.
+ * @param influence the influence value
+ */
+ private void locLimit(Vector3f translation, float influence) {
+ if ((flag & LIMIT_XMIN) != 0) {
+ if (translation.x < limits[0][0]) {
+ translation.x -= (translation.x - limits[0][0]) * influence;
+ }
+ }
+ if ((flag & LIMIT_XMAX) != 0) {
+ if (translation.x > limits[0][1]) {
+ translation.x -= (translation.x - limits[0][1]) * influence;
+ }
+ }
+ if ((flag & LIMIT_YMIN) != 0) {
+ if (translation.y < limits[1][0]) {
+ translation.y -= (translation.y - limits[1][0]) * influence;
+ }
+ }
+ if ((flag & LIMIT_YMAX) != 0) {
+ if (translation.y > limits[1][1]) {
+ translation.y -= (translation.y - limits[1][1]) * influence;
+ }
+ }
+ if ((flag & LIMIT_ZMIN) != 0) {
+ if (translation.z < limits[2][0]) {
+ translation.z -= (translation.z - limits[2][0]) * influence;
+ }
+ }
+ if ((flag & LIMIT_ZMAX) != 0) {
+ if (translation.z > limits[2][1]) {
+ translation.z -= (translation.z - limits[2][1]) * influence;
+ }
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java
new file mode 100644
index 0000000..e54d70a
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java
@@ -0,0 +1,43 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Action' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintLockTrack extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintLockTrack.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintLockTrack(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext)
+ throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Lock track' constraint
+ LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java
new file mode 100644
index 0000000..f4a5456
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Min max' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintMinMax extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintMinMax.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintMinMax(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Min max' constraint
+ LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java
new file mode 100644
index 0000000..4409969
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java
@@ -0,0 +1,37 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * This class represents 'Null' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintNull extends Constraint {
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintNull(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext)
+ throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {}
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java
new file mode 100644
index 0000000..f0f3bae
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java
@@ -0,0 +1,44 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * The pivot constraint. Available for blender 2.50+.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintPivot extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintPivot.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintPivot(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
+ BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO Auto-generated method stub
+ LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java
new file mode 100644
index 0000000..a0beb78
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Python' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintPython extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintPython.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintPython(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Python' constraint
+ LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java
new file mode 100644
index 0000000..b398ae6
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Rigid body joint' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintRigidBodyJoint extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintRigidBodyJoint.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintRigidBodyJoint(Structure constraintStructure,
+ Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Rigid body joint' constraint
+ LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java
new file mode 100644
index 0000000..db0c93a
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java
@@ -0,0 +1,114 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Rot like' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintRotLike extends Constraint {
+ private static final int ROTLIKE_X = 0x01;
+ private static final int ROTLIKE_Y = 0x02;
+ private static final int ROTLIKE_Z = 0x04;
+ private static final int ROTLIKE_X_INVERT = 0x10;
+ private static final int ROTLIKE_Y_INVERT = 0x20;
+ private static final int ROTLIKE_Z_INVERT = 0x40;
+ private static final int ROTLIKE_OFFSET = 0x80;
+
+ protected int flag;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintRotLike(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ flag = ((Number) data.getFieldValue("flag")).intValue();
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ Transform targetTransform = this.target.getTransform();
+ Quaternion targetRotation = targetTransform.getRotation();
+ for(Animation animation : animData.anims) {
+ BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+ float[] targetAngles = targetRotation.toAngles(null);
+ Quaternion[] rotations = track.getRotations();
+ int maxFrames = rotations.length;
+ float[] angles = new float[3];
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ rotations[frame].toAngles(angles);
+ this.rotLike(rotations[frame], angles, targetAngles, ipo.calculateValue(frame));
+ }
+ track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
+ }
+ }
+
+ if(owner instanceof Spatial) {
+ Transform targetTransform = this.target.getTransform();
+ Transform ownerTransform = this.owner.getTransform();
+ Quaternion ownerRotation = ownerTransform.getRotation();
+ this.rotLike(ownerRotation, ownerRotation.toAngles(null), targetTransform.getRotation().toAngles(null), ipo.calculateValue(0));
+ this.owner.applyTransform(ownerTransform);
+ }
+ }
+
+ private void rotLike(Quaternion ownerRotation, float[] ownerAngles, float[] targetAngles, float influence) {
+ Quaternion startRotation = ownerRotation.clone();
+ Quaternion offset = Quaternion.IDENTITY;
+ if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
+ offset = startRotation;
+ }
+
+ if ((flag & ROTLIKE_X) != 0) {
+ ownerAngles[0] = targetAngles[0];
+ if ((flag & ROTLIKE_X_INVERT) != 0) {
+ ownerAngles[0] = -ownerAngles[0];
+ }
+ }
+ if ((flag & ROTLIKE_Y) != 0) {
+ ownerAngles[1] = targetAngles[1];
+ if ((flag & ROTLIKE_Y_INVERT) != 0) {
+ ownerAngles[1] = -ownerAngles[1];
+ }
+ }
+ if ((flag & ROTLIKE_Z) != 0) {
+ ownerAngles[2] = targetAngles[2];
+ if ((flag & ROTLIKE_Z_INVERT) != 0) {
+ ownerAngles[2] = -ownerAngles[2];
+ }
+ }
+ ownerRotation.fromAngles(ownerAngles).multLocal(offset);
+
+ if(influence < 1.0f) {
+
+// startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
+// ownerLocation.addLocal(startLocation);
+ //TODO
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java
new file mode 100644
index 0000000..d63f342
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java
@@ -0,0 +1,180 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Rot limit' constraint type in blender.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ConstraintRotLimit extends Constraint {
+ private static final int LIMIT_XROT = 0x01;
+ private static final int LIMIT_YROT = 0x02;
+ private static final int LIMIT_ZROT = 0x04;
+
+ protected float[][] limits = new float[3][2];
+ protected int flag;
+ protected boolean updated;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintRotLimit(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ flag = ((Number) data.getFieldValue("flag")).intValue();
+ if (blenderContext.getBlenderKey().isFixUpAxis() && owner.spatial != null) {
+ limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
+ limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
+ limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
+ limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
+ limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
+ limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
+
+ // swapping Y and X limits flag in the bitwise flag
+ int limitY = flag & LIMIT_YROT;
+ int limitZ = flag & LIMIT_ZROT;
+ flag &= LIMIT_XROT;// clear the other flags to swap them
+ flag |= limitY << 1;
+ flag |= limitZ >> 1;
+ } else {
+ limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
+ limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
+ limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
+ limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
+ limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
+ limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
+ }
+
+ // until blender 2.49 the rotations values were stored in degrees
+ if (blenderContext.getBlenderVersion() <= 249) {
+ for (int i = 0; i < limits.length; ++i) {
+ limits[i][0] *= FastMath.DEG_TO_RAD;
+ limits[i][1] *= FastMath.DEG_TO_RAD;
+ }
+ }
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ this.update();
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if (animData != null) {
+ for (Animation animation : animData.anims) {
+ BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+ Quaternion[] rotations = track.getRotations();
+ float[] angles = new float[3];
+ int maxFrames = rotations.length;
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ rotations[frame].toAngles(angles);
+ this.rotLimit(angles, ipo.calculateValue(frame));
+ rotations[frame].fromAngles(angles);
+ }
+ track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
+ }
+ }
+
+ if (owner instanceof Spatial) {
+ Transform ownerTransform = this.owner.getTransform();
+ float[] angles = ownerTransform.getRotation().toAngles(null);
+ this.rotLimit(angles, ipo.calculateValue(0));
+ ownerTransform.getRotation().fromAngles(angles);
+ this.owner.applyTransform(ownerTransform);
+ }
+ }
+
+ /**
+ * This method computes new constrained angles.
+ *
+ * @param angles
+ * angles to be altered
+ * @param influence
+ * the alteration influence
+ */
+ private void rotLimit(float[] angles, float influence) {
+ if ((flag & LIMIT_XROT) != 0) {
+ float difference = 0.0f;
+ if (angles[0] < limits[0][0]) {
+ difference = (angles[0] - limits[0][0]) * influence;
+ } else if (angles[0] > limits[0][1]) {
+ difference = (angles[0] - limits[0][1]) * influence;
+ }
+ angles[0] -= difference;
+ }
+ if ((flag & LIMIT_YROT) != 0) {
+ float difference = 0.0f;
+ if (angles[1] < limits[1][0]) {
+ difference = (angles[1] - limits[1][0]) * influence;
+ } else if (angles[1] > limits[1][1]) {
+ difference = (angles[1] - limits[1][1]) * influence;
+ }
+ angles[1] -= difference;
+ }
+ if ((flag & LIMIT_ZROT) != 0) {
+ float difference = 0.0f;
+ if (angles[2] < limits[2][0]) {
+ difference = (angles[2] - limits[2][0]) * influence;
+ } else if (angles[2] > limits[2][1]) {
+ difference = (angles[2] - limits[2][1]) * influence;
+ }
+ angles[2] -= difference;
+ }
+ }
+
+ /**
+ * This method is called before baking (performes its operations only once).
+ * It is important to update the state of the limits and owner/target before
+ * baking the constraint.
+ */
+ private void update() {
+ if (!updated) {
+ updated = true;
+ if (owner != null) {
+ owner.update();
+ }
+ if (target != null) {
+ target.update();
+ }
+ if (this.owner.getObject() instanceof Bone) {// for bones we need to
+ // change the sign
+ // of the limits
+ for (int i = 0; i < limits.length; ++i) {
+ limits[i][0] *= -1;
+ limits[i][1] *= -1;
+ }
+ }
+
+ // sorting the limits (lower is always first)
+ for (int i = 0; i < limits.length; ++i) {
+ if (limits[i][0] > limits[i][1]) {
+ float temp = limits[i][0];
+ limits[i][0] = limits[i][1];
+ limits[i][1] = temp;
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java
new file mode 100644
index 0000000..7352032
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java
@@ -0,0 +1,92 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Shrink wrap' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintShrinkWrap extends Constraint {
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintShrinkWrap(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ //loading mesh points (blender ensures that the target is a mesh-object)
+ List<Vector3f> pts = new ArrayList<Vector3f>();
+ Node target = (Node) this.target.getObject();
+ for(Spatial spatial : target.getChildren()) {
+ if(spatial instanceof Geometry) {
+ Mesh mesh = ((Geometry) spatial).getMesh();
+ FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position);
+ for(int i=0;i<floatBuffer.limit();i+=3) {
+ pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2)));
+ }
+ }
+ }
+
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ Object owner = this.owner.getObject();
+ for(Animation animation : animData.anims) {
+ BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+ Vector3f[] translations = track.getTranslations();
+ Quaternion[] rotations = track.getRotations();
+ int maxFrames = translations.length;
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ Vector3f currentTranslation = translations[frame];
+
+ //looking for minimum distanced point
+ Vector3f minDistancePoint = null;
+ float distance = Float.MAX_VALUE;
+ for(Vector3f p : pts) {
+ float temp = currentTranslation.distance(p);
+ if(temp < distance) {
+ distance = temp;
+ minDistancePoint = p;
+ }
+ }
+ translations[frame] = minDistancePoint.clone();
+ }
+
+ track.setKeyframes(track.getTimes(), translations, rotations, track.getScales());
+ }
+ }
+
+ //TODO: static constraint for spatials
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java
new file mode 100644
index 0000000..8b697aa
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java
@@ -0,0 +1,98 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Size like' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintSizeLike extends Constraint {
+ private static final int SIZELIKE_X = 0x01;
+ private static final int SIZELIKE_Y = 0x02;
+ private static final int SIZELIKE_Z = 0x04;
+ private static final int LOCLIKE_OFFSET = 0x80;
+
+ protected int flag;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintSizeLike(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ flag = ((Number) data.getFieldValue("flag")).intValue();
+ if(blenderContext.getBlenderKey().isFixUpAxis()) {
+ //swapping Y and X limits flag in the bitwise flag
+ int y = flag & SIZELIKE_Y;
+ int z = flag & SIZELIKE_Z;
+ flag &= SIZELIKE_X | LOCLIKE_OFFSET;//clear the other flags to swap them
+ flag |= y << 1;
+ flag |= z >> 1;
+ }
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ Transform targetTransform = this.target.getTransform();
+ Vector3f targetScale = targetTransform.getScale();
+ for(Animation animation : animData.anims) {
+ BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+ Vector3f[] scales = track.getScales();
+ int maxFrames = scales.length;
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ this.sizeLike(scales[frame], targetScale, ipo.calculateValue(frame));
+ }
+ track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
+ }
+ }
+
+ if(owner instanceof Spatial) {
+ Transform targetTransform = this.target.getTransform();
+ Transform ownerTransform = this.owner.getTransform();
+ this.sizeLike(ownerTransform.getScale(), targetTransform.getScale(), ipo.calculateValue(0));
+ this.owner.applyTransform(ownerTransform);
+ }
+ }
+
+ private void sizeLike(Vector3f ownerScale, Vector3f targetScale, float influence) {
+ Vector3f offset = Vector3f.ZERO;
+ if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
+ offset = ownerScale.clone();
+ }
+
+ if ((flag & SIZELIKE_X) != 0) {
+ ownerScale.x = targetScale.x * influence + (1.0f - influence) * ownerScale.x;
+ }
+ if ((flag & SIZELIKE_Y) != 0) {
+ ownerScale.y = targetScale.y * influence + (1.0f - influence) * ownerScale.y;
+ }
+ if ((flag & SIZELIKE_Z) != 0) {
+ ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
+ }
+ ownerScale.addLocal(offset);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java
new file mode 100644
index 0000000..5d58299
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java
@@ -0,0 +1,131 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Animation;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This class represents 'Size limit' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintSizeLimit extends Constraint {
+ private static final int LIMIT_XMIN = 0x01;
+ private static final int LIMIT_XMAX = 0x02;
+ private static final int LIMIT_YMIN = 0x04;
+ private static final int LIMIT_YMAX = 0x08;
+ private static final int LIMIT_ZMIN = 0x10;
+ private static final int LIMIT_ZMAX = 0x20;
+
+ protected float[][] limits = new float[3][2];
+ protected int flag;
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintSizeLimit(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+
+ flag = ((Number) data.getFieldValue("flag")).intValue();
+ if(blenderContext.getBlenderKey().isFixUpAxis()) {
+ limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
+ limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
+ limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
+ limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
+ limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
+ limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
+
+ //swapping Y and X limits flag in the bitwise flag
+ int ymin = flag & LIMIT_YMIN;
+ int ymax = flag & LIMIT_YMAX;
+ int zmin = flag & LIMIT_ZMIN;
+ int zmax = flag & LIMIT_ZMAX;
+ flag &= LIMIT_XMIN | LIMIT_XMAX;//clear the other flags to swap them
+ flag |= ymin << 2;
+ flag |= ymax << 2;
+ flag |= zmin >> 2;
+ flag |= zmax >> 2;
+ } else {
+ limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
+ limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
+ limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
+ limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
+ limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
+ limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
+ }
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ Object owner = this.owner.getObject();
+ AnimData animData = blenderContext.getAnimData(this.owner.getOma());
+ if(animData != null) {
+ for(Animation animation : animData.anims) {
+ BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
+ Vector3f[] scales = track.getScales();
+ int maxFrames = scales.length;
+ for (int frame = 0; frame < maxFrames; ++frame) {
+ this.sizeLimit(scales[frame], ipo.calculateValue(frame));
+ }
+ track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
+ }
+ }
+
+ if(owner instanceof Spatial) {
+ Transform ownerTransform = this.owner.getTransform();
+ this.sizeLimit(ownerTransform.getScale(), ipo.calculateValue(0));
+ this.owner.applyTransform(ownerTransform);
+ }
+ }
+
+ private void sizeLimit(Vector3f scale, float influence) {
+ if ((flag & LIMIT_XMIN) != 0) {
+ if (scale.x < limits[0][0]) {
+ scale.x -= (scale.x - limits[0][0]) * influence;
+ }
+ }
+ if ((flag & LIMIT_XMAX) != 0) {
+ if (scale.x > limits[0][1]) {
+ scale.x -= (scale.x - limits[0][1]) * influence;
+ }
+ }
+ if ((flag & LIMIT_YMIN) != 0) {
+ if (scale.y < limits[1][0]) {
+ scale.y -= (scale.y - limits[1][0]) * influence;
+ }
+ }
+ if ((flag & LIMIT_YMAX) != 0) {
+ if (scale.y > limits[1][1]) {
+ scale.y -= (scale.y - limits[1][1]) * influence;
+ }
+ }
+ if ((flag & LIMIT_ZMIN) != 0) {
+ if (scale.z < limits[2][0]) {
+ scale.z -= (scale.z - limits[2][0]) * influence;
+ }
+ }
+ if ((flag & LIMIT_ZMAX) != 0) {
+ if (scale.z > limits[2][1]) {
+ scale.z -= (scale.z - limits[2][1]) * influence;
+ }
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java
new file mode 100644
index 0000000..0d9f9ab
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java
@@ -0,0 +1,44 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * The spline inverse kinematic constraint. Available for blender 2.50+.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintSplineInverseKinematic extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintSplineInverseKinematic.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure (bConstraint clss in blender 2.49).
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintSplineInverseKinematic(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
+ BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO Auto-generated method stub
+ LOGGER.log(Level.WARNING, "'Splie IK' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java
new file mode 100644
index 0000000..a5a9f55
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Stretch to' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintStretchTo extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintStretchTo.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintStretchTo(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Stretch to' constraint
+ LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java
new file mode 100644
index 0000000..5d111a3
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class represents 'Transform' constraint type in blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ConstraintTransform extends Constraint {
+ private static final Logger LOGGER = Logger.getLogger(ConstraintAction.class.getName());
+
+ /**
+ * This constructor creates the constraint instance.
+ *
+ * @param constraintStructure
+ * the constraint's structure
+ * @param ownerOMA
+ * the old memory address of the constraint owner
+ * @param influenceIpo
+ * the ipo curve of the influence factor
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ConstraintTransform(Structure constraintStructure, Long ownerOMA,
+ Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
+ super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
+ }
+
+ @Override
+ protected void bakeConstraint() {
+ // TODO: implement 'Transform' constraint
+ LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java
new file mode 100644
index 0000000..b08a20c
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java
@@ -0,0 +1,302 @@
+package com.jme3.scene.plugins.blender.constraints;
+
+import com.jme3.animation.Bone;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.constraints.Constraint.Space;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * This class represents either owner or target of the constraint. It has the
+ * common methods that take the evalueation space of the feature.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class Feature {
+ /** The evalueation space. */
+ protected Space space;
+ /** Old memory address of the feature. */
+ protected Long oma;
+ /** The spatial that is hold by the Feature. */
+ protected Spatial spatial;
+ /** The bone that is hold by the Feature. */
+ protected Bone bone;
+ /** The blender context. */
+ protected BlenderContext blenderContext;
+
+ /**
+ * Constructs the feature. This object should be loaded later
+ * when it is read from the blender file.
+ * The update method should be called before the feature is used.
+ *
+ * @param space
+ * the spatial's evaluation space
+ * @param oma
+ * the spatial's old memory address
+ * @param blenderContext
+ * the blender context
+ */
+ public Feature(Space space, Long oma, BlenderContext blenderContext) {
+ this.space = space;
+ this.oma = oma;
+ this.blenderContext = blenderContext;
+ }
+
+ /**
+ * Constructs the feature based on spatial.
+ *
+ * @param spatial
+ * the spatial
+ * @param space
+ * the spatial's evaluation space
+ * @param oma
+ * the spatial's old memory address
+ * @param blenderContext
+ * the blender context
+ */
+ public Feature(Spatial spatial, Space space, Long oma, BlenderContext blenderContext) {
+ this(space, oma, blenderContext);
+ this.blenderContext = blenderContext;
+ }
+
+ /**
+ * Constructs the feature based on bone.
+ *
+ * @param bone
+ * the bone
+ * @param space
+ * the bone evaluation space
+ * @param oma
+ * the bone old memory address
+ * @param blenderContext
+ * the blender context
+ */
+ public Feature(Bone bone, Space space, Long oma, BlenderContext blenderContext) {
+ this(space, oma, blenderContext);
+ this.bone = bone;
+ }
+
+ /**
+ * This method should be called before the feature is used.
+ * It may happen that the object this feature refers to was not yet loaded from blend file
+ * when the instance of this class was created.
+ */
+ public void update() {
+ Object owner = blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
+ if(owner instanceof Spatial) {
+ this.spatial = (Spatial) owner;
+ } else if(owner instanceof Bone) {
+ this.bone = (Bone) owner;
+ } else {
+ throw new IllegalStateException("Unknown type of owner: " + owner.getClass());
+ }
+ }
+
+ /**
+ * @return the feature's old memory address
+ */
+ public Long getOma() {
+ return oma;
+ }
+
+ /**
+ * @return the object held by the feature (either bone or spatial)
+ */
+ public Object getObject() {
+ if (spatial != null) {
+ return spatial;
+ }
+ return bone;
+ }
+
+ /**
+ * @return the feature's transform depending on the evaluation space
+ */
+ @SuppressWarnings("unchecked")
+ public Transform getTransform() {
+ if (spatial != null) {
+ switch (space) {
+ case CONSTRAINT_SPACE_LOCAL:
+ Structure targetStructure = (Structure) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_STRUCTURE);
+
+ DynamicArray<Number> locArray = ((DynamicArray<Number>) targetStructure.getFieldValue("loc"));
+ Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue());
+ DynamicArray<Number> rotArray = ((DynamicArray<Number>) targetStructure.getFieldValue("rot"));
+ Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() });
+ DynamicArray<Number> sizeArray = ((DynamicArray<Number>) targetStructure.getFieldValue("size"));
+ Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue());
+
+ if (blenderContext.getBlenderKey().isFixUpAxis()) {
+ float y = loc.y;
+ loc.y = loc.z;
+ loc.z = -y;
+
+ y = rot.getY();
+ float z = rot.getZ();
+ rot.set(rot.getX(), z, -y, rot.getW());
+
+ y = size.y;
+ size.y = size.z;
+ size.z = y;
+ }
+
+ Transform result = new Transform(loc, rot);
+ result.setScale(size);
+ return result;
+ case CONSTRAINT_SPACE_WORLD:
+ return spatial.getWorldTransform();
+ default:
+ throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+ }
+ }
+ // Bone
+ switch (space) {
+ case CONSTRAINT_SPACE_LOCAL:
+ Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+ localTransform.setScale(bone.getLocalScale());
+ return localTransform;
+ case CONSTRAINT_SPACE_WORLD:
+ Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation());
+ worldTransform.setScale(bone.getWorldBindScale());
+ return worldTransform;
+ case CONSTRAINT_SPACE_POSE:
+ Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+ poseTransform.setScale(bone.getLocalScale());
+ return poseTransform;
+ case CONSTRAINT_SPACE_PARLOCAL:
+ Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
+ parentLocalTransform.setScale(bone.getLocalScale());
+ return parentLocalTransform;
+ default:
+ throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+ }
+ }
+
+ /**
+ * This method applies the given transform to the feature in the proper
+ * evaluation space.
+ *
+ * @param transform
+ * the transform to be applied
+ */
+ public void applyTransform(Transform transform) {
+ if (spatial != null) {
+ switch (space) {
+ case CONSTRAINT_SPACE_LOCAL:
+ Transform ownerLocalTransform = spatial.getLocalTransform();
+ ownerLocalTransform.getTranslation().addLocal(transform.getTranslation());
+ ownerLocalTransform.getRotation().multLocal(transform.getRotation());
+ ownerLocalTransform.getScale().multLocal(transform.getScale());
+ break;
+ case CONSTRAINT_SPACE_WORLD:
+ Matrix4f m = this.getParentWorldTransformMatrix();
+ m.invertLocal();
+ Matrix4f matrix = this.toMatrix(transform);
+ m.multLocal(matrix);
+
+ float scaleX = (float) Math.sqrt(m.m00 * m.m00 + m.m10 * m.m10 + m.m20 * m.m20);
+ float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21);
+ float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22);
+
+ transform.setTranslation(m.toTranslationVector());
+ transform.setRotation(m.toRotationQuat());
+ transform.setScale(scaleX, scaleY, scaleZ);
+ spatial.setLocalTransform(transform);
+ break;
+ case CONSTRAINT_SPACE_PARLOCAL:
+ case CONSTRAINT_SPACE_POSE:
+ throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object.");
+ default:
+ throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+ }
+ } else {// Bone
+ switch (space) {
+ case CONSTRAINT_SPACE_LOCAL:
+ bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
+ break;
+ case CONSTRAINT_SPACE_WORLD:
+ Matrix4f m = this.getParentWorldTransformMatrix();
+// m.invertLocal();
+ transform.setTranslation(m.mult(transform.getTranslation()));
+ transform.setRotation(m.mult(transform.getRotation(), null));
+ transform.setScale(transform.getScale());
+ bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
+// float x = FastMath.HALF_PI/2;
+// float y = -FastMath.HALF_PI;
+// float z = -FastMath.HALF_PI/2;
+// bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1));
+ break;
+ case CONSTRAINT_SPACE_PARLOCAL:
+ Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation());
+ Quaternion parentLocalRotation = bone.getLocalRotation().mult(transform.getRotation());
+ bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale());
+ break;
+ case CONSTRAINT_SPACE_POSE:
+ bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
+ break;
+ default:
+ throw new IllegalStateException("Invalid space type for target object: " + space.toString());
+ }
+ }
+ }
+
+ /**
+ * @return world transform matrix of the feature
+ */
+ public Matrix4f getWorldTransformMatrix() {
+ if (spatial != null) {
+ Matrix4f result = new Matrix4f();
+ Transform t = spatial.getWorldTransform();
+ result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
+ return result;
+ }
+ // Bone
+ Matrix4f result = new Matrix4f();
+ result.setTransform(bone.getWorldBindPosition(), bone.getWorldBindScale(), bone.getWorldBindRotation().toRotationMatrix());
+ return result;
+ }
+
+ /**
+ * @return world transform matrix of the feature's parent or identity matrix
+ * if the feature has no parent
+ */
+ public Matrix4f getParentWorldTransformMatrix() {
+ Matrix4f result = new Matrix4f();
+ if (spatial != null) {
+ if (spatial.getParent() != null) {
+ Transform t = spatial.getParent().getWorldTransform();
+ result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
+ }
+ } else {// Bone
+ Bone parent = bone.getParent();
+ if (parent != null) {
+ result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Converts given transform to the matrix.
+ *
+ * @param transform
+ * the transform to be converted
+ * @return 4x4 matri that represents the given transform
+ */
+ protected Matrix4f toMatrix(Transform transform) {
+ Matrix4f result = Matrix4f.IDENTITY;
+ if (transform != null) {
+ result = new Matrix4f();
+ result.setTranslation(transform.getTranslation());
+ result.setRotationQuaternion(transform.getRotation());
+ result.setScale(transform.getScale());
+ }
+ return result;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java b/engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java
new file mode 100644
index 0000000..ebf6096
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java
@@ -0,0 +1,136 @@
+package com.jme3.scene.plugins.blender.curves;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize
+ * floating point operations errors.
+ * @author Marcin Roguski
+ */
+public class BezierCurve {
+
+ public static final int X_VALUE = 0;
+ public static final int Y_VALUE = 1;
+ public static final int Z_VALUE = 2;
+ /**
+ * The type of the curve. Describes the data it modifies.
+ * Used in ipos calculations.
+ */
+ private int type;
+ /** The dimension of the curve. */
+ private int dimension;
+ /** A table of the bezier points. */
+ private float[][][] bezierPoints;
+
+ @SuppressWarnings("unchecked")
+ public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {
+ if (dimension != 2 && dimension != 3) {
+ throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!");
+ }
+ this.type = type;
+ this.dimension = dimension;
+ //first index of the bezierPoints table has the length of triples amount
+ //the second index points to a table od three points of a bezier triple (handle, point, handle)
+ //the third index specifies the coordinates of the specific point in a bezier triple
+ bezierPoints = new float[bezTriples.size()][3][dimension];
+ int i = 0, j, k;
+ for (Structure bezTriple : bezTriples) {
+ DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec");
+ for (j = 0; j < 3; ++j) {
+ for (k = 0; k < dimension; ++k) {
+ bezierPoints[i][j][k] = vec.get(j, k).floatValue();
+ }
+ }
+ ++i;
+ }
+ }
+
+ /**
+ * This method evaluates the data for the specified frame. The Y value is returned.
+ * @param frame
+ * the frame for which the value is being calculated
+ * @param valuePart
+ * this param specifies wheather we should return the X, Y or Z part of the result value; it should have
+ * one of the following values: X_VALUE - the X factor of the result Y_VALUE - the Y factor of the result
+ * Z_VALUE - the Z factor of the result
+ * @return the value of the curve
+ */
+ public float evaluate(int frame, int valuePart) {
+ for (int i = 0; i < bezierPoints.length - 1; ++i) {
+ if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) {
+ float t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]);
+ float oneMinusT = 1.0f - t;
+ float oneMinusT2 = oneMinusT * oneMinusT;
+ float t2 = t * t;
+ return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t;
+ }
+ }
+ if (frame < bezierPoints[0][1][0]) {
+ return bezierPoints[0][1][1];
+ } else { //frame>bezierPoints[bezierPoints.length-1][1][0]
+ return bezierPoints[bezierPoints.length - 1][1][1];
+ }
+ }
+
+ /**
+ * This method returns the frame where last bezier triple center point of the bezier curve is located.
+ * @return the frame number of the last defined bezier triple point for the curve
+ */
+ public int getLastFrame() {
+ return (int) bezierPoints[bezierPoints.length - 1][1][0];
+ }
+
+ /**
+ * This method returns the type of the bezier curve. The type describes the parameter that this curve modifies
+ * (ie. LocationX or rotationW of the feature).
+ * @return the type of the bezier curve
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * This method returns a list of control points for this curve.
+ * @return a list of control points for this curve.
+ */
+ public List<Vector3f> getControlPoints() {
+ List<Vector3f> controlPoints = new ArrayList<Vector3f>(bezierPoints.length * 3);
+ for (int i = 0; i < bezierPoints.length; ++i) {
+ controlPoints.add(new Vector3f(bezierPoints[i][0][0], bezierPoints[i][0][1], bezierPoints[i][0][2]));
+ controlPoints.add(new Vector3f(bezierPoints[i][1][0], bezierPoints[i][1][1], bezierPoints[i][1][2]));
+ controlPoints.add(new Vector3f(bezierPoints[i][2][0], bezierPoints[i][2][1], bezierPoints[i][2][2]));
+ }
+ return controlPoints;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Bezier curve: ").append(type).append('\n');
+ for (int i = 0; i < bezierPoints.length; ++i) {
+ sb.append(this.toStringBezTriple(i)).append('\n');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * This method converts the bezier triple of a specified index into text.
+ * @param tripleIndex
+ * index of the triple
+ * @return text representation of the triple
+ */
+ private String toStringBezTriple(int tripleIndex) {
+ if (this.dimension == 2) {
+ return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ") ("
+ + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ") ("
+ + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ")]";
+ } else {
+ return "[(" + bezierPoints[tripleIndex][0][0] + ", " + bezierPoints[tripleIndex][0][1] + ", " + bezierPoints[tripleIndex][0][2] + ") ("
+ + bezierPoints[tripleIndex][1][0] + ", " + bezierPoints[tripleIndex][1][1] + ", " + bezierPoints[tripleIndex][1][2] + ") ("
+ + bezierPoints[tripleIndex][2][0] + ", " + bezierPoints[tripleIndex][2][1] + ", " + bezierPoints[tripleIndex][2][2] + ")]";
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java
new file mode 100644
index 0000000..af30f3b
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java
@@ -0,0 +1,589 @@
+package com.jme3.scene.plugins.blender.curves;
+
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.math.Spline.SplineType;
+import com.jme3.math.*;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.*;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+import com.jme3.scene.plugins.blender.meshes.MeshHelper;
+import com.jme3.scene.plugins.blender.objects.Properties;
+import com.jme3.scene.shape.Curve;
+import com.jme3.scene.shape.Surface;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+/**
+ * A class that is used in mesh calculations.
+ * @author Marcin Roguski
+ */
+public class CurvesHelper extends AbstractBlenderHelper {
+
+ private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName());
+ /** Minimum basis U function degree for NURBS curves and surfaces. */
+ protected int minimumBasisUFunctionDegree = 4;
+ /** Minimum basis V function degree for NURBS curves and surfaces. */
+ protected int minimumBasisVFunctionDegree = 4;
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+ * different blender versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public CurvesHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object
+ * can have several separate curves.
+ * @param curveStructure
+ * the curve structure
+ * @param blenderContext
+ * the blender context
+ * @return a list of geometries repreenting a single curve object
+ * @throws BlenderFileException
+ */
+ public List<Geometry> toCurve(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException {
+ String name = curveStructure.getName();
+ int flag = ((Number) curveStructure.getFieldValue("flag")).intValue();
+ boolean is3D = (flag & 0x01) != 0;
+ boolean isFront = (flag & 0x02) != 0 && !is3D;
+ boolean isBack = (flag & 0x04) != 0 && !is3D;
+ if (isFront) {
+ LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face
+ }
+ if (isBack) {
+ LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face
+ }
+
+ //reading nurbs (and sorting them by material)
+ List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(blenderContext);
+ Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
+ for (Structure nurb : nurbStructures) {
+ Number matNumber = (Number) nurb.getFieldValue("mat_nr");
+ List<Structure> nurbList = nurbs.get(matNumber);
+ if (nurbList == null) {
+ nurbList = new ArrayList<Structure>();
+ nurbs.put(matNumber, nurbList);
+ }
+ nurbList.add(nurb);
+ }
+
+ //getting materials
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ Material[] materials = materialHelper.getMaterials(curveStructure, blenderContext);
+ if (materials == null) {
+ materials = new Material[]{blenderContext.getDefaultMaterial().clone()};
+ }
+ for (Material material : materials) {
+ material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
+ }
+
+ //getting or creating bevel object
+ List<Geometry> bevelObject = null;
+ Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj");
+ if (pBevelObject.isNotNull()) {
+ Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data");
+ Structure bevelStructure = pBevelStructure.fetchData(blenderContext.getInputStream()).get(0);
+ bevelObject = this.toCurve(bevelStructure, blenderContext);
+ } else {
+ int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue();
+ float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue();
+ float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue();
+ if (bevelDepth > 0.0f) {
+ float handlerLength = bevelDepth / 2.0f;
+
+ List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude > 0.0f ? 19 : 13);
+ conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));
+ conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength + extrude, 0));
+
+ conrtolPoints.add(new Vector3f(-handlerLength, bevelDepth + extrude, 0));
+ conrtolPoints.add(new Vector3f(0, bevelDepth + extrude, 0));
+ conrtolPoints.add(new Vector3f(handlerLength, bevelDepth + extrude, 0));
+
+ conrtolPoints.add(new Vector3f(bevelDepth, extrude + handlerLength, 0));
+ conrtolPoints.add(new Vector3f(bevelDepth, extrude, 0));
+ conrtolPoints.add(new Vector3f(bevelDepth, extrude - handlerLength, 0));
+
+ if (extrude > 0.0f) {
+ conrtolPoints.add(new Vector3f(bevelDepth, -extrude + handlerLength, 0));
+ conrtolPoints.add(new Vector3f(bevelDepth, -extrude, 0));
+ conrtolPoints.add(new Vector3f(bevelDepth, -extrude - handlerLength, 0));
+ }
+
+ conrtolPoints.add(new Vector3f(handlerLength, -bevelDepth - extrude, 0));
+ conrtolPoints.add(new Vector3f(0, -bevelDepth - extrude, 0));
+ conrtolPoints.add(new Vector3f(-handlerLength, -bevelDepth - extrude, 0));
+
+ conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength - extrude, 0));
+ conrtolPoints.add(new Vector3f(-bevelDepth, -extrude, 0));
+
+ if (extrude > 0.0f) {
+ conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength - extrude, 0));
+
+ conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength + extrude, 0));
+ conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));
+ }
+
+ Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false);
+ Curve bevelCurve = new Curve(bevelSpline, bevResol);
+ bevelObject = new ArrayList<Geometry>(1);
+ bevelObject.add(new Geometry("", bevelCurve));
+ } else if (extrude > 0.0f) {
+ Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[]{
+ new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0)
+ }, 1, false);
+ Curve bevelCurve = new Curve(bevelSpline, bevResol);
+ bevelObject = new ArrayList<Geometry>(1);
+ bevelObject.add(new Geometry("", bevelCurve));
+ }
+ }
+
+ //getting taper object
+ Curve taperObject = null;
+ Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");
+ if (bevelObject != null && pTaperObject.isNotNull()) {
+ Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data");
+ Structure taperStructure = pTaperStructure.fetchData(blenderContext.getInputStream()).get(0);
+ taperObject = this.loadTaperObject(taperStructure, blenderContext);
+ }
+
+ Vector3f loc = this.getLoc(curveStructure);
+ //creating the result curves
+ List<Geometry> result = new ArrayList<Geometry>(nurbs.size());
+ for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {
+ for (Structure nurb : nurbEntry.getValue()) {
+ int type = ((Number) nurb.getFieldValue("type")).intValue();
+ List<Geometry> nurbGeoms = null;
+ if ((type & 0x01) != 0) {//Bezier curve
+ nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, blenderContext);
+ } else if ((type & 0x04) != 0) {//NURBS
+ nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, blenderContext);
+ }
+ if (nurbGeoms != null) {//setting the name and assigning materials
+ for (Geometry nurbGeom : nurbGeoms) {
+ nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]);
+ nurbGeom.setName(name);
+ result.add(nurbGeom);
+ }
+ }
+ }
+ }
+
+ //reading custom properties
+ Properties properties = this.loadProperties(curveStructure, blenderContext);
+ if(properties != null && properties.getValue() != null) {
+ for(Geometry geom : result) {
+ geom.setUserData("properties", properties);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * This method loads the bezier curve.
+ * @param loc
+ * the translation of the curve
+ * @param nurb
+ * the nurb structure
+ * @param bevelObject
+ * the bevel object
+ * @param taperObject
+ * the taper object
+ * @param blenderContext
+ * the blender context
+ * @return a list of geometries representing the curves
+ * @throws BlenderFileException
+ * an exception is thrown when there are problems with the blender file
+ */
+ protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
+ BlenderContext blenderContext) throws BlenderFileException {
+ Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
+ List<Geometry> result = new ArrayList<Geometry>();
+ if (pBezierTriple.isNotNull()) {
+ boolean smooth = (((Number) nurb.getFlatFieldValue("flag")).intValue() & 0x01) != 0;
+ int resolution = ((Number) nurb.getFieldValue("resolu")).intValue();
+ boolean cyclic = (((Number) nurb.getFieldValue("flagu")).intValue() & 0x01) != 0;
+
+ //creating the curve object
+ BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3);
+ List<Vector3f> controlPoints = bezierCurve.getControlPoints();
+ if (cyclic) {
+ //copy the first three points at the end
+ for (int i = 0; i < 3; ++i) {
+ controlPoints.add(controlPoints.get(i));
+ }
+ }
+ //removing the first and last handles
+ controlPoints.remove(0);
+ controlPoints.remove(controlPoints.size() - 1);
+
+ //creating curve
+ Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);
+ Curve curve = new Curve(spline, resolution);
+ if (bevelObject == null) {//creating a normal curve
+ Geometry curveGeometry = new Geometry(null, curve);
+ result.add(curveGeometry);
+ //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added
+ } else {//creating curve with bevel and taper shape
+ result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, blenderContext);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method loads the NURBS curve or surface.
+ * @param loc
+ * object's location
+ * @param nurb
+ * the NURBS data structure
+ * @param bevelObject
+ * the bevel object to be applied
+ * @param taperObject
+ * the taper object to be applied
+ * @param blenderContext
+ * the blender context
+ * @return a list of geometries that represents the loaded NURBS curve or surface
+ * @throws BlenderFileException
+ * an exception is throw when problems with blender loaded data occurs
+ */
+ @SuppressWarnings("unchecked")
+ protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
+ BlenderContext blenderContext) throws BlenderFileException {
+ //loading the knots
+ List<Float>[] knots = new List[2];
+ Pointer[] pKnots = new Pointer[]{(Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv")};
+ for (int i = 0; i < knots.length; ++i) {
+ if (pKnots[i].isNotNull()) {
+ FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress());
+ BlenderInputStream blenderInputStream = blenderContext.getInputStream();
+ blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());
+ int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;
+ knots[i] = new ArrayList<Float>(knotsAmount);
+ for (int j = 0; j < knotsAmount; ++j) {
+ knots[i].add(Float.valueOf(blenderInputStream.readFloat()));
+ }
+ }
+ }
+
+ //loading the flags and orders (basis functions degrees)
+ int flagU = ((Number) nurb.getFieldValue("flagu")).intValue();
+ int flagV = ((Number) nurb.getFieldValue("flagv")).intValue();
+ int orderU = ((Number) nurb.getFieldValue("orderu")).intValue();
+ int orderV = ((Number) nurb.getFieldValue("orderv")).intValue();
+
+ //loading control points and their weights
+ int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue();
+ int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue();
+ List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(blenderContext.getInputStream());
+ List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
+ for (int i = 0; i < pntsV; ++i) {
+ List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
+ for (int j = 0; j < pntsU; ++j) {
+ DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec");
+ if (fixUpAxis) {
+ uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));
+ } else {
+ uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));
+ }
+ }
+ if ((flagU & 0x01) != 0) {
+ for (int k = 0; k < orderU - 1; ++k) {
+ uControlPoints.add(uControlPoints.get(k));
+ }
+ }
+ controlPoints.add(uControlPoints);
+ }
+ if ((flagV & 0x01) != 0) {
+ for (int k = 0; k < orderV - 1; ++k) {
+ controlPoints.add(controlPoints.get(k));
+ }
+ }
+
+ int resolu = ((Number) nurb.getFieldValue("resolu")).intValue() + 1;
+ List<Geometry> result;
+ if (knots[1] == null) {//creating the curve
+ Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]);
+ Curve nurbCurve = new Curve(nurbSpline, resolu);
+ if (bevelObject != null) {
+ result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, blenderContext);//TODO: smooth
+ } else {
+ result = new ArrayList<Geometry>(1);
+ Geometry nurbGeometry = new Geometry("", nurbCurve);
+ result.add(nurbGeometry);
+ }
+ } else {//creating the nurb surface
+ int resolv = ((Number) nurb.getFieldValue("resolv")).intValue() + 1;
+ Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV);
+ Geometry nurbGeometry = new Geometry("", nurbSurface);
+ result = new ArrayList<Geometry>(1);
+ result.add(nurbGeometry);
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the taper scale that should be applied to the object.
+ * @param taperPoints
+ * the taper points
+ * @param taperLength
+ * the taper curve length
+ * @param percent
+ * the percent of way along the whole taper curve
+ * @param store
+ * the vector where the result will be stored
+ */
+ protected float getTaperScale(float[] taperPoints, float taperLength, float percent) {
+ float length = taperLength * percent;
+ float currentLength = 0;
+ Vector3f p = new Vector3f();
+ int i;
+ for (i = 0; i < taperPoints.length - 6 && currentLength < length; i += 3) {
+ p.set(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]);
+ p.subtractLocal(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]);
+ currentLength += p.length();
+ }
+ currentLength -= p.length();
+ float leftLength = length - currentLength;
+ float percentOnSegment = p.length() == 0 ? 0 : leftLength / p.length();
+ Vector3f store = FastMath.interpolateLinear(percentOnSegment,
+ new Vector3f(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]),
+ new Vector3f(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]));
+ return store.y;
+ }
+
+ /**
+ * This method applies bevel and taper objects to the curve.
+ * @param curve
+ * the curve we apply the objects to
+ * @param bevelObject
+ * the bevel object
+ * @param taperObject
+ * the taper object
+ * @param smooth
+ * the smooth flag
+ * @param blenderContext
+ * the blender context
+ * @return a list of geometries representing the beveled and/or tapered curve
+ */
+ protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject,
+ boolean smooth, BlenderContext blenderContext) {
+ float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));
+ MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
+ float curveLength = curve.getLength();
+ //TODO: use the smooth var
+
+ //taper data
+ float[] taperPoints = null;
+ float taperLength = 0;
+ if (taperObject != null) {
+ taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position));
+ taperLength = taperObject.getLength();
+ }
+
+ //several objects can be allocated only once
+ Vector3f p = new Vector3f();
+ Vector3f z = new Vector3f(0, 0, 1);
+ Vector3f negativeY = new Vector3f(0, -1, 0);
+ Matrix4f m = new Matrix4f();
+ float lengthAlongCurve = 0, taperScale = 1.0f;
+ Quaternion planeRotation = new Quaternion();
+ Quaternion zRotation = new Quaternion();
+ float[] temp = new float[]{0, 0, 0, 1};
+ Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//normalMap merges normals of faces that will be rendered smooth
+
+ FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];
+ FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];
+ IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()];
+ for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
+ Mesh mesh = bevelObject.get(geomIndex).getMesh();
+ FloatBuffer positions = mesh.getFloatBuffer(Type.Position);
+ float[] vertices = BufferUtils.getFloatArray(positions);
+
+ for (int i = 0; i < curvePoints.length; i += 3) {
+ p.set(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);
+ Vector3f v;
+ if (i == 0) {
+ v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z);
+ } else if (i + 3 >= curvePoints.length) {
+ v = new Vector3f(p.x - curvePoints[i - 3], p.y - curvePoints[i - 2], p.z - curvePoints[i - 1]);
+ lengthAlongCurve += v.length();
+ } else {
+ v = new Vector3f(curvePoints[i + 3] - curvePoints[i - 3],
+ curvePoints[i + 4] - curvePoints[i - 2],
+ curvePoints[i + 5] - curvePoints[i - 1]);
+ lengthAlongCurve += new Vector3f(curvePoints[i + 3] - p.x, curvePoints[i + 4] - p.y, curvePoints[i + 5] - p.z).length();
+ }
+ v.normalizeLocal();
+
+ float angle = FastMath.acos(v.dot(z));
+ v.crossLocal(z).normalizeLocal();//v is the rotation axis now
+ planeRotation.fromAngleAxis(angle, v);
+
+ Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal();
+ float zAxisRotationAngle = FastMath.acos(negativeY.dot(v));
+ zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector);
+
+ //point transformation matrix
+ if (taperPoints != null) {
+ taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength);
+ }
+ m.set(Matrix4f.IDENTITY);
+ m.setRotationQuaternion(planeRotation.multLocal(zRotation));
+ m.setTranslation(p);
+
+ //these vertices need to be thrown on XY plane
+ //and moved to the origin of [p1.x, p1.y] on the plane
+ Vector3f[] verts = new Vector3f[vertices.length / 3];
+ for (int j = 0; j < verts.length; ++j) {
+ temp[0] = vertices[j * 3] * taperScale;
+ temp[1] = vertices[j * 3 + 1] * taperScale;
+ temp[2] = 0;
+ m.mult(temp);//the result is stored in the array
+ if (fixUpAxis) {//TODO: not the other way ???
+ verts[j] = new Vector3f(temp[0], temp[1], temp[2]);
+ } else {
+ verts[j] = new Vector3f(temp[0], temp[2], -temp[1]);
+ }
+ }
+ if (vertexBuffers[geomIndex] == null) {
+ vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length);
+ }
+ FloatBuffer buffer = BufferUtils.createFloatBuffer(verts);
+ vertexBuffers[geomIndex].put(buffer);
+
+ //adding indexes
+ IntBuffer indexBuffer = indexBuffers[geomIndex];
+ if (indexBuffer == null) {
+ //the amount of faces in the final mesh is the amount of edges in the bevel curve
+ //(which is less by 1 than its number of vertices)
+ //multiplied by 2 (because each edge has two faces assigned on both sides)
+ //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve
+ //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached
+ //and at last multiply everything by 3 because each face needs 3 indexes to be described
+ int bevelCurveEdgesAmount = verts.length - 1;
+ indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3);
+ indexBuffers[geomIndex] = indexBuffer;
+ }
+ int pointOffset = i / 3 * verts.length;
+ if (i + 3 < curvePoints.length) {
+ for (int index = 0; index < verts.length - 1; ++index) {
+ indexBuffer.put(index + pointOffset);
+ indexBuffer.put(index + pointOffset + 1);
+ indexBuffer.put(verts.length + index + pointOffset);
+ indexBuffer.put(verts.length + index + pointOffset);
+ indexBuffer.put(index + pointOffset + 1);
+ indexBuffer.put(verts.length + index + pointOffset + 1);
+ }
+ }
+ }
+ }
+
+ //calculating the normals
+ for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
+ Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]);
+ int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);
+ for (int i = 0; i < allIndices.length - 3; i += 3) {
+ Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
+ meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
+ }
+ if (normalBuffers[geomIndex] == null) {
+ normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);
+ }
+ for (Vector3f v : allVerts) {
+ Vector3f n = normalMap.get(v);
+ normalBuffers[geomIndex].put(n.x);
+ normalBuffers[geomIndex].put(n.y);
+ normalBuffers[geomIndex].put(n.z);
+ }
+ }
+
+ List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);
+ Float oneReferenceToCurveLength = new Float(curveLength);//its important for array modifier to use one reference here
+ for (int i = 0; i < vertexBuffers.length; ++i) {
+ Mesh mesh = new Mesh();
+ mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);
+ mesh.setBuffer(Type.Index, 3, indexBuffers[i]);
+ mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);
+ Geometry g = new Geometry("g" + i, mesh);
+ g.setUserData("curveLength", oneReferenceToCurveLength);
+ g.updateModelBound();
+ result.add(g);
+ }
+
+ return result;
+ }
+
+ /**
+ * This method loads the taper object.
+ * @param taperStructure
+ * the taper structure
+ * @param blenderContext
+ * the blender context
+ * @return the taper object
+ * @throws BlenderFileException
+ */
+ protected Curve loadTaperObject(Structure taperStructure, BlenderContext blenderContext) throws BlenderFileException {
+ //reading nurbs
+ List<Structure> nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase(blenderContext);
+ for (Structure nurb : nurbStructures) {
+ Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
+ if (pBezierTriple.isNotNull()) {
+ //creating the curve object
+ BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3);
+ List<Vector3f> controlPoints = bezierCurve.getControlPoints();
+ //removing the first and last handles
+ controlPoints.remove(0);
+ controlPoints.remove(controlPoints.size() - 1);
+
+ //return the first taper curve that has more than 3 control points
+ if (controlPoints.size() > 3) {
+ Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);
+ int resolution = ((Number) taperStructure.getFieldValue("resolu")).intValue();
+ return new Curve(spline, resolution);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the translation of the curve. The UP axis is taken into account here.
+ * @param curveStructure
+ * the curve structure
+ * @return curve translation
+ */
+ @SuppressWarnings("unchecked")
+ protected Vector3f getLoc(Structure curveStructure) {
+ DynamicArray<Number> locArray = (DynamicArray<Number>) curveStructure.getFieldValue("loc");
+ if (fixUpAxis) {
+ return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue());
+ } else {
+ return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue());
+ }
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java b/engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java
new file mode 100644
index 0000000..e0b40b3
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/exceptions/BlenderFileException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.exceptions;
+
+/**
+ * This exception is thrown when blend file data is somehow invalid.
+ * @author Marcin Roguski
+ */
+public class BlenderFileException extends Exception {
+
+ private static final long serialVersionUID = 7573482836437866767L;
+
+ /**
+ * Constructor. Creates an exception with no description.
+ */
+ public BlenderFileException() {
+ }
+
+ /**
+ * Constructor. Creates an exception containing the given message.
+ * @param message
+ * the message describing the problem that occured
+ */
+ public BlenderFileException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor. Creates an exception that is based upon other thrown object. It contains the whole stacktrace then.
+ * @param throwable
+ * an exception/error that occured
+ */
+ public BlenderFileException(Throwable throwable) {
+ super(throwable);
+ }
+
+ /**
+ * Constructor. Creates an exception with both a message and stacktrace.
+ * @param message
+ * the message describing the problem that occured
+ * @param throwable
+ * an exception/error that occured
+ */
+ public BlenderFileException(String message, Throwable throwable) {
+ super(message, throwable);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java
new file mode 100644
index 0000000..0f58474
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/BlenderInputStream.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * An input stream with random access to data.
+ * @author Marcin Roguski
+ */
+public class BlenderInputStream extends InputStream {
+
+ private static final Logger LOGGER = Logger.getLogger(BlenderInputStream.class.getName());
+ /** The default size of the blender buffer. */
+ private static final int DEFAULT_BUFFER_SIZE = 1048576; //1MB
+ /** The application's asset manager. */
+ private AssetManager assetManager;
+ /**
+ * Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes and '-' means 8 bytes.
+ */
+ private int pointerSize;
+ /**
+ * Type of byte ordering used; 'v' means little endian and 'V' means big endian.
+ */
+ private char endianess;
+ /** Version of Blender the file was created in; '248' means version 2.48. */
+ private String versionNumber;
+ /** The buffer we store the read data to. */
+ protected byte[] cachedBuffer;
+ /** The total size of the stored data. */
+ protected int size;
+ /** The current position of the read cursor. */
+ protected int position;
+ /** The input stream we read the data from. */
+ protected InputStream inputStream;
+
+ /**
+ * Constructor. The input stream is stored and used to read data.
+ * @param inputStream
+ * the stream we read data from
+ * @param assetManager
+ * the application's asset manager
+ * @throws BlenderFileException
+ * this exception is thrown if the file header has some invalid data
+ */
+ public BlenderInputStream(InputStream inputStream, AssetManager assetManager) throws BlenderFileException {
+ this.assetManager = assetManager;
+ this.inputStream = inputStream;
+ //the size value will canche while reading the file; the available() method cannot be counted on
+ try {
+ size = inputStream.available();
+ } catch (IOException e) {
+ size = 0;
+ }
+ if (size <= 0) {
+ size = BlenderInputStream.DEFAULT_BUFFER_SIZE;
+ }
+
+ //buffered input stream is used here for much faster file reading
+ BufferedInputStream bufferedInputStream;
+ if (inputStream instanceof BufferedInputStream) {
+ bufferedInputStream = (BufferedInputStream) inputStream;
+ } else {
+ bufferedInputStream = new BufferedInputStream(inputStream);
+ }
+
+ try {
+ this.readStreamToCache(bufferedInputStream);
+ } catch (IOException e) {
+ throw new BlenderFileException("Problems occured while caching the file!", e);
+ }
+
+ try {
+ this.readFileHeader();
+ } catch (BlenderFileException e) {//the file might be packed, don't panic, try one more time ;)
+ this.decompressFile();
+ this.position = 0;
+ this.readFileHeader();
+ }
+ }
+
+ /**
+ * This method reads the whole stream into a buffer.
+ * @param inputStream
+ * the stream to read the file data from
+ * @throws IOException
+ * an exception is thrown when data read from the stream is invalid or there are problems with i/o
+ * operations
+ */
+ private void readStreamToCache(InputStream inputStream) throws IOException {
+ int data = inputStream.read();
+ cachedBuffer = new byte[size];
+ size = 0;//this will count the actual size
+ while (data != -1) {
+ cachedBuffer[size++] = (byte) data;
+ if (size >= cachedBuffer.length) {//widen the cached array
+ byte[] newBuffer = new byte[cachedBuffer.length + (cachedBuffer.length >> 1)];
+ System.arraycopy(cachedBuffer, 0, newBuffer, 0, cachedBuffer.length);
+ cachedBuffer = newBuffer;
+ }
+ data = inputStream.read();
+ }
+ }
+
+ /**
+ * This method is used when the blender file is gzipped. It decompresses the data and stores it back into the
+ * cachedBuffer field.
+ */
+ private void decompressFile() {
+ GZIPInputStream gis = null;
+ try {
+ gis = new GZIPInputStream(new ByteArrayInputStream(cachedBuffer));
+ this.readStreamToCache(gis);
+ } catch (IOException e) {
+ throw new IllegalStateException("IO errors occured where they should NOT! "
+ + "The data is already buffered at this point!", e);
+ } finally {
+ try {
+ if (gis != null) {
+ gis.close();
+ }
+ } catch (IOException e) {
+ LOGGER.warning(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * This method loads the header from the given stream during instance creation.
+ * @param inputStream
+ * the stream we read the header from
+ * @throws BlenderFileException
+ * this exception is thrown if the file header has some invalid data
+ */
+ private void readFileHeader() throws BlenderFileException {
+ byte[] identifier = new byte[7];
+ int bytesRead = this.readBytes(identifier);
+ if (bytesRead != 7) {
+ throw new BlenderFileException("Error reading header identifier. Only " + bytesRead + " bytes read and there should be 7!");
+ }
+ String strIdentifier = new String(identifier);
+ if (!"BLENDER".equals(strIdentifier)) {
+ throw new BlenderFileException("Wrong file identifier: " + strIdentifier + "! Should be 'BLENDER'!");
+ }
+ char pointerSizeSign = (char) this.readByte();
+ if (pointerSizeSign == '-') {
+ pointerSize = 8;
+ } else if (pointerSizeSign == '_') {
+ pointerSize = 4;
+ } else {
+ throw new BlenderFileException("Invalid pointer size character! Should be '_' or '-' and there is: " + pointerSizeSign);
+ }
+ endianess = (char) this.readByte();
+ if (endianess != 'v' && endianess != 'V') {
+ throw new BlenderFileException("Unknown endianess value! 'v' or 'V' expected and found: " + endianess);
+ }
+ byte[] versionNumber = new byte[3];
+ bytesRead = this.readBytes(versionNumber);
+ if (bytesRead != 3) {
+ throw new BlenderFileException("Error reading version numberr. Only " + bytesRead + " bytes read and there should be 3!");
+ }
+ this.versionNumber = new String(versionNumber);
+ }
+
+ @Override
+ public int read() throws IOException {
+ return this.readByte();
+ }
+
+ /**
+ * This method reads 1 byte from the stream.
+ * It works just in the way the read method does.
+ * It just not throw an exception because at this moment the whole file
+ * is loaded into buffer, so no need for IOException to be thrown.
+ * @return a byte from the stream (1 bytes read)
+ */
+ public int readByte() {
+ return cachedBuffer[position++] & 0xFF;
+ }
+
+ /**
+ * This method reads a bytes number big enough to fill the table.
+ * It does not throw exceptions so it is for internal use only.
+ * @param bytes
+ * an array to be filled with data
+ * @return number of read bytes (a length of array actually)
+ */
+ private int readBytes(byte[] bytes) {
+ for (int i = 0; i < bytes.length; ++i) {
+ bytes[i] = (byte) this.readByte();
+ }
+ return bytes.length;
+ }
+
+ /**
+ * This method reads 2-byte number from the stream.
+ * @return a number from the stream (2 bytes read)
+ */
+ public int readShort() {
+ int part1 = this.readByte();
+ int part2 = this.readByte();
+ if (endianess == 'v') {
+ return (part2 << 8) + part1;
+ } else {
+ return (part1 << 8) + part2;
+ }
+ }
+
+ /**
+ * This method reads 4-byte number from the stream.
+ * @return a number from the stream (4 bytes read)
+ */
+ public int readInt() {
+ int part1 = this.readByte();
+ int part2 = this.readByte();
+ int part3 = this.readByte();
+ int part4 = this.readByte();
+ if (endianess == 'v') {
+ return (part4 << 24) + (part3 << 16) + (part2 << 8) + part1;
+ } else {
+ return (part1 << 24) + (part2 << 16) + (part3 << 8) + part4;
+ }
+ }
+
+ /**
+ * This method reads 4-byte floating point number (float) from the stream.
+ * @return a number from the stream (4 bytes read)
+ */
+ public float readFloat() {
+ int intValue = this.readInt();
+ return Float.intBitsToFloat(intValue);
+ }
+
+ /**
+ * This method reads 8-byte number from the stream.
+ * @return a number from the stream (8 bytes read)
+ */
+ public long readLong() {
+ long part1 = this.readInt();
+ long part2 = this.readInt();
+ long result = -1;
+ if (endianess == 'v') {
+ result = part2 << 32 | part1;
+ } else {
+ result = part1 << 32 | part2;
+ }
+ return result;
+ }
+
+ /**
+ * This method reads 8-byte floating point number (double) from the stream.
+ * @return a number from the stream (8 bytes read)
+ */
+ public double readDouble() {
+ long longValue = this.readLong();
+ return Double.longBitsToDouble(longValue);
+ }
+
+ /**
+ * This method reads the pointer value. Depending on the pointer size defined in the header, the stream reads either
+ * 4 or 8 bytes of data.
+ * @return the pointer value
+ */
+ public long readPointer() {
+ if (pointerSize == 4) {
+ return this.readInt();
+ }
+ return this.readLong();
+ }
+
+ /**
+ * This method reads the string. It assumes the string is terminated with zero in the stream.
+ * @return the string read from the stream
+ */
+ public String readString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ int data = this.readByte();
+ while (data != 0) {
+ stringBuilder.append((char) data);
+ data = this.readByte();
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * This method sets the current position of the read cursor.
+ * @param position
+ * the position of the read cursor
+ */
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ /**
+ * This method returns the position of the read cursor.
+ * @return the position of the read cursor
+ */
+ public int getPosition() {
+ return position;
+ }
+
+ /**
+ * This method returns the blender version number where the file was created.
+ * @return blender version number
+ */
+ public String getVersionNumber() {
+ return versionNumber;
+ }
+
+ /**
+ * This method returns the size of the pointer.
+ * @return the size of the pointer
+ */
+ public int getPointerSize() {
+ return pointerSize;
+ }
+
+ /**
+ * This method returns the application's asset manager.
+ * @return the application's asset manager
+ */
+ public AssetManager getAssetManager() {
+ return assetManager;
+ }
+
+ /**
+ * This method aligns cursor position forward to a given amount of bytes.
+ * @param bytesAmount
+ * the byte amount to which we aligh the cursor
+ */
+ public void alignPosition(int bytesAmount) {
+ if (bytesAmount <= 0) {
+ throw new IllegalArgumentException("Alignment byte number shoulf be positivbe!");
+ }
+ long move = position % bytesAmount;
+ if (move > 0) {
+ position += bytesAmount - move;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ inputStream.close();
+// cachedBuffer = null;
+// size = position = 0;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java
new file mode 100644
index 0000000..30922be
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/DnaBlockData.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The data block containing the description of the file.
+ * @author Marcin Roguski
+ */
+public class DnaBlockData {
+
+ private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A'; //SDNA
+ private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E'; //NAME
+ private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E'; //TYPE
+ private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N'; //TLEN
+ private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C'; //STRC
+ /** Structures available inside the file. */
+ private final Structure[] structures;
+ /** A map that helps finding a structure by type. */
+ private final Map<String, Structure> structuresMap;
+
+ /**
+ * Constructor. Loads the block from the given stream during instance creation.
+ * @param inputStream
+ * the stream we read the block from
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is throw if the blend file is invalid or somehow corrupted
+ */
+ public DnaBlockData(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
+ int identifier;
+
+ //reading 'SDNA' identifier
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
+ | inputStream.readByte() << 8 | inputStream.readByte();
+
+ if (identifier != SDNA_ID) {
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier));
+ }
+
+ //reading names
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
+ | inputStream.readByte() << 8 | inputStream.readByte();
+ if (identifier != NAME_ID) {
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier));
+ }
+ int amount = inputStream.readInt();
+ if (amount <= 0) {
+ throw new BlenderFileException("The names amount number should be positive!");
+ }
+ String[] names = new String[amount];
+ for (int i = 0; i < amount; ++i) {
+ names[i] = inputStream.readString();
+ }
+
+ //reding types
+ inputStream.alignPosition(4);
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
+ | inputStream.readByte() << 8 | inputStream.readByte();
+ if (identifier != TYPE_ID) {
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier));
+ }
+ amount = inputStream.readInt();
+ if (amount <= 0) {
+ throw new BlenderFileException("The types amount number should be positive!");
+ }
+ String[] types = new String[amount];
+ for (int i = 0; i < amount; ++i) {
+ types[i] = inputStream.readString();
+ }
+
+ //reading lengths
+ inputStream.alignPosition(4);
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
+ | inputStream.readByte() << 8 | inputStream.readByte();
+ if (identifier != TLEN_ID) {
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier));
+ }
+ int[] lengths = new int[amount];//theamount is the same as int types
+ for (int i = 0; i < amount; ++i) {
+ lengths[i] = inputStream.readShort();
+ }
+
+ //reading structures
+ inputStream.alignPosition(4);
+ identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
+ | inputStream.readByte() << 8 | inputStream.readByte();
+ if (identifier != STRC_ID) {
+ throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier));
+ }
+ amount = inputStream.readInt();
+ if (amount <= 0) {
+ throw new BlenderFileException("The structures amount number should be positive!");
+ }
+ structures = new Structure[amount];
+ structuresMap = new HashMap<String, Structure>(amount);
+ for (int i = 0; i < amount; ++i) {
+ structures[i] = new Structure(inputStream, names, types, blenderContext);
+ if (structuresMap.containsKey(structures[i].getType())) {
+ throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!");
+ }
+ structuresMap.put(structures[i].getType(), structures[i]);
+ }
+ }
+
+ /**
+ * This method returns the amount of the structures.
+ * @return the amount of the structures
+ */
+ public int getStructuresCount() {
+ return structures.length;
+ }
+
+ /**
+ * This method returns the structure of the given index.
+ * @param index
+ * the index of the structure
+ * @return the structure of the given index
+ */
+ public Structure getStructure(int index) {
+ try {
+ return (Structure) structures[index].clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException("Structure should be clonable!!!", e);
+ }
+ }
+
+ /**
+ * This method returns a structure of the given name. If the name does not exists then null is returned.
+ * @param name
+ * the name of the structure
+ * @return the required structure or null if the given name is inapropriate
+ */
+ public Structure getStructure(String name) {
+ try {
+ return (Structure) structuresMap.get(name).clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * This method indicates if the structure of the given name exists.
+ * @param name
+ * the name of the structure
+ * @return true if the structure exists and false otherwise
+ */
+ public boolean hasStructure(String name) {
+ return structuresMap.containsKey(name);
+ }
+
+ /**
+ * This method converts the given identifier code to string.
+ * @param code
+ * the code taht is to be converted
+ * @return the string value of the identifier
+ */
+ private String toString(int code) {
+ char c1 = (char) ((code & 0xFF000000) >> 24);
+ char c2 = (char) ((code & 0xFF0000) >> 16);
+ char c3 = (char) ((code & 0xFF00) >> 8);
+ char c4 = (char) (code & 0xFF);
+ return String.valueOf(c1) + c2 + c3 + c4;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n');
+ for (Structure structure : structures) {
+ stringBuilder.append(structure.toString()).append('\n');
+ }
+ return stringBuilder.append("===============").toString();
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java
new file mode 100644
index 0000000..3fff5a8
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/DynamicArray.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+
+/**
+ * An array that can be dynamically modified/
+ * @author Marcin Roguski
+ * @param <T>
+ * the type of stored data in the array
+ */
+public class DynamicArray<T> implements Cloneable {
+
+ /** An array object that holds the required data. */
+ private T[] array;
+ /**
+ * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a
+ * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:
+ * dynTable[a][b][c], where a,b,c are stored in the tableSizes table.
+ */
+ private int[] tableSizes;
+
+ /**
+ * Constructor. Builds an empty array of the specified sizes.
+ * @param tableSizes
+ * the sizes of the table
+ * @throws BlenderFileException
+ * an exception is thrown if one of the sizes is not a positive number
+ */
+ @SuppressWarnings("unchecked")
+ public DynamicArray(int[] tableSizes) throws BlenderFileException {
+ this.tableSizes = tableSizes;
+ int totalSize = 1;
+ for (int size : tableSizes) {
+ if (size <= 0) {
+ throw new BlenderFileException("The size of the table must be positive!");
+ }
+ totalSize *= size;
+ }
+ this.array = (T[]) new Object[totalSize];
+ }
+
+ /**
+ * Constructor. Builds an empty array of the specified sizes.
+ * @param tableSizes
+ * the sizes of the table
+ * @throws BlenderFileException
+ * an exception is thrown if one of the sizes is not a positive number
+ */
+ public DynamicArray(int[] tableSizes, T[] data) throws BlenderFileException {
+ this.tableSizes = tableSizes;
+ int totalSize = 1;
+ for (int size : tableSizes) {
+ if (size <= 0) {
+ throw new BlenderFileException("The size of the table must be positive!");
+ }
+ totalSize *= size;
+ }
+ if (totalSize != data.length) {
+ throw new IllegalArgumentException("The size of the table does not match the size of the given data!");
+ }
+ this.array = data;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ /**
+ * This method returns a value on the specified position. The dimension of the table is not taken into
+ * consideration.
+ * @param position
+ * the position of the data
+ * @return required data
+ */
+ public T get(int position) {
+ return array[position];
+ }
+
+ /**
+ * This method returns a value on the specified position in multidimensional array. Be careful not to exceed the
+ * table boundaries. Check the table's dimension first.
+ * @param position
+ * the position of the data indices of data position
+ * @return required data required data
+ */
+ public T get(int... position) {
+ if (position.length != tableSizes.length) {
+ throw new ArrayIndexOutOfBoundsException("The table accepts " + tableSizes.length + " indexing number(s)!");
+ }
+ int index = 0;
+ for (int i = 0; i < position.length - 1; ++i) {
+ index += position[i] * tableSizes[i + 1];
+ }
+ index += position[position.length - 1];
+ return array[index];
+ }
+
+ /**
+ * This method returns the total amount of data stored in the array.
+ * @return the total amount of data stored in the array
+ */
+ public int getTotalSize() {
+ return array.length;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ if (array instanceof Character[]) {//in case of character array we convert it to String
+ for (int i = 0; i < array.length && (Character) array[i] != '\0'; ++i) {//strings are terminater with '0'
+ result.append(array[i]);
+ }
+ } else {
+ result.append('[');
+ for (int i = 0; i < array.length; ++i) {
+ result.append(array[i].toString());
+ if (i + 1 < array.length) {
+ result.append(',');
+ }
+ }
+ result.append(']');
+ }
+ return result.toString();
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java
new file mode 100644
index 0000000..ef7802d
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/Field.java
@@ -0,0 +1,316 @@
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure.DataType;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a single field in the structure. It can be either a primitive type or a table or a reference to
+ * another structure.
+ * @author Marcin Roguski
+ */
+/*package*/
+class Field implements Cloneable {
+
+ private static final int NAME_LENGTH = 24;
+ private static final int TYPE_LENGTH = 16;
+ /** The blender context. */
+ public BlenderContext blenderContext;
+ /** The type of the field. */
+ public String type;
+ /** The name of the field. */
+ public String name;
+ /** The value of the field. Filled during data reading. */
+ public Object value;
+ /** This variable indicates the level of the pointer. */
+ public int pointerLevel;
+ /**
+ * This variable determines the sizes of the array. If the value is null the n the field is not an array.
+ */
+ public int[] tableSizes;
+ /** This variable indicates if the field is a function pointer. */
+ public boolean function;
+
+ /**
+ * Constructor. Saves the field data and parses its name.
+ * @param name
+ * the name of the field
+ * @param type
+ * the type of the field
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown if the names contain errors
+ */
+ public Field(String name, String type, BlenderContext blenderContext) throws BlenderFileException {
+ this.type = type;
+ this.blenderContext = blenderContext;
+ this.parseField(new StringBuilder(name));
+ }
+
+ /**
+ * Copy constructor. Used in clone method. Copying is not full. The value in the new object is not set so that we
+ * have a clead empty copy of the filed to fill with data.
+ * @param field
+ * the object that we copy
+ */
+ private Field(Field field) {
+ type = field.type;
+ name = field.name;
+ blenderContext = field.blenderContext;
+ pointerLevel = field.pointerLevel;
+ if (field.tableSizes != null) {
+ tableSizes = field.tableSizes.clone();
+ }
+ function = field.function;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return new Field(this);
+ }
+
+ /**
+ * This method fills the field wth data read from the input stream.
+ * @param blenderInputStream
+ * the stream we read data from
+ * @throws BlenderFileException
+ * an exception is thrown when the blend file is somehow invalid or corrupted
+ */
+ public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException {
+ int dataToRead = 1;
+ if (tableSizes != null && tableSizes.length > 0) {
+ for (int size : tableSizes) {
+ if (size <= 0) {
+ throw new BlenderFileException("The field " + name + " has invalid table size: " + size);
+ }
+ dataToRead *= size;
+ }
+ }
+ DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, blenderContext) : DataType.POINTER;
+ switch (dataType) {
+ case POINTER:
+ if (dataToRead == 1) {
+ Pointer pointer = new Pointer(pointerLevel, function, blenderContext);
+ pointer.fill(blenderInputStream);
+ value = pointer;
+ } else {
+ Pointer[] data = new Pointer[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ Pointer pointer = new Pointer(pointerLevel, function, blenderContext);
+ pointer.fill(blenderInputStream);
+ data[i] = pointer;
+ }
+ value = new DynamicArray<Pointer>(tableSizes, data);
+ }
+ break;
+ case CHARACTER:
+ //character is also stored as a number, because sometimes the new blender version uses
+ //other number type instead of character as a field type
+ //and characters are very often used as byte number stores instead of real chars
+ if (dataToRead == 1) {
+ value = Byte.valueOf((byte) blenderInputStream.readByte());
+ } else {
+ Character[] data = new Character[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ data[i] = Character.valueOf((char) blenderInputStream.readByte());
+ }
+ value = new DynamicArray<Character>(tableSizes, data);
+ }
+ break;
+ case SHORT:
+ if (dataToRead == 1) {
+ value = Integer.valueOf(blenderInputStream.readShort());
+ } else {
+ Number[] data = new Number[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ data[i] = Integer.valueOf(blenderInputStream.readShort());
+ }
+ value = new DynamicArray<Number>(tableSizes, data);
+ }
+ break;
+ case INTEGER:
+ if (dataToRead == 1) {
+ value = Integer.valueOf(blenderInputStream.readInt());
+ } else {
+ Number[] data = new Number[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ data[i] = Integer.valueOf(blenderInputStream.readInt());
+ }
+ value = new DynamicArray<Number>(tableSizes, data);
+ }
+ break;
+ case LONG:
+ if (dataToRead == 1) {
+ value = Long.valueOf(blenderInputStream.readLong());
+ } else {
+ Number[] data = new Number[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ data[i] = Long.valueOf(blenderInputStream.readLong());
+ }
+ value = new DynamicArray<Number>(tableSizes, data);
+ }
+ break;
+ case FLOAT:
+ if (dataToRead == 1) {
+ value = Float.valueOf(blenderInputStream.readFloat());
+ } else {
+ Number[] data = new Number[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ data[i] = Float.valueOf(blenderInputStream.readFloat());
+ }
+ value = new DynamicArray<Number>(tableSizes, data);
+ }
+ break;
+ case DOUBLE:
+ if (dataToRead == 1) {
+ value = Double.valueOf(blenderInputStream.readDouble());
+ } else {
+ Number[] data = new Number[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ data[i] = Double.valueOf(blenderInputStream.readDouble());
+ }
+ value = new DynamicArray<Number>(tableSizes, data);
+ }
+ break;
+ case VOID:
+ break;
+ case STRUCTURE:
+ if (dataToRead == 1) {
+ Structure structure = blenderContext.getDnaBlockData().getStructure(type);
+ structure.fill(blenderInputStream);
+ value = structure;
+ } else {
+ Structure[] data = new Structure[dataToRead];
+ for (int i = 0; i < dataToRead; ++i) {
+ Structure structure = blenderContext.getDnaBlockData().getStructure(type);
+ structure.fill(blenderInputStream);
+ data[i] = structure;
+ }
+ value = new DynamicArray<Structure>(tableSizes, data);
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unimplemented filling of type: " + type);
+ }
+ }
+
+ /**
+ * This method parses the field name to determine how the field should be used.
+ * @param nameBuilder
+ * the name of the field (given as StringBuilder)
+ * @throws BlenderFileException
+ * this exception is thrown if the names contain errors
+ */
+ private void parseField(StringBuilder nameBuilder) throws BlenderFileException {
+ this.removeWhitespaces(nameBuilder);
+ //veryfying if the name is a pointer
+ int pointerIndex = nameBuilder.indexOf("*");
+ while (pointerIndex >= 0) {
+ ++pointerLevel;
+ nameBuilder.deleteCharAt(pointerIndex);
+ pointerIndex = nameBuilder.indexOf("*");
+ }
+ //veryfying if the name is a function pointer
+ if (nameBuilder.indexOf("(") >= 0) {
+ function = true;
+ this.removeCharacter(nameBuilder, '(');
+ this.removeCharacter(nameBuilder, ')');
+ } else {
+ //veryfying if the name is a table
+ int tableStartIndex = 0;
+ List<Integer> lengths = new ArrayList<Integer>(3);//3 dimensions will be enough in most cases
+ do {
+ tableStartIndex = nameBuilder.indexOf("[");
+ if (tableStartIndex > 0) {
+ int tableStopIndex = nameBuilder.indexOf("]");
+ if (tableStopIndex < 0) {
+ throw new BlenderFileException("Invalid structure name: " + name);
+ }
+ try {
+ lengths.add(Integer.valueOf(nameBuilder.substring(tableStartIndex + 1, tableStopIndex)));
+ } catch (NumberFormatException e) {
+ throw new BlenderFileException("Invalid structure name caused by invalid table length: " + name, e);
+ }
+ nameBuilder.delete(tableStartIndex, tableStopIndex + 1);
+ }
+ } while (tableStartIndex > 0);
+ if (!lengths.isEmpty()) {
+ tableSizes = new int[lengths.size()];
+ for (int i = 0; i < tableSizes.length; ++i) {
+ tableSizes[i] = lengths.get(i).intValue();
+ }
+ }
+ }
+ name = nameBuilder.toString();
+ }
+
+ /**
+ * This method removes the required character from the text.
+ * @param text
+ * the text we remove characters from
+ * @param toRemove
+ * the character to be removed
+ */
+ private void removeCharacter(StringBuilder text, char toRemove) {
+ for (int i = 0; i < text.length(); ++i) {
+ if (text.charAt(i) == toRemove) {
+ text.deleteCharAt(i);
+ --i;
+ }
+ }
+ }
+
+ /**
+ * This method removes all whitespaces from the text.
+ * @param text
+ * the text we remove whitespaces from
+ */
+ private void removeWhitespaces(StringBuilder text) {
+ for (int i = 0; i < text.length(); ++i) {
+ if (Character.isWhitespace(text.charAt(i))) {
+ text.deleteCharAt(i);
+ --i;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ if (function) {
+ result.append('(');
+ }
+ for (int i = 0; i < pointerLevel; ++i) {
+ result.append('*');
+ }
+ result.append(name);
+ if (tableSizes != null) {
+ for (int i = 0; i < tableSizes.length; ++i) {
+ result.append('[').append(tableSizes[i]).append(']');
+ }
+ }
+ if (function) {
+ result.append(")()");
+ }
+ //insert appropriate amount of spaces to format the output corrently
+ int nameLength = result.length();
+ result.append(' ');//at least one space is a must
+ for (int i = 1; i < NAME_LENGTH - nameLength; ++i) {//we start from i=1 because one space is already added
+ result.append(' ');
+ }
+ result.append(type);
+ nameLength = result.length();
+ for (int i = 0; i < NAME_LENGTH + TYPE_LENGTH - nameLength; ++i) {
+ result.append(' ');
+ }
+ if (value instanceof Character) {
+ result.append(" = ").append((int) ((Character) value).charValue());
+ } else {
+ result.append(" = ").append(value != null ? value.toString() : "null");
+ }
+ return result.toString();
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java
new file mode 100644
index 0000000..c2ca6b0
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/FileBlockHeader.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+
+/**
+ * A class that holds the header data of a file block. The file block itself is not implemented. This class holds its
+ * start position in the stream and using this the structure can fill itself with the proper data.
+ * @author Marcin Roguski
+ */
+public class FileBlockHeader {
+
+ public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; //TE00
+ public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; //ME00
+ public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; //SR00
+ public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; //CA00
+ public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; //LA00
+ public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; //OB00
+ public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; //MA00
+ public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; //SC00
+ public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; //WO00
+ public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; //TX00
+ public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; //IP00
+ public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; //AC00
+ public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; //GLOB
+ public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; //REND
+ public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; //DATA
+ public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; //DNA1
+ public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; //ENDB
+ /** Identifier of the file-block [4 bytes]. */
+ private int code;
+ /** Total length of the data after the file-block-header [4 bytes]. */
+ private int size;
+ /**
+ * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer
+ * size)].
+ */
+ private long oldMemoryAddress;
+ /** Index of the SDNA structure [4 bytes]. */
+ private int sdnaIndex;
+ /** Number of structure located in this file-block [4 bytes]. */
+ private int count;
+ /** Start position of the block's data in the stream. */
+ private int blockPosition;
+
+ /**
+ * Constructor. Loads the block header from the given stream during instance creation.
+ * @param inputStream
+ * the stream we read the block header from
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the pointer size is neither 4 nor 8
+ */
+ public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
+ inputStream.alignPosition(4);
+ code = inputStream.readByte() << 24 | inputStream.readByte() << 16
+ | inputStream.readByte() << 8 | inputStream.readByte();
+ size = inputStream.readInt();
+ oldMemoryAddress = inputStream.readPointer();
+ sdnaIndex = inputStream.readInt();
+ count = inputStream.readInt();
+ blockPosition = inputStream.getPosition();
+ if (FileBlockHeader.BLOCK_DNA1 == code) {
+ blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext));
+ } else {
+ inputStream.setPosition(blockPosition + size);
+ blenderContext.addFileBlockHeader(Long.valueOf(oldMemoryAddress), this);
+ }
+ }
+
+ /**
+ * This method returns the structure described by the header filled with appropriate data.
+ * @param blenderContext
+ * the blender context
+ * @return structure filled with data
+ * @throws BlenderFileException
+ */
+ public Structure getStructure(BlenderContext blenderContext) throws BlenderFileException {
+ blenderContext.getInputStream().setPosition(blockPosition);
+ Structure structure = blenderContext.getDnaBlockData().getStructure(sdnaIndex);
+ structure.fill(blenderContext.getInputStream());
+ return structure;
+ }
+
+ /**
+ * This method returns the code of this data block.
+ * @return the code of this data block
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * This method returns the size of the data stored in this block.
+ * @return the size of the data stored in this block
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * This method returns the memory address.
+ * @return the memory address
+ */
+ public long getOldMemoryAddress() {
+ return oldMemoryAddress;
+ }
+
+ /**
+ * This method returns the sdna index.
+ * @return the sdna index
+ */
+ public int getSdnaIndex() {
+ return sdnaIndex;
+ }
+
+ /**
+ * This data returns the number of structure stored in the data block after this header.
+ * @return the number of structure stored in the data block after this header
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * This method returns the start position of the data block in the blend file stream.
+ * @return the start position of the data block
+ */
+ public int getBlockPosition() {
+ return blockPosition;
+ }
+
+ /**
+ * This method indicates if the block is the last block in the file.
+ * @return true if this block is the last one in the file nad false otherwise
+ */
+ public boolean isLastBlock() {
+ return FileBlockHeader.BLOCK_ENDB == code;
+ }
+
+ /**
+ * This method indicates if the block is the SDNA block.
+ * @return true if this block is the SDNA block and false otherwise
+ */
+ public boolean isDnaBlock() {
+ return FileBlockHeader.BLOCK_DNA1 == code;
+ }
+
+ @Override
+ public String toString() {
+ return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
+ }
+
+ /**
+ * This method transforms the coded bloch id into a string value.
+ * @param code
+ * the id of the block
+ * @return the string value of the block id
+ */
+ protected String codeToString(int code) {
+ char c1 = (char) ((code & 0xFF000000) >> 24);
+ char c2 = (char) ((code & 0xFF0000) >> 16);
+ char c3 = (char) ((code & 0xFF00) >> 8);
+ char c4 = (char) (code & 0xFF);
+ return String.valueOf(c1) + c2 + c3 + c4;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java
new file mode 100644
index 0000000..59f20e9
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class that represents a pointer of any level that can be stored in the file.
+ * @author Marcin Roguski
+ */
+public class Pointer {
+
+ /** The blender context. */
+ private BlenderContext blenderContext;
+ /** The level of the pointer. */
+ private int pointerLevel;
+ /** The address in file it points to. */
+ private long oldMemoryAddress;
+ /** This variable indicates if the field is a function pointer. */
+ public boolean function;
+
+ /**
+ * Constructr. Stores the basic data about the pointer.
+ * @param pointerLevel
+ * the level of the pointer
+ * @param function
+ * this variable indicates if the field is a function pointer
+ * @param blenderContext
+ * the repository f data; used in fetching the value that the pointer points
+ */
+ public Pointer(int pointerLevel, boolean function, BlenderContext blenderContext) {
+ this.pointerLevel = pointerLevel;
+ this.function = function;
+ this.blenderContext = blenderContext;
+ }
+
+ /**
+ * This method fills the pointer with its address value (it doesn't get the actual data yet. Use the 'fetch' method
+ * for this.
+ * @param inputStream
+ * the stream we read the pointer value from
+ */
+ public void fill(BlenderInputStream inputStream) {
+ oldMemoryAddress = inputStream.readPointer();
+ }
+
+ /**
+ * This method fetches the data stored under the given address.
+ * @param inputStream
+ * the stream we read data from
+ * @return the data read from the file
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public List<Structure> fetchData(BlenderInputStream inputStream) throws BlenderFileException {
+ if (oldMemoryAddress == 0) {
+ throw new NullPointerException("The pointer points to nothing!");
+ }
+ List<Structure> structures = null;
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(oldMemoryAddress);
+ if (pointerLevel > 1) {
+ int pointersAmount = dataFileBlock.getSize() / inputStream.getPointerSize() * dataFileBlock.getCount();
+ for (int i = 0; i < pointersAmount; ++i) {
+ inputStream.setPosition(dataFileBlock.getBlockPosition() + inputStream.getPointerSize() * i);
+ long oldMemoryAddress = inputStream.readPointer();
+ if (oldMemoryAddress != 0L) {
+ Pointer p = new Pointer(pointerLevel - 1, this.function, blenderContext);
+ p.oldMemoryAddress = oldMemoryAddress;
+ if (structures == null) {
+ structures = p.fetchData(inputStream);
+ } else {
+ structures.addAll(p.fetchData(inputStream));
+ }
+ }
+ }
+ } else {
+ inputStream.setPosition(dataFileBlock.getBlockPosition());
+ structures = new ArrayList<Structure>(dataFileBlock.getCount());
+ for (int i = 0; i < dataFileBlock.getCount(); ++i) {
+ Structure structure = blenderContext.getDnaBlockData().getStructure(dataFileBlock.getSdnaIndex());
+ structure.fill(inputStream);
+ structures.add(structure);
+ }
+ return structures;
+ }
+ return structures;
+ }
+
+ /**
+ * This method indicates if this pointer points to a function.
+ * @return <b>true</b> if this is a function pointer and <b>false</b> otherwise
+ */
+ public boolean isFunction() {
+ return function;
+ }
+
+ /**
+ * This method indicates if this is a null-pointer or not.
+ * @return <b>true</b> if the pointer is null and <b>false</b> otherwise
+ */
+ public boolean isNull() {
+ return oldMemoryAddress == 0;
+ }
+
+ /**
+ * This method indicates if this is a null-pointer or not.
+ * @return <b>true</b> if the pointer is not null and <b>false</b> otherwise
+ */
+ public boolean isNotNull() {
+ return oldMemoryAddress != 0;
+ }
+
+ /**
+ * This method returns the old memory address of the structure pointed by the pointer.
+ * @return the old memory address of the structure pointed by the pointer
+ */
+ public long getOldMemoryAddress() {
+ return oldMemoryAddress;
+ }
+
+ @Override
+ public String toString() {
+ return oldMemoryAddress == 0 ? "{$null$}" : "{$" + oldMemoryAddress + "$}";
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 + (int) (oldMemoryAddress ^ oldMemoryAddress >>> 32);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (this.getClass() != obj.getClass()) {
+ return false;
+ }
+ Pointer other = (Pointer) obj;
+ if (oldMemoryAddress != other.oldMemoryAddress) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java
new file mode 100644
index 0000000..bf4afa2
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/Structure.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.file;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class representing a single structure in the file.
+ * @author Marcin Roguski
+ */
+public class Structure implements Cloneable {
+
+ /** The blender context. */
+ private BlenderContext blenderContext;
+ /** The address of the block that fills the structure. */
+ private transient Long oldMemoryAddress;
+ /** The type of the structure. */
+ private String type;
+ /**
+ * The fields of the structure. Each field consists of a pair: name-type.
+ */
+ private Field[] fields;
+
+ /**
+ * Constructor that copies the data of the structure.
+ * @param structure
+ * the structure to copy.
+ * @param blenderContext
+ * the blender context of the structure
+ * @throws CloneNotSupportedException
+ * this exception should never be thrown
+ */
+ private Structure(Structure structure, BlenderContext blenderContext) throws CloneNotSupportedException {
+ type = structure.type;
+ fields = new Field[structure.fields.length];
+ for (int i = 0; i < fields.length; ++i) {
+ fields[i] = (Field) structure.fields[i].clone();
+ }
+ this.blenderContext = blenderContext;
+ this.oldMemoryAddress = structure.oldMemoryAddress;
+ }
+
+ /**
+ * Constructor. Loads the structure from the given stream during instance creation.
+ * @param inputStream
+ * the stream we read the structure from
+ * @param names
+ * the names from which the name of structure and its fields will be taken
+ * @param types
+ * the names of types for the structure
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception occurs if the amount of fields, defined in the file, is negative
+ */
+ public Structure(BlenderInputStream inputStream, String[] names, String[] types, BlenderContext blenderContext) throws BlenderFileException {
+ int nameIndex = inputStream.readShort();
+ type = types[nameIndex];
+ this.blenderContext = blenderContext;
+ int fieldsAmount = inputStream.readShort();
+ if (fieldsAmount < 0) {
+ throw new BlenderFileException("The amount of fields of " + this.type + " structure cannot be negative!");
+ }
+ if (fieldsAmount > 0) {
+ fields = new Field[fieldsAmount];
+ for (int i = 0; i < fieldsAmount; ++i) {
+ int typeIndex = inputStream.readShort();
+ nameIndex = inputStream.readShort();
+ fields[i] = new Field(names[nameIndex], types[typeIndex], blenderContext);
+ }
+ }
+ this.oldMemoryAddress = Long.valueOf(-1L);
+ }
+
+ /**
+ * This method fills the structure with data.
+ * @param inputStream
+ * the stream we read data from, its read cursor should be placed at the start position of the data for the
+ * structure
+ * @throws BlenderFileException
+ * an exception is thrown when the blend file is somehow invalid or corrupted
+ */
+ public void fill(BlenderInputStream inputStream) throws BlenderFileException {
+ int position = inputStream.getPosition();
+ inputStream.setPosition(position - 8 - inputStream.getPointerSize());
+ this.oldMemoryAddress = Long.valueOf(inputStream.readPointer());
+ inputStream.setPosition(position);
+ for (Field field : fields) {
+ field.fill(inputStream);
+ }
+ }
+
+ /**
+ * This method returns the value of the filed with a given name.
+ * @param fieldName
+ * the name of the field
+ * @return the value of the field or null if no field with a given name is found
+ */
+ public Object getFieldValue(String fieldName) {
+ for (Field field : fields) {
+ if (field.name.equalsIgnoreCase(fieldName)) {
+ return field.value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the value of the filed with a given name. The structure is considered to have flat fields
+ * only (no substructures).
+ * @param fieldName
+ * the name of the field
+ * @return the value of the field or null if no field with a given name is found
+ */
+ public Object getFlatFieldValue(String fieldName) {
+ for (Field field : fields) {
+ Object value = field.value;
+ if (field.name.equalsIgnoreCase(fieldName)) {
+ return value;
+ } else if (value instanceof Structure) {
+ value = ((Structure) value).getFlatFieldValue(fieldName);
+ if (value != null) {//we can compare references here, since we use one static object as a NULL field value
+ return value;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are
+ * held by this structure within the blend file.
+ * @param blenderContext
+ * the blender context
+ * @return a list of filled structures
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ * @throws IllegalArgumentException
+ * this exception is thrown if the type of the structure is not 'ListBase'
+ */
+ public List<Structure> evaluateListBase(BlenderContext blenderContext) throws BlenderFileException {
+ if (!"ListBase".equals(this.type)) {
+ throw new IllegalStateException("This structure is not of type: 'ListBase'");
+ }
+ Pointer first = (Pointer) this.getFieldValue("first");
+ Pointer last = (Pointer) this.getFieldValue("last");
+ long currentAddress = 0;
+ long lastAddress = last.getOldMemoryAddress();
+ List<Structure> result = new LinkedList<Structure>();
+ while (currentAddress != lastAddress) {
+ currentAddress = first.getOldMemoryAddress();
+ Structure structure = first.fetchData(blenderContext.getInputStream()).get(0);
+ result.add(structure);
+ first = (Pointer) structure.getFlatFieldValue("next");
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the type of the structure.
+ * @return the type of the structure
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * This method returns the amount of fields for the current structure.
+ * @return the amount of fields for the current structure
+ */
+ public int getFieldsAmount() {
+ return fields.length;
+ }
+
+ /**
+ * This method returns the field name of the given index.
+ * @param fieldIndex
+ * the index of the field
+ * @return the field name of the given index
+ */
+ public String getFieldName(int fieldIndex) {
+ return fields[fieldIndex].name;
+ }
+
+ /**
+ * This method returns the field type of the given index.
+ * @param fieldIndex
+ * the index of the field
+ * @return the field type of the given index
+ */
+ public String getFieldType(int fieldIndex) {
+ return fields[fieldIndex].type;
+ }
+
+ /**
+ * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception
+ * is thrown.
+ * @return the address of the feature stored in this structure
+ */
+ public Long getOldMemoryAddress() {
+ if (oldMemoryAddress.longValue() == -1L) {
+ throw new IllegalStateException("Call the 'fill' method and fill the structure with data first!");
+ }
+ return oldMemoryAddress;
+ }
+
+/**
+ * This method returns the name of the structure. If the structure has an ID field then the name is returned.
+ * Otherwise the name does not exists and the method returns null.
+ * @return the name of the structure read from the ID field or null
+ */
+ public String getName() {
+ Object fieldValue = this.getFieldValue("ID");
+ if(fieldValue instanceof Structure) {
+ Structure id = (Structure)fieldValue;
+ return id == null ? null : id.getFieldValue("name").toString().substring(2);//blender adds 2-charactes as a name prefix
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("struct ").append(type).append(" {\n");
+ for (int i = 0; i < fields.length; ++i) {
+ result.append(fields[i].toString()).append('\n');
+ }
+ return result.append('}').toString();
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return new Structure(this, blenderContext);
+ }
+
+ /**
+ * This enum enumerates all known data types that can be found in the blend file.
+ * @author Marcin Roguski
+ */
+ /*package*/
+ static enum DataType {
+
+ CHARACTER, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VOID, STRUCTURE, POINTER;
+ /** The map containing the known primary types. */
+ private static final Map<String, DataType> PRIMARY_TYPES = new HashMap<String, DataType>(10);
+
+ static {
+ PRIMARY_TYPES.put("char", CHARACTER);
+ PRIMARY_TYPES.put("uchar", CHARACTER);
+ PRIMARY_TYPES.put("short", SHORT);
+ PRIMARY_TYPES.put("ushort", SHORT);
+ PRIMARY_TYPES.put("int", INTEGER);
+ PRIMARY_TYPES.put("long", LONG);
+ PRIMARY_TYPES.put("ulong", LONG);
+ PRIMARY_TYPES.put("uint64_t", LONG);
+ PRIMARY_TYPES.put("float", FLOAT);
+ PRIMARY_TYPES.put("double", DOUBLE);
+ PRIMARY_TYPES.put("void", VOID);
+ }
+
+ /**
+ * This method returns the data type that is appropriate to the given type name. WARNING! The type recognition
+ * is case sensitive!
+ * @param type
+ * the type name of the data
+ * @param blenderContext
+ * the blender context
+ * @return appropriate enum value to the given type name
+ * @throws BlenderFileException
+ * this exception is thrown if the given type name does not exist in the blend file
+ */
+ public static DataType getDataType(String type, BlenderContext blenderContext) throws BlenderFileException {
+ DataType result = PRIMARY_TYPES.get(type);
+ if (result != null) {
+ return result;
+ }
+ if (blenderContext.getDnaBlockData().hasStructure(type)) {
+ return STRUCTURE;
+ }
+ throw new BlenderFileException("Unknown data type: " + type);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java
new file mode 100644
index 0000000..add3e21
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.lights;
+
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A class that is used in light calculations.
+ * @author Marcin Roguski
+ */
+public class LightHelper extends AbstractBlenderHelper {
+
+ private static final Logger LOGGER = Logger.getLogger(LightHelper.class.getName());
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+ * different blender versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public LightHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+ Light result = (Light) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (result != null) {
+ return result;
+ }
+ int type = ((Number) structure.getFieldValue("type")).intValue();
+ switch (type) {
+ case 0://Lamp
+ result = new PointLight();
+ float distance = ((Number) structure.getFieldValue("dist")).floatValue();
+ ((PointLight) result).setRadius(distance);
+ break;
+ case 1://Sun
+ LOGGER.log(Level.WARNING, "'Sun' lamp is not supported in jMonkeyEngine.");
+ break;
+ case 2://Spot
+ result = new SpotLight();
+ //range
+ ((SpotLight)result).setSpotRange(((Number) structure.getFieldValue("dist")).floatValue());
+ //outer angle
+ float outerAngle = ((Number) structure.getFieldValue("spotsize")).floatValue()*FastMath.DEG_TO_RAD * 0.5f;
+ ((SpotLight)result).setSpotOuterAngle(outerAngle);
+
+ //inner angle
+ float spotblend = ((Number) structure.getFieldValue("spotblend")).floatValue();
+ spotblend = FastMath.clamp(spotblend, 0, 1);
+ float innerAngle = outerAngle * (1 - spotblend);
+ ((SpotLight)result).setSpotInnerAngle(innerAngle);
+ break;
+ case 3://Hemi
+ LOGGER.log(Level.WARNING, "'Hemi' lamp is not supported in jMonkeyEngine.");
+ break;
+ case 4://Area
+ result = new DirectionalLight();
+ break;
+ default:
+ throw new BlenderFileException("Unknown light source type: " + type);
+ }
+ if (result != null) {
+ float r = ((Number) structure.getFieldValue("r")).floatValue();
+ float g = ((Number) structure.getFieldValue("g")).floatValue();
+ float b = ((Number) structure.getFieldValue("b")).floatValue();
+ result.setColor(new ColorRGBA(r, g, b, 1.0f));
+ }
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java
new file mode 100644
index 0000000..45e6f35
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/materials/IAlphaMask.java
@@ -0,0 +1,26 @@
+package com.jme3.scene.plugins.blender.materials;
+
+/**
+ * An interface used in calculating alpha mask during particles' texture calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ interface IAlphaMask {
+ /**
+ * This method sets the size of the texture's image.
+ * @param width
+ * the width of the image
+ * @param height
+ * the height of the image
+ */
+ void setImageSize(int width, int height);
+
+ /**
+ * This method returns the alpha value for the specified texture position.
+ * @param x
+ * the X coordinate of the texture position
+ * @param y
+ * the Y coordinate of the texture position
+ * @return the alpha value for the specified texture position
+ */
+ byte getAlpha(float x, float y);
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java
new file mode 100644
index 0000000..2e6b0cf
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java
@@ -0,0 +1,483 @@
+package com.jme3.scene.plugins.blender.materials;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper.DiffuseShader;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper.SpecularShader;
+import com.jme3.scene.plugins.blender.textures.TextureHelper;
+import com.jme3.scene.plugins.blender.textures.blending.TextureBlender;
+import com.jme3.scene.plugins.blender.textures.blending.TextureBlenderFactory;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.Type;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class holds the data about the material.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public final class MaterialContext {
+ private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName());
+
+ //texture mapping types
+ public static final int MTEX_COL = 0x01;
+ public static final int MTEX_NOR = 0x02;
+ public static final int MTEX_SPEC = 0x04;
+ public static final int MTEX_EMIT = 0x40;
+ public static final int MTEX_ALPHA = 0x80;
+
+ /* package */final String name;
+ /* package */final List<Structure> mTexs;
+ /* package */final List<Structure> textures;
+ /* package */final Map<Number, Texture> loadedTextures;
+ /* package */final Map<Texture, Structure> textureToMTexMap;
+ /* package */final int texturesCount;
+ /* package */final Type textureType;
+
+ /* package */final ColorRGBA diffuseColor;
+ /* package */final DiffuseShader diffuseShader;
+ /* package */final SpecularShader specularShader;
+ /* package */final ColorRGBA specularColor;
+ /* package */final ColorRGBA ambientColor;
+ /* package */final float shininess;
+ /* package */final boolean shadeless;
+ /* package */final boolean vertexColor;
+ /* package */final boolean transparent;
+ /* package */final boolean vTangent;
+
+ /* package */int uvCoordinatesType = -1;
+ /* package */int projectionType;
+
+ @SuppressWarnings("unchecked")
+ /* package */MaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+ name = structure.getName();
+
+ int mode = ((Number) structure.getFieldValue("mode")).intValue();
+ shadeless = (mode & 0x4) != 0;
+ vertexColor = (mode & 0x80) != 0;
+ vTangent = (mode & 0x4000000) != 0; // NOTE: Requires tangents
+
+ int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue();
+ diffuseShader = DiffuseShader.values()[diff_shader];
+
+ if(this.shadeless) {
+ float r = ((Number) structure.getFieldValue("r")).floatValue();
+ float g = ((Number) structure.getFieldValue("g")).floatValue();
+ float b = ((Number) structure.getFieldValue("b")).floatValue();
+ float alpha = ((Number) structure.getFieldValue("alpha")).floatValue();
+
+ diffuseColor = new ColorRGBA(r, g, b, alpha);
+ specularShader = null;
+ specularColor = ambientColor = null;
+ shininess = 0.0f;
+ } else {
+ diffuseColor = this.readDiffuseColor(structure, diffuseShader);
+
+ int spec_shader = ((Number) structure.getFieldValue("spec_shader")).intValue();
+ specularShader = SpecularShader.values()[spec_shader];
+ specularColor = this.readSpecularColor(structure, specularShader);
+
+ float r = ((Number) structure.getFieldValue("ambr")).floatValue();
+ float g = ((Number) structure.getFieldValue("ambg")).floatValue();
+ float b = ((Number) structure.getFieldValue("ambb")).floatValue();
+ float alpha = ((Number) structure.getFieldValue("alpha")).floatValue();
+ ambientColor = new ColorRGBA(r, g, b, alpha);
+
+ float shininess = ((Number) structure.getFieldValue("emit")).floatValue();
+ this.shininess = shininess > 0.0f ? shininess : MaterialHelper.DEFAULT_SHININESS;
+ }
+
+ mTexs = new ArrayList<Structure>();
+ textures = new ArrayList<Structure>();
+
+ DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
+ int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue();
+ Type firstTextureType = null;
+ for (int i = 0; i < mtexsArray.getTotalSize(); ++i) {
+ Pointer p = mtexsArray.get(i);
+ if (p.isNotNull() && (separatedTextures & 1 << i) == 0) {
+ Structure mtex = p.fetchData(blenderContext.getInputStream()).get(0);
+
+ // the first texture determines the texture coordinates type
+ if (uvCoordinatesType == -1) {
+ uvCoordinatesType = ((Number) mtex.getFieldValue("texco")).intValue();
+ projectionType = ((Number) mtex.getFieldValue("mapping")).intValue();
+ } else if (uvCoordinatesType != ((Number) mtex.getFieldValue("texco")).intValue()) {
+ LOGGER.log(Level.WARNING, "The texture with index: {0} has different UV coordinates type than the first texture! This texture will NOT be loaded!", i + 1);
+ continue;
+ }
+
+ Pointer pTex = (Pointer) mtex.getFieldValue("tex");
+ if (pTex.isNotNull()) {
+ Structure tex = pTex.fetchData(blenderContext.getInputStream()).get(0);
+ int type = ((Number) tex.getFieldValue("type")).intValue();
+ Type textureType = this.getType(type);
+ if (textureType != null) {
+ if (firstTextureType == null) {
+ firstTextureType = textureType;
+ mTexs.add(mtex);
+ textures.add(tex);
+ } else if (firstTextureType == textureType) {
+ mTexs.add(mtex);
+ textures.add(tex);
+ } else {
+ LOGGER.log(Level.WARNING, "The texture with index: {0} is of different dimension than the first one! This texture will NOT be loaded!", i + 1);
+ }
+ }
+ }
+ }
+ }
+
+ //loading the textures and merging them
+ Map<Number, List<Structure[]>> sortedTextures = this.sortAndFilterTextures();
+ loadedTextures = new HashMap<Number, Texture>(sortedTextures.size());
+ textureToMTexMap = new HashMap<Texture, Structure>();
+ float[] diffuseColorArray = new float[] {diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a};
+ TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+ for(Entry<Number, List<Structure[]>> entry : sortedTextures.entrySet()) {
+ if(entry.getValue().size()>0) {
+ List<Texture> textures = new ArrayList<Texture>(entry.getValue().size());
+ for(Structure[] mtexAndTex : entry.getValue()) {
+ int texflag = ((Number) mtexAndTex[0].getFieldValue("texflag")).intValue();
+ boolean negateTexture = (texflag & 0x04) != 0;
+ Texture texture = textureHelper.getTexture(mtexAndTex[1], blenderContext);
+ int blendType = ((Number) mtexAndTex[0].getFieldValue("blendtype")).intValue();
+ float[] color = new float[] { ((Number) mtexAndTex[0].getFieldValue("r")).floatValue(),
+ ((Number) mtexAndTex[0].getFieldValue("g")).floatValue(),
+ ((Number) mtexAndTex[0].getFieldValue("b")).floatValue() };
+ float colfac = ((Number) mtexAndTex[0].getFieldValue("colfac")).floatValue();
+ TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat());
+ texture = textureBlender.blend(diffuseColorArray, texture, color, colfac, blendType, negateTexture, blenderContext);
+ texture.setWrap(WrapMode.Repeat);
+ textures.add(texture);
+ textureToMTexMap.put(texture, mtexAndTex[0]);
+ }
+ loadedTextures.put(entry.getKey(), textureHelper.mergeTextures(textures, this));
+ }
+ }
+
+ this.texturesCount = mTexs.size();
+ this.textureType = firstTextureType;
+
+ //veryfying if the transparency is present
+ //(in blender transparent mask is 0x10000 but its better to verify it because blender can indicate transparency when
+ //it is not required
+ boolean transparent = false;
+ if(diffuseColor != null) {
+ transparent = diffuseColor.a < 1.0f;
+ if(sortedTextures.size() > 0) {//texutre covers the material color
+ diffuseColor.set(1, 1, 1, 1);
+ }
+ }
+ if(specularColor != null) {
+ transparent = transparent || specularColor.a < 1.0f;
+ }
+ if(ambientColor != null) {
+ transparent = transparent || ambientColor.a < 1.0f;
+ }
+ this.transparent = transparent;
+ }
+
+ /**
+ * This method sorts the textures by their mapping type.
+ * In each group only textures of one type are put (either two- or three-dimensional).
+ * If the mapping type is MTEX_COL then if the texture has no alpha channel then all textures before it are
+ * discarded and will not be loaded and merged because texture with no alpha will cover them anyway.
+ * @return a map with sorted and filtered textures
+ */
+ private Map<Number, List<Structure[]>> sortAndFilterTextures() {
+ Map<Number, List<Structure[]>> result = new HashMap<Number, List<Structure[]>>();
+ for (int i = 0; i < mTexs.size(); ++i) {
+ Structure mTex = mTexs.get(i);
+ Structure texture = textures.get(i);
+ Number mapto = (Number) mTex.getFieldValue("mapto");
+ List<Structure[]> mtexs = result.get(mapto);
+ if(mtexs==null) {
+ mtexs = new ArrayList<Structure[]>();
+ result.put(mapto, mtexs);
+ }
+ if(mapto.intValue() == MTEX_COL && this.isWithoutAlpha(textures.get(i))) {
+ mtexs.clear();//remove previous textures, they will be covered anyway
+ }
+ mtexs.add(new Structure[] {mTex, texture});
+ }
+ return result;
+ }
+
+ /**
+ * This method determines if the given texture has no alpha channel.
+ *
+ * @param texture
+ * the texture to check for alpha channel
+ * @return <b>true</b> if the texture has no alpha channel and <b>false</b>
+ * otherwise
+ */
+ private boolean isWithoutAlpha(Structure texture) {
+ int flag = ((Number) texture.getFieldValue("flag")).intValue();
+ if((flag & 0x01) == 0) {//the texture has no colorband
+ int type = ((Number) texture.getFieldValue("type")).intValue();
+ if(type==TextureHelper.TEX_MAGIC) {
+ return true;
+ }
+ if(type==TextureHelper.TEX_VORONOI) {
+ int voronoiColorType = ((Number) texture.getFieldValue("vn_coltype")).intValue();
+ return voronoiColorType != 0;//voronoiColorType == 0: intensity, voronoiColorType != 0: col1, col2 or col3
+ }
+ if(type==TextureHelper.TEX_CLOUDS) {
+ int sType = ((Number) texture.getFieldValue("stype")).intValue();
+ return sType == 1;//sType==0: without colors, sType==1: with colors
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method returns the current material's texture UV coordinates type.
+ * @return uv coordinates type
+ */
+ public int getUvCoordinatesType() {
+ return uvCoordinatesType;
+ }
+
+ /**
+ * This method returns the proper projection type for the material's texture.
+ * This applies only to 2D textures.
+ * @return texture's projection type
+ */
+ public int getProjectionType() {
+ return projectionType;
+ }
+
+ /**
+ * This method returns current material's texture dimension.
+ * @return the material's texture dimension
+ */
+ public int getTextureDimension() {
+ return this.textureType == Type.TwoDimensional ? 2 : 3;
+ }
+
+ /**
+ * This method returns the amount of textures applied for the current
+ * material.
+ *
+ * @return the amount of textures applied for the current material
+ */
+ public int getTexturesCount() {
+ return textures == null ? 0 : textures.size();
+ }
+
+ /**
+ * This method returns the projection array that indicates where the current coordinate factor X, Y or Z (represented
+ * by the index in the array) will be used where (indicated by the value in the array).
+ * For example the configuration: [1,2,3] means that X - coordinate will be used as X, Y as Y and Z as Z.
+ * The configuration [2,1,0] means that Z will be used instead of X coordinate, Y will be used as Y and
+ * Z will not be used at all (0 will be in its place).
+ * @param textureIndex
+ * the index of the texture
+ * @return texture projection array
+ */
+ public int[] getProjection(int textureIndex) {
+ Structure mtex = mTexs.get(textureIndex);
+ return new int[] { ((Number) mtex.getFieldValue("projx")).intValue(), ((Number) mtex.getFieldValue("projy")).intValue(), ((Number) mtex.getFieldValue("projz")).intValue() };
+ }
+
+ /**
+ * This method returns the diffuse color.
+ *
+ * @param materialStructure the material structure
+ * @param diffuseShader the diffuse shader
+ * @return the diffuse color
+ */
+ private ColorRGBA readDiffuseColor(Structure materialStructure, DiffuseShader diffuseShader) {
+ // bitwise 'or' of all textures mappings
+ int commonMapto = ((Number) materialStructure.getFieldValue("mapto")).intValue();
+
+ // diffuse color
+ float r = ((Number) materialStructure.getFieldValue("r")).floatValue();
+ float g = ((Number) materialStructure.getFieldValue("g")).floatValue();
+ float b = ((Number) materialStructure.getFieldValue("b")).floatValue();
+ float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();
+ if ((commonMapto & 0x01) == 0x01) {// Col
+ return new ColorRGBA(r, g, b, alpha);
+ } else {
+ switch (diffuseShader) {
+ case FRESNEL:
+ case ORENNAYAR:
+ case TOON:
+ break;// TODO: find what is the proper modification
+ case MINNAERT:
+ case LAMBERT:// TODO: check if that is correct
+ float ref = ((Number) materialStructure.getFieldValue("ref")).floatValue();
+ r *= ref;
+ g *= ref;
+ b *= ref;
+ break;
+ default:
+ throw new IllegalStateException("Unknown diffuse shader type: " + diffuseShader.toString());
+ }
+ return new ColorRGBA(r, g, b, alpha);
+ }
+ }
+
+ /**
+ * This method returns a specular color used by the material.
+ *
+ * @param materialStructure
+ * the material structure filled with data
+ * @return a specular color used by the material
+ */
+ private ColorRGBA readSpecularColor(Structure materialStructure, SpecularShader specularShader) {
+ float r = ((Number) materialStructure.getFieldValue("specr")).floatValue();
+ float g = ((Number) materialStructure.getFieldValue("specg")).floatValue();
+ float b = ((Number) materialStructure.getFieldValue("specb")).floatValue();
+ float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();
+ switch (specularShader) {
+ case BLINN:
+ case COOKTORRENCE:
+ case TOON:
+ case WARDISO:// TODO: find what is the proper modification
+ break;
+ case PHONG:// TODO: check if that is correct
+ float spec = ((Number) materialStructure.getFieldValue("spec")).floatValue();
+ r *= spec * 0.5f;
+ g *= spec * 0.5f;
+ b *= spec * 0.5f;
+ break;
+ default:
+ throw new IllegalStateException("Unknown specular shader type: " + specularShader.toString());
+ }
+ return new ColorRGBA(r, g, b, alpha);
+ }
+
+ /**
+ * This method determines the type of the texture.
+ * @param texType
+ * texture type (from blender)
+ * @return texture type (used by jme)
+ */
+ private Type getType(int texType) {
+ switch (texType) {
+ case TextureHelper.TEX_IMAGE:// (it is first because probably this will be most commonly used)
+ return Type.TwoDimensional;
+ case TextureHelper.TEX_CLOUDS:
+ case TextureHelper.TEX_WOOD:
+ case TextureHelper.TEX_MARBLE:
+ case TextureHelper.TEX_MAGIC:
+ case TextureHelper.TEX_BLEND:
+ case TextureHelper.TEX_STUCCI:
+ case TextureHelper.TEX_NOISE:
+ case TextureHelper.TEX_MUSGRAVE:
+ case TextureHelper.TEX_VORONOI:
+ case TextureHelper.TEX_DISTNOISE:
+ return Type.ThreeDimensional;
+ case TextureHelper.TEX_NONE:// No texture, do nothing
+ return null;
+ case TextureHelper.TEX_POINTDENSITY:
+ case TextureHelper.TEX_VOXELDATA:
+ case TextureHelper.TEX_PLUGIN:
+ case TextureHelper.TEX_ENVMAP:
+ LOGGER.log(Level.WARNING, "Texture type NOT supported: {0}", texType);
+ return null;
+ default:
+ throw new IllegalStateException("Unknown texture type: " + texType);
+ }
+ }
+
+ /**
+ * @return he material's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return a copy of diffuse color
+ */
+ public ColorRGBA getDiffuseColor() {
+ return diffuseColor.clone();
+ }
+
+ /**
+ * @return an enum describing the type of a diffuse shader used by this material
+ */
+ public DiffuseShader getDiffuseShader() {
+ return diffuseShader;
+ }
+
+ /**
+ * @return a copy of specular color
+ */
+ public ColorRGBA getSpecularColor() {
+ return specularColor.clone();
+ }
+
+ /**
+ * @return an enum describing the type of a specular shader used by this material
+ */
+ public SpecularShader getSpecularShader() {
+ return specularShader;
+ }
+
+ /**
+ * @return an ambient color used by the material
+ */
+ public ColorRGBA getAmbientColor() {
+ return ambientColor;
+ }
+
+ /**
+ * @return the sihiness of this material
+ */
+ public float getShininess() {
+ return shininess;
+ }
+
+ /**
+ * @return <b>true</b> if the material is shadeless and <b>false</b> otherwise
+ */
+ public boolean isShadeless() {
+ return shadeless;
+ }
+
+ /**
+ * @return <b>true</b> if the material uses vertex color and <b>false</b> otherwise
+ */
+ public boolean isVertexColor() {
+ return vertexColor;
+ }
+
+ /**
+ * @return <b>true</b> if the material is transparent and <b>false</b> otherwise
+ */
+ public boolean isTransparent() {
+ return transparent;
+ }
+
+ /**
+ * @return <b>true</b> if the material uses tangents and <b>false</b> otherwise
+ */
+ public boolean isvTangent() {
+ return vTangent;
+ }
+
+ /**
+ * @param texture
+ * the texture for which its mtex structure definition will be
+ * fetched
+ * @return mtex structure of the current texture or <b>null</b> if none
+ * exists
+ */
+ public Structure getMTex(Texture texture) {
+ return textureToMTexMap.get(texture);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
new file mode 100644
index 0000000..a25b727
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.materials;
+
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.material.MatParam;
+import com.jme3.material.MatParamTexture;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.Type;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class MaterialHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName());
+ protected static final float DEFAULT_SHININESS = 20.0f;
+
+ public static final String TEXTURE_TYPE_3D = "Texture";
+ public static final String TEXTURE_TYPE_COLOR = "ColorMap";
+ public static final String TEXTURE_TYPE_DIFFUSE = "DiffuseMap";
+ public static final String TEXTURE_TYPE_NORMAL = "NormalMap";
+ public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap";
+ public static final String TEXTURE_TYPE_GLOW = "GlowMap";
+ public static final String TEXTURE_TYPE_ALPHA = "AlphaMap";
+
+ public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0);
+ public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1);
+ public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2);
+ public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3);
+ protected final Map<Integer, IAlphaMask> alphaMasks = new HashMap<Integer, IAlphaMask>();
+
+ /**
+ * The type of the material's diffuse shader.
+ */
+ public static enum DiffuseShader {
+ LAMBERT, ORENNAYAR, TOON, MINNAERT, FRESNEL
+ }
+
+ /**
+ * The type of the material's specular shader.
+ */
+ public static enum SpecularShader {
+ COOKTORRENCE, PHONG, BLINN, TOON, WARDISO
+ }
+
+ /** Face cull mode. Should be excplicitly set before this helper is used. */
+ protected FaceCullMode faceCullMode;
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
+ * versions.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public MaterialHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, false);
+ // setting alpha masks
+ alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {
+ @Override
+ public void setImageSize(int width, int height) {}
+
+ @Override
+ public byte getAlpha(float x, float y) {
+ return (byte) 255;
+ }
+ });
+ alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() {
+ private float r;
+ private float[] center;
+
+ @Override
+ public void setImageSize(int width, int height) {
+ r = Math.min(width, height) * 0.5f;
+ center = new float[] { width * 0.5f, height * 0.5f };
+ }
+
+ @Override
+ public byte getAlpha(float x, float y) {
+ float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1])));
+ return (byte) (d >= r ? 0 : 255);
+ }
+ });
+ alphaMasks.put(ALPHA_MASK_CONE, new IAlphaMask() {
+ private float r;
+ private float[] center;
+
+ @Override
+ public void setImageSize(int width, int height) {
+ r = Math.min(width, height) * 0.5f;
+ center = new float[] { width * 0.5f, height * 0.5f };
+ }
+
+ @Override
+ public byte getAlpha(float x, float y) {
+ float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1])));
+ return (byte) (d >= r ? 0 : -255.0f * d / r + 255.0f);
+ }
+ });
+ alphaMasks.put(ALPHA_MASK_HYPERBOLE, new IAlphaMask() {
+ private float r;
+ private float[] center;
+
+ @Override
+ public void setImageSize(int width, int height) {
+ r = Math.min(width, height) * 0.5f;
+ center = new float[] { width * 0.5f, height * 0.5f };
+ }
+
+ @Override
+ public byte getAlpha(float x, float y) {
+ float d = FastMath.abs(FastMath.sqrt((x - center[0]) * (x - center[0]) + (y - center[1]) * (y - center[1]))) / r;
+ return d >= 1.0f ? 0 : (byte) ((-FastMath.sqrt((2.0f - d) * d) + 1.0f) * 255.0f);
+ }
+ });
+ }
+
+ /**
+ * This method sets the face cull mode to be used with every loaded material.
+ *
+ * @param faceCullMode
+ * the face cull mode
+ */
+ public void setFaceCullMode(FaceCullMode faceCullMode) {
+ this.faceCullMode = faceCullMode;
+ }
+
+ /**
+ * This method converts the material structure to jme Material.
+ * @param structure
+ * structure with material data
+ * @param blenderContext
+ * the blender context
+ * @return jme material
+ * @throws BlenderFileException
+ * an exception is throw when problems with blend file occur
+ */
+ public Material toMaterial(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.INFO, "Loading material.");
+ if (structure == null) {
+ return blenderContext.getDefaultMaterial();
+ }
+ Material result = (Material) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (result != null) {
+ return result;
+ }
+
+ MaterialContext materialContext = new MaterialContext(structure, blenderContext);
+ LOGGER.log(Level.INFO, "Material's name: {0}", materialContext.name);
+
+ if(materialContext.textures.size() > 1) {
+ LOGGER.log(Level.WARNING, "Attetion! Many textures found for material: {0}. Only the first of each supported mapping types will be used!", materialContext.name);
+ }
+
+ // texture
+ Type colorTextureType = null;
+ Map<String, Texture> texturesMap = new HashMap<String, Texture>();
+ for(Entry<Number, Texture> textureEntry : materialContext.loadedTextures.entrySet()) {
+ int mapto = textureEntry.getKey().intValue();
+ Texture texture = textureEntry.getValue();
+ if ((mapto & MaterialContext.MTEX_COL) != 0) {
+ colorTextureType = texture.getType();
+ if (materialContext.shadeless) {
+ texturesMap.put(colorTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_COLOR, texture);
+ } else {
+ texturesMap.put(colorTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_DIFFUSE, texture);
+ }
+ }
+ if(texture.getType()==Type.TwoDimensional) {//so far only 2D textures can be mapped in other way than color
+ if ((mapto & MaterialContext.MTEX_NOR) != 0 && !materialContext.shadeless) {
+ //Structure mTex = materialContext.getMTex(texture);
+ //Texture normalMapTexture = textureHelper.convertToNormalMapTexture(texture, ((Number) mTex.getFieldValue("norfac")).floatValue());
+ //texturesMap.put(TEXTURE_TYPE_NORMAL, normalMapTexture);
+ texturesMap.put(TEXTURE_TYPE_NORMAL, texture);
+ }
+ if ((mapto & MaterialContext.MTEX_EMIT) != 0) {
+ texturesMap.put(TEXTURE_TYPE_GLOW, texture);
+ }
+ if ((mapto & MaterialContext.MTEX_SPEC) != 0 && !materialContext.shadeless) {
+ texturesMap.put(TEXTURE_TYPE_SPECULAR, texture);
+ }
+ if ((mapto & MaterialContext.MTEX_ALPHA) != 0 && !materialContext.shadeless) {
+ texturesMap.put(TEXTURE_TYPE_ALPHA, texture);
+ }
+ }
+ }
+
+ //creating the material
+ if(colorTextureType==Type.ThreeDimensional) {
+ result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Texture3D/tex3D.j3md");
+ } else {
+ if (materialContext.shadeless) {
+ result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+
+ if (!materialContext.transparent) {
+ materialContext.diffuseColor.a = 1;
+ }
+
+ result.setColor("Color", materialContext.diffuseColor);
+ } else {
+ result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
+ result.setBoolean("UseMaterialColors", Boolean.TRUE);
+
+ // setting the colors
+ result.setBoolean("Minnaert", materialContext.diffuseShader == DiffuseShader.MINNAERT);
+ if (!materialContext.transparent) {
+ materialContext.diffuseColor.a = 1;
+ }
+ result.setColor("Diffuse", materialContext.diffuseColor);
+
+ result.setBoolean("WardIso", materialContext.specularShader == SpecularShader.WARDISO);
+ result.setColor("Specular", materialContext.specularColor);
+
+ result.setColor("Ambient", materialContext.ambientColor);
+ result.setFloat("Shininess", materialContext.shininess);
+ }
+
+ if (materialContext.vertexColor) {
+ result.setBoolean(materialContext.shadeless ? "VertexColor" : "UseVertexColor", true);
+ }
+ }
+
+ //applying textures
+ for(Entry<String, Texture> textureEntry : texturesMap.entrySet()) {
+ result.setTexture(textureEntry.getKey(), textureEntry.getValue());
+ }
+
+ //applying other data
+ result.getAdditionalRenderState().setFaceCullMode(faceCullMode);
+ if (materialContext.transparent) {
+ result.setTransparent(true);
+ result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ }
+
+ result.setName(materialContext.getName());
+ blenderContext.setMaterialContext(result, materialContext);
+ blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result);
+ return result;
+ }
+
+ /**
+ * This method returns a material similar to the one given but without textures. If the material has no textures it is not cloned but
+ * returned itself.
+ *
+ * @param material
+ * a material to be cloned without textures
+ * @param imageType
+ * type of image defined by blender; the constants are defined in TextureHelper
+ * @return material without textures of a specified type
+ */
+ public Material getNonTexturedMaterial(Material material, int imageType) {
+ String[] textureParamNames = new String[] { TEXTURE_TYPE_DIFFUSE, TEXTURE_TYPE_NORMAL, TEXTURE_TYPE_GLOW, TEXTURE_TYPE_SPECULAR, TEXTURE_TYPE_ALPHA };
+ Map<String, Texture> textures = new HashMap<String, Texture>(textureParamNames.length);
+ for (String textureParamName : textureParamNames) {
+ MatParamTexture matParamTexture = material.getTextureParam(textureParamName);
+ if (matParamTexture != null) {
+ textures.put(textureParamName, matParamTexture.getTextureValue());
+ }
+ }
+ if (textures.isEmpty()) {
+ return material;
+ } else {
+ // clear all textures first so that wo de not waste resources cloning them
+ for (Entry<String, Texture> textureParamName : textures.entrySet()) {
+ String name = textureParamName.getValue().getName();
+ try {
+ int type = Integer.parseInt(name);
+ if (type == imageType) {
+ material.clearParam(textureParamName.getKey());
+ }
+ } catch (NumberFormatException e) {
+ LOGGER.log(Level.WARNING, "The name of the texture does not contain the texture type value! {0} will not be removed!", name);
+ }
+ }
+ Material result = material.clone();
+ // put the textures back in place
+ for (Entry<String, Texture> textureEntry : textures.entrySet()) {
+ material.setTexture(textureEntry.getKey(), textureEntry.getValue());
+ }
+ return result;
+ }
+ }
+
+ /**
+ * This method converts the given material into particles-usable material.
+ * The texture and glow color are being copied.
+ * The method assumes it receives the Lighting type of material.
+ * @param material
+ * the source material
+ * @param blenderContext
+ * the blender context
+ * @return material converted into particles-usable material
+ */
+ public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, BlenderContext blenderContext) {
+ Material result = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
+
+ // copying texture
+ MatParam diffuseMap = material.getParam("DiffuseMap");
+ if (diffuseMap != null) {
+ Texture texture = ((Texture) diffuseMap.getValue()).clone();
+
+ // applying alpha mask to the texture
+ Image image = texture.getImage();
+ ByteBuffer sourceBB = image.getData(0);
+ sourceBB.rewind();
+ int w = image.getWidth();
+ int h = image.getHeight();
+ ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4);
+ IAlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex);
+ iAlphaMask.setImageSize(w, h);
+
+ for (int x = 0; x < w; ++x) {
+ for (int y = 0; y < h; ++y) {
+ bb.put(sourceBB.get());
+ bb.put(sourceBB.get());
+ bb.put(sourceBB.get());
+ bb.put(iAlphaMask.getAlpha(x, y));
+ }
+ }
+
+ image = new Image(Format.RGBA8, w, h, bb);
+ texture.setImage(image);
+
+ result.setTextureParam("Texture", VarType.Texture2D, texture);
+ }
+
+ // copying glow color
+ MatParam glowColor = material.getParam("GlowColor");
+ if (glowColor != null) {
+ ColorRGBA color = (ColorRGBA) glowColor.getValue();
+ result.setParam("GlowColor", VarType.Vector3, color);
+ }
+ return result;
+ }
+
+ /**
+ * This method indicates if the material has any kind of texture.
+ *
+ * @param material
+ * the material
+ * @return <b>true</b> if the texture exists in the material and <B>false</b> otherwise
+ */
+ public boolean hasTexture(Material material) {
+ if (material != null) {
+ if (material.getTextureParam(TEXTURE_TYPE_3D) != null) {
+ return true;
+ }
+ if (material.getTextureParam(TEXTURE_TYPE_ALPHA) != null) {
+ return true;
+ }
+ if (material.getTextureParam(TEXTURE_TYPE_COLOR) != null) {
+ return true;
+ }
+ if (material.getTextureParam(TEXTURE_TYPE_DIFFUSE) != null) {
+ return true;
+ }
+ if (material.getTextureParam(TEXTURE_TYPE_GLOW) != null) {
+ return true;
+ }
+ if (material.getTextureParam(TEXTURE_TYPE_NORMAL) != null) {
+ return true;
+ }
+ if (material.getTextureParam(TEXTURE_TYPE_SPECULAR) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method indicates if the material has a texture of a specified type.
+ *
+ * @param material
+ * the material
+ * @param textureType
+ * the type of the texture
+ * @return <b>true</b> if the texture exists in the material and <B>false</b> otherwise
+ */
+ public boolean hasTexture(Material material, String textureType) {
+ if (material != null) {
+ return material.getTextureParam(textureType) != null;
+ }
+ return false;
+ }
+
+ /**
+ * This method returns the table of materials connected to the specified structure. The given structure can be of any type (ie. mesh or
+ * curve) but needs to have 'mat' field/
+ *
+ * @param structureWithMaterials
+ * the structure containing the mesh data
+ * @param blenderContext
+ * the blender context
+ * @return a list of vertices colors, each color belongs to a single vertex
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public Material[] getMaterials(Structure structureWithMaterials, BlenderContext blenderContext) throws BlenderFileException {
+ Pointer ppMaterials = (Pointer) structureWithMaterials.getFieldValue("mat");
+ Material[] materials = null;
+ if (ppMaterials.isNotNull()) {
+ List<Structure> materialStructures = ppMaterials.fetchData(blenderContext.getInputStream());
+ if (materialStructures != null && materialStructures.size() > 0) {
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ materials = new Material[materialStructures.size()];
+ int i = 0;
+ for (Structure s : materialStructures) {
+ Material material = (Material) blenderContext.getLoadedFeature(s.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (material == null) {
+ material = materialHelper.toMaterial(s, blenderContext);
+ }
+ materials[i++] = material;
+ }
+ }
+ }
+ return materials;
+ }
+
+ /**
+ * This method converts rgb values to hsv values.
+ *
+ * @param r
+ * red value of the color
+ * @param g
+ * green value of the color
+ * @param b
+ * blue value of the color
+ * @param hsv
+ * hsv values of a color (this table contains the result of the transformation)
+ */
+ public void rgbToHsv(float r, float g, float b, float[] hsv) {
+ float cmax = r;
+ float cmin = r;
+ cmax = g > cmax ? g : cmax;
+ cmin = g < cmin ? g : cmin;
+ cmax = b > cmax ? b : cmax;
+ cmin = b < cmin ? b : cmin;
+
+ hsv[2] = cmax; /* value */
+ if (cmax != 0.0) {
+ hsv[1] = (cmax - cmin) / cmax;
+ } else {
+ hsv[1] = 0.0f;
+ hsv[0] = 0.0f;
+ }
+ if (hsv[1] == 0.0) {
+ hsv[0] = -1.0f;
+ } else {
+ float cdelta = cmax - cmin;
+ float rc = (cmax - r) / cdelta;
+ float gc = (cmax - g) / cdelta;
+ float bc = (cmax - b) / cdelta;
+ if (r == cmax) {
+ hsv[0] = bc - gc;
+ } else if (g == cmax) {
+ hsv[0] = 2.0f + rc - bc;
+ } else {
+ hsv[0] = 4.0f + gc - rc;
+ }
+ hsv[0] *= 60.0f;
+ if (hsv[0] < 0.0f) {
+ hsv[0] += 360.0f;
+ }
+ }
+
+ hsv[0] /= 360.0f;
+ if (hsv[0] < 0.0f) {
+ hsv[0] = 0.0f;
+ }
+ }
+
+ /**
+ * This method converts rgb values to hsv values.
+ *
+ * @param h
+ * hue
+ * @param s
+ * saturation
+ * @param v
+ * value
+ * @param rgb
+ * rgb result vector (should have 3 elements)
+ */
+ public void hsvToRgb(float h, float s, float v, float[] rgb) {
+ h *= 360.0f;
+ if (s == 0.0) {
+ rgb[0] = rgb[1] = rgb[2] = v;
+ } else {
+ if (h == 360) {
+ h = 0;
+ } else {
+ h /= 60;
+ }
+ int i = (int) Math.floor(h);
+ float f = h - i;
+ float p = v * (1.0f - s);
+ float q = v * (1.0f - s * f);
+ float t = v * (1.0f - s * (1.0f - f));
+ switch (i) {
+ case 0:
+ rgb[0] = v;
+ rgb[1] = t;
+ rgb[2] = p;
+ break;
+ case 1:
+ rgb[0] = q;
+ rgb[1] = v;
+ rgb[2] = p;
+ break;
+ case 2:
+ rgb[0] = p;
+ rgb[1] = v;
+ rgb[2] = t;
+ break;
+ case 3:
+ rgb[0] = p;
+ rgb[1] = q;
+ rgb[2] = v;
+ break;
+ case 4:
+ rgb[0] = t;
+ rgb[1] = p;
+ rgb[2] = v;
+ break;
+ case 5:
+ rgb[0] = v;
+ rgb[1] = p;
+ rgb[2] = q;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java
new file mode 100644
index 0000000..de43db7
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java
@@ -0,0 +1,105 @@
+package com.jme3.scene.plugins.blender.meshes;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.VertexBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class that holds information about the mesh.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class MeshContext {
+ /** The mesh stored here as a list of geometries. */
+ private List<Geometry> mesh;
+ /** Vertex list that is referenced by all the geometries. */
+ private List<Vector3f> vertexList;
+ /** The vertex reference map. */
+ private Map<Integer, List<Integer>> vertexReferenceMap;
+ /** The UV-coordinates for each of the geometries. */
+ private Map<Geometry, VertexBuffer> uvCoordinates = new HashMap<Geometry, VertexBuffer>();
+
+ /**
+ * This method returns the referenced mesh.
+ *
+ * @return the referenced mesh
+ */
+ public List<Geometry> getMesh() {
+ return mesh;
+ }
+
+ /**
+ * This method sets the referenced mesh.
+ *
+ * @param mesh
+ * the referenced mesh
+ */
+ public void setMesh(List<Geometry> mesh) {
+ this.mesh = mesh;
+ }
+
+ /**
+ * This method returns the vertex list.
+ *
+ * @return the vertex list
+ */
+ public List<Vector3f> getVertexList() {
+ return vertexList;
+ }
+
+ /**
+ * This method sets the vertex list.
+ *
+ * @param vertexList
+ * the vertex list
+ */
+ public void setVertexList(List<Vector3f> vertexList) {
+ this.vertexList = vertexList;
+ }
+
+ /**
+ * This method returns the vertex reference map.
+ *
+ * @return the vertex reference map
+ */
+ public Map<Integer, List<Integer>> getVertexReferenceMap() {
+ return vertexReferenceMap;
+ }
+
+ /**
+ * This method sets the vertex reference map.
+ *
+ * @param vertexReferenceMap
+ * the vertex reference map
+ */
+ public void setVertexReferenceMap(
+ Map<Integer, List<Integer>> vertexReferenceMap) {
+ this.vertexReferenceMap = vertexReferenceMap;
+ }
+
+ /**
+ * This method adds the mesh's UV-coordinates.
+ *
+ * @param geometry
+ * the mesh that has the UV-coordinates
+ * @param vertexBuffer
+ * the mesh's UV-coordinates
+ */
+ public void addUVCoordinates(Geometry geometry, VertexBuffer vertexBuffer) {
+ uvCoordinates.put(geometry, vertexBuffer);
+ }
+
+ /**
+ * This method returns the mesh's UV-coordinates.
+ *
+ * @param geometry
+ * the mesh
+ * @return the mesh's UV-coordinates
+ */
+ public VertexBuffer getUVCoordinates(Geometry geometry) {
+ return uvCoordinates.get(geometry);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
new file mode 100644
index 0000000..484e942
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.meshes;
+
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.materials.MaterialContext;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+import com.jme3.scene.plugins.blender.objects.Properties;
+import com.jme3.scene.plugins.blender.textures.TextureHelper;
+import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;
+import com.jme3.texture.Texture;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.*;
+import java.util.Map.Entry;
+
+/**
+ * A class that is used in mesh calculations.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class MeshHelper extends AbstractBlenderHelper {
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
+ * versions.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public MeshHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion,fixUpAxis);
+ }
+
+ /**
+ * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data.
+ *
+ * @param structure
+ * the structure we read the mesh from
+ * @return the mesh feature
+ * @throws BlenderFileException
+ */
+ @SuppressWarnings("unchecked")
+ public List<Geometry> toMesh(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
+ List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(),
+ LoadedFeatureDataType.LOADED_FEATURE);
+ if (geometries != null) {
+ List<Geometry> copiedGeometries = new ArrayList<Geometry>(geometries.size());
+ for (Geometry geometry : geometries) {
+ copiedGeometries.add(geometry.clone());
+ }
+ return copiedGeometries;
+ }
+
+ // helpers
+ TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
+
+ // reading mesh data
+ String name = structure.getName();
+ MeshContext meshContext = new MeshContext();
+
+ // reading vertices
+ Vector3f[] vertices = this.getVertices(structure, blenderContext);
+ int verticesAmount = vertices.length;
+
+ // vertices Colors
+ List<byte[]> verticesColors = this.getVerticesColors(structure, blenderContext);
+
+ // reading faces
+ // the following map sorts faces by material number (because in jme Mesh can have only one material)
+ Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
+ Pointer pMFace = (Pointer) structure.getFieldValue("mface");
+ List<Structure> mFaces = null;
+ if (pMFace.isNotNull()) {
+ mFaces = pMFace.fetchData(blenderContext.getInputStream());
+ if (mFaces == null || mFaces.size() == 0) {
+ return new ArrayList<Geometry>(0);
+ }
+ } else{
+ mFaces = new ArrayList<Structure>(0);
+ }
+
+ Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
+ List<Vector2f> uvCoordinates = null;
+ List<Structure> mtFaces = null;
+
+ if (pMTFace.isNotNull()) {
+ mtFaces = pMTFace.fetchData(blenderContext.getInputStream());
+ int facesAmount = ((Number) structure.getFieldValue("totface")).intValue();
+ if (mtFaces.size() != facesAmount) {
+ throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
+ }
+ uvCoordinates = new ArrayList<Vector2f>();
+ }
+
+ // normalMap merges normals of faces that will be rendered smooth
+ Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
+
+ List<Vector3f> normalList = new ArrayList<Vector3f>();
+ List<Vector3f> vertexList = new ArrayList<Vector3f>();
+ // indicates if the material with the specified number should have a texture attached
+ Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();
+ // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
+ // positions (it simply tells which vertex is referenced where in the result list)
+ Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);
+ int vertexColorIndex = 0;
+ for (int i = 0; i < mFaces.size(); ++i) {
+ Structure mFace = mFaces.get(i);
+ boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
+ DynamicArray<Number> uvs = null;
+ boolean materialWithoutTextures = false;
+ Pointer pImage = null;
+ if (mtFaces != null) {
+ Structure mtFace = mtFaces.get(i);
+ pImage = (Pointer) mtFace.getFieldValue("tpage");
+ materialWithoutTextures = pImage.isNull();
+ // uvs always must be added wheater we have texture or not
+ uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
+ uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
+ uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
+ uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
+ }
+ int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
+ Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
+ List<Integer> indexList = meshesMap.get(materialNumber);
+ if (indexList == null) {
+ indexList = new ArrayList<Integer>();
+ meshesMap.put(materialNumber, indexList);
+ }
+
+ // attaching image to texture (face can have UV's and image whlie its material may have no texture attached)
+ if (pImage != null && pImage.isNotNull() && !materialNumberToTexture.containsKey(materialNumber)) {
+ Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(blenderContext.getInputStream()).get(0),
+ blenderContext);
+ if (texture != null) {
+ materialNumberToTexture.put(materialNumber, texture);
+ }
+ }
+
+ int v1 = ((Number) mFace.getFieldValue("v1")).intValue();
+ int v2 = ((Number) mFace.getFieldValue("v2")).intValue();
+ int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
+ int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
+
+ Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
+ this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
+ normalList.add(normalMap.get(vertices[v1]));
+ normalList.add(normalMap.get(vertices[v2]));
+ normalList.add(normalMap.get(vertices[v3]));
+
+ this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
+ indexList.add(vertexList.size());
+ vertexList.add(vertices[v1]);
+
+ this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);
+ indexList.add(vertexList.size());
+ vertexList.add(vertices[v2]);
+
+ this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
+ indexList.add(vertexList.size());
+ vertexList.add(vertices[v3]);
+
+ if (v4 > 0) {
+ if (uvs != null) {
+ uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
+ uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
+ uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
+ }
+ this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
+ indexList.add(vertexList.size());
+ vertexList.add(vertices[v1]);
+
+ this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
+ indexList.add(vertexList.size());
+ vertexList.add(vertices[v3]);
+
+ this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);
+ indexList.add(vertexList.size());
+ vertexList.add(vertices[v4]);
+
+ this.addNormal(n, normalMap, smooth, vertices[v4]);
+ normalList.add(normalMap.get(vertices[v1]));
+ normalList.add(normalMap.get(vertices[v3]));
+ normalList.add(normalMap.get(vertices[v4]));
+
+ if (verticesColors != null) {
+ verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));
+ verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2));
+ }
+ vertexColorIndex += 6;
+ } else {
+ if (verticesColors != null) {
+ verticesColors.remove(vertexColorIndex + 3);
+ vertexColorIndex += 3;
+ }
+ }
+ }
+ meshContext.setVertexList(vertexList);
+ meshContext.setVertexReferenceMap(vertexReferenceMap);
+
+ Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
+
+ // reading vertices groups (from the parent)
+ Structure parent = blenderContext.peekParent();
+ Structure defbase = (Structure) parent.getFieldValue("defbase");
+ List<Structure> defs = defbase.evaluateListBase(blenderContext);
+ String[] verticesGroups = new String[defs.size()];
+ int defIndex = 0;
+ for (Structure def : defs) {
+ verticesGroups[defIndex++] = def.getFieldValue("name").toString();
+ }
+
+ // reading materials
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ Material[] materials = null;
+ Material[] nonTexturedMaterials = null;
+ if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
+ materials = materialHelper.getMaterials(structure, blenderContext);
+ nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed
+ }
+
+ // creating the result meshes
+ geometries = new ArrayList<Geometry>(meshesMap.size());
+
+ VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
+ verticesBuffer.setupData(Usage.Stream, 3, Format.Float,
+ BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));
+
+ // initial vertex position (used with animation)
+ VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
+ verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
+
+ VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
+ normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals));
+
+ // initial normals position (used with animation)
+ VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
+ normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
+
+ VertexBuffer uvCoordsBuffer = null;
+ if (uvCoordinates != null) {
+ uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
+ uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
+ BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
+ }
+
+ //reading custom properties
+ Properties properties = this.loadProperties(structure, blenderContext);
+
+ // generating meshes
+ //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
+ ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);
+ for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
+ Mesh mesh = new Mesh();
+
+ // creating vertices indices for this mesh
+ List<Integer> indexList = meshEntry.getValue();
+ if(verticesAmount < Short.MAX_VALUE * 2) {
+ short[] indices = new short[indexList.size()];
+ for (int i = 0; i < indexList.size(); ++i) {
+ indices[i] = indexList.get(i).shortValue();
+ }
+ mesh.setBuffer(Type.Index, 1, indices);
+ } else {
+ int[] indices = new int[indexList.size()];
+ for (int i = 0; i < indexList.size(); ++i) {
+ indices[i] = indexList.get(i).intValue();
+ }
+ mesh.setBuffer(Type.Index, 1, indices);
+ }
+
+ mesh.setBuffer(verticesBuffer);
+ mesh.setBuffer(verticesBind);
+
+ // setting vertices colors
+ if (verticesColorsBuffer != null) {
+ mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);
+ mesh.getBuffer(Type.Color).setNormalized(true);
+ }
+
+ // setting faces' normals
+ mesh.setBuffer(normalsBuffer);
+ mesh.setBuffer(normalsBind);
+
+ // creating the result
+ Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
+ if (materials != null) {
+ int materialNumber = meshEntry.getKey().intValue();
+ Material material;
+ if (materialNumber >= 0) {
+ material = materials[materialNumber];
+ if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {
+ if (material.getMaterialDef().getAssetName().contains("Lighting")) {
+ if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {
+ material = material.clone();
+ material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,
+ materialNumberToTexture.get(Integer.valueOf(materialNumber)));
+ }
+ } else {
+ if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {
+ material = material.clone();
+ material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,
+ materialNumberToTexture.get(Integer.valueOf(materialNumber)));
+ }
+ }
+ }
+ } else {
+ materialNumber = -1 * (materialNumber + 1);
+ if (nonTexturedMaterials[materialNumber] == null) {
+ nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],
+ TextureHelper.TEX_IMAGE);
+ }
+ material = nonTexturedMaterials[materialNumber];
+ }
+ geometry.setMaterial(material);
+ if (material.isTransparent()) {
+ geometry.setQueueBucket(Bucket.Transparent);
+ }
+ } else {
+ geometry.setMaterial(blenderContext.getDefaultMaterial());
+ }
+ if (properties != null && properties.getValue() != null) {
+ geometry.setUserData("properties", properties);
+ }
+ geometries.add(geometry);
+ }
+
+ //applying uvCoordinates for all the meshes
+ if (uvCoordsBuffer != null) {
+ for (Geometry geom : geometries) {
+ geom.getMesh().setBuffer(uvCoordsBuffer);
+ }
+ } else {
+ Map<Material, List<Geometry>> materialMap = new HashMap<Material, List<Geometry>>();
+ for (Geometry geom : geometries) {
+ Material material = geom.getMaterial();
+ List<Geometry> geomsWithCommonMaterial = materialMap.get(material);
+ if (geomsWithCommonMaterial == null) {
+ geomsWithCommonMaterial = new ArrayList<Geometry>();
+ materialMap.put(material, geomsWithCommonMaterial);
+ }
+ geomsWithCommonMaterial.add(geom);
+
+ }
+ for (Entry<Material, List<Geometry>> entry : materialMap.entrySet()) {
+ MaterialContext materialContext = blenderContext.getMaterialContext(entry.getKey());
+ if (materialContext != null && materialContext.getTexturesCount() > 0) {
+ VertexBuffer coords = UVCoordinatesGenerator.generateUVCoordinates(materialContext.getUvCoordinatesType(),
+ materialContext.getProjectionType(), materialContext.getTextureDimension(),
+ materialContext.getProjection(0), entry.getValue());
+ //setting the coordinates inside the mesh context
+ for (Geometry geometry : entry.getValue()) {
+ meshContext.addUVCoordinates(geometry, coords);
+ }
+ }
+ }
+ }
+
+ // if there are multiple materials used, extract the shared
+ // vertex data
+ if (geometries.size() > 1){
+ // extract from itself
+ for (Geometry geom : geometries){
+ geom.getMesh().extractVertexData(geom.getMesh());
+ }
+ }
+
+ blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
+ blenderContext.setMeshContext(structure.getOldMemoryAddress(), meshContext);
+ return geometries;
+ }
+
+ /**
+ * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
+ *
+ * @param normalToAdd
+ * a normal to be added
+ * @param normalMap
+ * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
+ * @param smooth
+ * the variable that indicates wheather to merge normals (creating the smooth mesh) or not
+ * @param vertices
+ * a list of vertices read from the blender file
+ */
+ public void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
+ for (Vector3f v : vertices) {
+ Vector3f n = normalMap.get(v);
+ if (!smooth || n == null) {
+ normalMap.put(v, normalToAdd.clone());
+ } else {
+ n.addLocal(normalToAdd).normalizeLocal();
+ }
+ }
+ }
+
+ /**
+ * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
+ * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key
+ * - the reference indices list.
+ *
+ * @param basicVertexIndex
+ * the index of the vertex from its basic table
+ * @param resultIndex
+ * the index of the vertex in its result vertex list
+ * @param vertexReferenceMap
+ * the reference map
+ */
+ protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
+ List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
+ if (referenceList == null) {
+ referenceList = new ArrayList<Integer>();
+ vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
+ }
+ referenceList.add(Integer.valueOf(resultIndex));
+ }
+
+ /**
+ * This method returns the vertices colors. Each vertex is stored in byte[4] array.
+ *
+ * @param meshStructure
+ * the structure containing the mesh data
+ * @param blenderContext
+ * the blender context
+ * @return a list of vertices colors, each color belongs to a single vertex
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public List<byte[]> getVerticesColors(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");
+ List<byte[]> verticesColors = null;
+ List<Structure> mCol = null;
+ if (pMCol.isNotNull()) {
+ verticesColors = new LinkedList<byte[]>();
+ mCol = pMCol.fetchData(blenderContext.getInputStream());
+ for (Structure color : mCol) {
+ byte r = ((Number)color.getFieldValue("r")).byteValue();
+ byte g = ((Number)color.getFieldValue("g")).byteValue();
+ byte b = ((Number)color.getFieldValue("b")).byteValue();
+ byte a = ((Number)color.getFieldValue("a")).byteValue();
+ verticesColors.add(new byte[]{b, g, r, a});
+ }
+ }
+ return verticesColors;
+ }
+
+ /**
+ * This method returns the vertices.
+ *
+ * @param meshStructure
+ * the structure containing the mesh data
+ * @param blenderContext
+ * the blender context
+ * @return a list of vertices colors, each color belongs to a single vertex
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ @SuppressWarnings("unchecked")
+ private Vector3f[] getVertices(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
+ int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue();
+ Vector3f[] vertices = new Vector3f[verticesAmount];
+ if (verticesAmount == 0) {
+ return vertices;
+ }
+
+ Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
+ List<Structure> mVerts = pMVert.fetchData(blenderContext.getInputStream());
+ if(this.fixUpAxis) {
+ for (int i = 0; i < verticesAmount; ++i) {
+ DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
+ vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
+ }
+ } else {
+ for (int i = 0; i < verticesAmount; ++i) {
+ DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
+ vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
+ }
+ }
+ return vertices;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
new file mode 100644
index 0000000..c15e91f
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
@@ -0,0 +1,393 @@
+package com.jme3.scene.plugins.blender.modifiers;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.math.Matrix4f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
+import com.jme3.scene.plugins.blender.constraints.Constraint;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.meshes.MeshContext;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+import com.jme3.scene.plugins.ogre.AnimData;
+import com.jme3.util.BufferUtils;
+
+/**
+ * This modifier allows to add bone animation to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ArmatureModifier extends Modifier {
+ private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
+ private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4;
+ // @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight
+ // variable in mesh,
+ // i guess this limitation has no sense for the blender loader...so i guess
+ // it's up to you. You'll have to deternine the max weight according to the
+ // provided blend file
+ // I added a check to avoid crash when loading a model that has more than 4
+ // weight per vertex on line 258
+ // If you decide to remove this limitation, remove this code.
+ // Rémy
+
+ /** Loaded animation data. */
+ private AnimData animData;
+ /** Old memory address of the mesh that will have the skeleton applied. */
+ private Long meshOMA;
+ /**
+ * The maxiumum amount of bone groups applied to a single vertex (max =
+ * MAXIMUM_WEIGHTS_PER_VERTEX).
+ */
+ private int boneGroups;
+ /** The weights of vertices. */
+ private VertexBuffer verticesWeights;
+ /** The indexes of bones applied to vertices. */
+ private VertexBuffer verticesWeightsIndices;
+
+ /**
+ * This constructor reads animation data from the object structore. The
+ * stored data is the AnimData and additional data is armature's OMA.
+ *
+ * @param objectStructure
+ * the structure of the object
+ * @param modifierStructure
+ * the structure of the modifier
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
+ Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
+ // =
+ // DeformVERTices
+
+ // if pDvert==null then there are not vertex groups and no need to load
+ // skeleton (untill bone envelopes are supported)
+ if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) {
+ Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
+ if (pArmatureObject.isNotNull()) {
+ ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
+
+ Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0);
+
+ // load skeleton
+ Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
+
+ Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0);
+ List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext);
+
+ Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size());
+ for (Structure poseChannel : chanbase) {
+ Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
+ bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel);
+ }
+
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);
+ Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
+ Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);
+
+ List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
+ List<Bone> bonesList = new ArrayList<Bone>();
+ for (int i = 0; i < bonebase.size(); ++i) {
+ armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
+ }
+ bonesList.add(0, new Bone(""));
+ Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
+ Skeleton skeleton = new Skeleton(bones);
+
+ // read mesh indexes
+ this.meshOMA = meshStructure.getOldMemoryAddress();
+ this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext);
+
+ // read animations
+ ArrayList<Animation> animations = new ArrayList<Animation>();
+ List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
+ if (actionHeaders != null) {// it may happen that the model has
+ // armature with no actions
+ for (FileBlockHeader header : actionHeaders) {
+ Structure actionStructure = header.getStructure(blenderContext);
+ String actionName = actionStructure.getName();
+
+ BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
+ if(tracks != null && tracks.length > 0) {
+ // determining the animation time
+ float maximumTrackLength = 0;
+ for (BoneTrack track : tracks) {
+ float length = track.getLength();
+ if (length > maximumTrackLength) {
+ maximumTrackLength = length;
+ }
+ }
+
+ Animation boneAnimation = new Animation(actionName, maximumTrackLength);
+ boneAnimation.setTracks(tracks);
+ animations.add(boneAnimation);
+ }
+ }
+ }
+ animData = new AnimData(skeleton, animations);
+
+ // store the animation data for each bone
+ for (Bone bone : bones) {
+ Long boneOma = armatureHelper.getBoneOMA(bone);
+ if (boneOma != null) {
+ blenderContext.setAnimData(boneOma, animData);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Node apply(Node node, BlenderContext blenderContext) {
+ if (invalid) {
+ LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
+ }// if invalid, animData will be null
+ if (animData == null) {
+ return node;
+ }
+
+ // setting weights for bones
+ List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(this.meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
+ for (Geometry geom : geomList) {
+ Mesh mesh = geom.getMesh();
+ if (this.verticesWeights != null) {
+ mesh.setMaxNumWeights(this.boneGroups);
+ mesh.setBuffer(this.verticesWeights);
+ mesh.setBuffer(this.verticesWeightsIndices);
+ }
+ }
+
+ // applying constraints to Bones
+ ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
+ for (int i = 0; i < animData.skeleton.getBoneCount(); ++i) {
+ Long boneOMA = armatureHelper.getBoneOMA(animData.skeleton.getBone(i));
+ List<Constraint> constraints = blenderContext.getConstraints(boneOMA);
+ if (constraints != null && constraints.size() > 0) {
+ for (Constraint constraint : constraints) {
+ constraint.bake();
+ }
+ }
+ }
+
+ // applying animations
+ AnimControl control = new AnimControl(animData.skeleton);
+ ArrayList<Animation> animList = animData.anims;
+ if (animList != null && animList.size() > 0) {
+ HashMap<String, Animation> anims = new HashMap<String, Animation>(animList.size());
+ for (int i = 0; i < animList.size(); ++i) {
+ Animation animation = animList.get(i);
+ anims.put(animation.getName(), animation);
+ }
+ control.setAnimations(anims);
+ }
+ node.addControl(control);
+ node.addControl(new SkeletonControl(animData.skeleton));
+
+ return node;
+ }
+
+ /**
+ * This method reads mesh indexes
+ *
+ * @param objectStructure
+ * structure of the object that has the armature modifier applied
+ * @param meshStructure
+ * the structure of the object's mesh
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is
+ * somehow invalid or corrupted
+ */
+ private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+ ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
+ Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
+ Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext);
+
+ int[] bonesGroups = new int[] { 0 };
+ MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
+
+ VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexList().size(), bonesGroups, meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext);
+ this.verticesWeights = boneWeightsAndIndex[0];
+ this.verticesWeightsIndices = boneWeightsAndIndex[1];
+ this.boneGroups = bonesGroups[0];
+ }
+
+ /**
+ * This method returns an array of size 2. The first element is a vertex
+ * buffer holding bone weights for every vertex in the model. The second
+ * element is a vertex buffer holding bone indices for vertices (the indices
+ * of bones the vertices are assigned to).
+ *
+ * @param meshStructure
+ * the mesh structure object
+ * @param vertexListSize
+ * a number of vertices in the model
+ * @param bonesGroups
+ * this is an output parameter, it should be a one-sized array;
+ * the maximum amount of weights per vertex (up to
+ * MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
+ * @param vertexReferenceMap
+ * this reference map allows to map the original vertices read
+ * from blender to vertices that are really in the model; one
+ * vertex may appear several times in the result model
+ * @param groupToBoneIndexMap
+ * this object maps the group index (to which a vertices in
+ * blender belong) to bone index of the model
+ * @param blenderContext
+ * the blender context
+ * @return arrays of vertices weights and their bone indices and (as an
+ * output parameter) the maximum amount of weights for a vertex
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is
+ * somehow invalid or corrupted
+ */
+ private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
+ throws BlenderFileException {
+ Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
+ FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
+ ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
+ if (pDvert.isNotNull()) {// assigning weights and bone indices
+ List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per
+ // vertex in blender)
+ int vertexIndex = 0;
+ for (Structure dvert : dverts) {
+ int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex
+ // (max. 4 in JME)
+ Pointer pDW = (Pointer) dvert.getFieldValue("dw");
+ List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
+ if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
+ int weightIndex = 0;
+ List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
+ for (Structure deformWeight : dw) {
+ Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
+
+ // Remove this code if 4 weights limitation is removed
+ if (weightIndex == 4) {
+ LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex });
+ break;
+ }
+
+ // null here means that we came accross group that has no bone attached to
+ if (boneIndex != null) {
+ float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
+ if (weight == 0.0f) {
+ weight = 1;
+ boneIndex = Integer.valueOf(0);
+ }
+ // we apply the weight to all referenced vertices
+ for (Integer index : vertexIndices) {
+ weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
+ indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
+ }
+ }
+ ++weightIndex;
+ }
+ } else {
+ for (Integer index : vertexIndices) {
+ weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
+ indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
+ }
+ }
+ ++vertexIndex;
+ }
+ } else {
+ // always bind all vertices to 0-indexed bone
+ // this bone makes the model look normally if vertices have no bone
+ // assigned
+ // and it is used in object animation, so if we come accross object
+ // animation
+ // we can use the 0-indexed bone for this
+ for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
+ // we apply the weight to all referenced vertices
+ for (Integer index : vertexIndexList) {
+ weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
+ indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
+ }
+ }
+ }
+
+ bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
+ VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
+ verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
+
+ VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);
+ verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);
+ return new VertexBuffer[] { verticesWeights, verticesWeightsIndices };
+ }
+
+ /**
+ * Normalizes weights if needed and finds largest amount of weights used for
+ * all vertices in the buffer.
+ *
+ * @param vertCount
+ * amount of vertices
+ * @param weightsFloatData
+ * weights for vertices
+ */
+ private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
+ int maxWeightsPerVert = 0;
+ weightsFloatData.rewind();
+ for (int v = 0; v < vertCount; ++v) {
+ float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();
+
+ if (w3 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
+ } else if (w2 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
+ } else if (w1 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
+ } else if (w0 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
+ }
+
+ float sum = w0 + w1 + w2 + w3;
+ if (sum != 1f && sum != 0.0f) {
+ weightsFloatData.position(weightsFloatData.position() - 4);
+ // compute new vals based on sum
+ float sumToB = 1f / sum;
+ weightsFloatData.put(w0 * sumToB);
+ weightsFloatData.put(w1 * sumToB);
+ weightsFloatData.put(w2 * sumToB);
+ weightsFloatData.put(w3 * sumToB);
+ }
+ }
+ weightsFloatData.rewind();
+ return maxWeightsPerVert;
+ }
+
+ @Override
+ public String getType() {
+ return Modifier.ARMATURE_MODIFIER_DATA;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java
new file mode 100644
index 0000000..4997fdc
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java
@@ -0,0 +1,247 @@
+package com.jme3.scene.plugins.blender.modifiers;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+import com.jme3.scene.shape.Curve;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This modifier allows to array modifier to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ArrayModifier extends Modifier {
+ private static final Logger LOGGER = Logger.getLogger(ArrayModifier.class.getName());
+
+ /** Parameters of the modifier. */
+ private Map<String, Object> modifierData = new HashMap<String, Object>();
+
+ /**
+ * This constructor reads array data from the modifier structure. The
+ * stored data is a map of parameters for array modifier. No additional data
+ * is loaded.
+ *
+ * @param objectStructure
+ * the structure of the object
+ * @param modifierStructure
+ * the structure of the modifier
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ @SuppressWarnings("unchecked")
+ public ArrayModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
+ if(this.validate(modifierStructure, blenderContext)) {
+ Number fittype = (Number) modifierStructure.getFieldValue("fit_type");
+ modifierData.put("fittype", fittype);
+ switch (fittype.intValue()) {
+ case 0:// FIXED COUNT
+ modifierData.put("count", modifierStructure.getFieldValue("count"));
+ break;
+ case 1:// FIXED LENGTH
+ modifierData.put("length", modifierStructure.getFieldValue("length"));
+ break;
+ case 2:// FITCURVE
+ Pointer pCurveOb = (Pointer) modifierStructure.getFieldValue("curve_ob");
+ float length = 0;
+ if (pCurveOb.isNotNull()) {
+ Structure curveStructure = pCurveOb.fetchData(blenderContext.getInputStream()).get(0);
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ Node curveObject = (Node) objectHelper.toObject(curveStructure, blenderContext);
+ Set<Number> referencesToCurveLengths = new HashSet<Number>(curveObject.getChildren().size());
+ for (Spatial spatial : curveObject.getChildren()) {
+ if (spatial instanceof Geometry) {
+ Mesh mesh = ((Geometry) spatial).getMesh();
+ if (mesh instanceof Curve) {
+ length += ((Curve) mesh).getLength();
+ } else {
+ //if bevel object has several parts then each mesh will have the same reference
+ //to length value (and we should use only one)
+ Number curveLength = spatial.getUserData("curveLength");
+ if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) {
+ length += curveLength.floatValue();
+ referencesToCurveLengths.add(curveLength);
+ }
+ }
+ }
+ }
+ }
+ modifierData.put("length", Float.valueOf(length));
+ modifierData.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH
+ break;
+ default:
+ assert false : "Unknown array modifier fit type: " + fittype;
+ }
+
+ // offset parameters
+ int offsettype = ((Number) modifierStructure.getFieldValue("offset_type")).intValue();
+ if ((offsettype & 0x01) != 0) {// Constant offset
+ DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifierStructure.getFieldValue("offset");
+ float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()};
+ modifierData.put("offset", offset);
+ }
+ if ((offsettype & 0x02) != 0) {// Relative offset
+ DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifierStructure.getFieldValue("scale");
+ float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()};
+ modifierData.put("scale", scale);
+ }
+ if ((offsettype & 0x04) != 0) {// Object offset
+ Pointer pOffsetObject = (Pointer) modifierStructure.getFieldValue("offset_ob");
+ if (pOffsetObject.isNotNull()) {
+ modifierData.put("offsetob", pOffsetObject);
+ }
+ }
+
+ // start cap and end cap
+ Pointer pStartCap = (Pointer) modifierStructure.getFieldValue("start_cap");
+ if (pStartCap.isNotNull()) {
+ modifierData.put("startcap", pStartCap);
+ }
+ Pointer pEndCap = (Pointer) modifierStructure.getFieldValue("end_cap");
+ if (pEndCap.isNotNull()) {
+ modifierData.put("endcap", pEndCap);
+ }
+ }
+ }
+
+ @Override
+ public Node apply(Node node, BlenderContext blenderContext) {
+ if(invalid) {
+ LOGGER.log(Level.WARNING, "Array modifier is invalid! Cannot be applied to: {0}", node.getName());
+ return node;
+ }
+ int fittype = ((Number) modifierData.get("fittype")).intValue();
+ float[] offset = (float[]) modifierData.get("offset");
+ if (offset == null) {// the node will be repeated several times in the same place
+ offset = new float[]{0.0f, 0.0f, 0.0f};
+ }
+ float[] scale = (float[]) modifierData.get("scale");
+ if (scale == null) {// the node will be repeated several times in the same place
+ scale = new float[]{0.0f, 0.0f, 0.0f};
+ } else {
+ // getting bounding box
+ node.updateModelBound();
+ BoundingVolume boundingVolume = node.getWorldBound();
+ if (boundingVolume instanceof BoundingBox) {
+ scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f;
+ scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f;
+ scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f;
+ } else if (boundingVolume instanceof BoundingSphere) {
+ float radius = ((BoundingSphere) boundingVolume).getRadius();
+ scale[0] *= radius * 2.0f;
+ scale[1] *= radius * 2.0f;
+ scale[2] *= radius * 2.0f;
+ } else {
+ throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName());
+ }
+ }
+
+ // adding object's offset
+ float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f};
+ Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");
+ if (pOffsetObject != null) {
+ FileBlockHeader offsetObjectBlock = blenderContext.getFileBlock(pOffsetObject.getOldMemoryAddress());
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ try {// we take the structure in case the object was not yet loaded
+ Structure offsetStructure = offsetObjectBlock.getStructure(blenderContext);
+ Vector3f translation = objectHelper.getTransformation(offsetStructure, blenderContext).getTranslation();
+ objectOffset[0] = translation.x;
+ objectOffset[1] = translation.y;
+ objectOffset[2] = translation.z;
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.WARNING, "Problems in blender file structure! Object offset cannot be applied! The problem: {0}", e.getMessage());
+ }
+ }
+
+ // getting start and end caps
+ Node[] caps = new Node[]{null, null};
+ Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")};
+ for (int i = 0; i < pCaps.length; ++i) {
+ if (pCaps[i] != null) {
+ caps[i] = (Node) blenderContext.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (caps[i] != null) {
+ caps[i] = (Node) caps[i].clone();
+ } else {
+ FileBlockHeader capBlock = blenderContext.getFileBlock(pOffsetObject.getOldMemoryAddress());
+ try {// we take the structure in case the object was not yet loaded
+ Structure capStructure = capBlock.getStructure(blenderContext);
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ caps[i] = (Node) objectHelper.toObject(capStructure, blenderContext);
+ if (caps[i] == null) {
+ LOGGER.log(Level.WARNING, "Cap object ''{0}'' couldn''t be loaded!", capStructure.getName());
+ }
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.WARNING, "Problems in blender file structure! Cap object cannot be applied! The problem: {0}", e.getMessage());
+ }
+ }
+ }
+ }
+
+ Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], offset[1] + scale[1] + objectOffset[1], offset[2] + scale[2] + objectOffset[2]);
+
+ // getting/calculating repeats amount
+ int count = 0;
+ if (fittype == 0) {// Fixed count
+ count = ((Number) modifierData.get("count")).intValue() - 1;
+ } else if (fittype == 1) {// Fixed length
+ float length = ((Number) modifierData.get("length")).floatValue();
+ if (translationVector.length() > 0.0f) {
+ count = (int) (length / translationVector.length()) - 1;
+ }
+ } else if (fittype == 2) {// Fit curve
+ throw new IllegalStateException("Fit curve should be transformed to Fixed Length array type!");
+ } else {
+ throw new IllegalStateException("Unknown fit type: " + fittype);
+ }
+
+ // adding translated nodes and caps
+ if (count > 0) {
+ Node[] arrayNodes = new Node[count];
+ Vector3f newTranslation = new Vector3f();
+ for (int i = 0; i < count; ++i) {
+ newTranslation.addLocal(translationVector);
+ Node nodeClone = (Node) node.clone();
+ nodeClone.setLocalTranslation(newTranslation);
+ arrayNodes[i] = nodeClone;
+ }
+ for (Node nodeClone : arrayNodes) {
+ node.attachChild(nodeClone);
+ }
+ if (caps[0] != null) {
+ caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector);
+ node.attachChild(caps[0]);
+ }
+ if (caps[1] != null) {
+ caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector);
+ node.attachChild(caps[1]);
+ }
+ }
+ return node;
+ }
+
+ @Override
+ public String getType() {
+ return ARRAY_MODIFIER_DATA;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java
new file mode 100644
index 0000000..d441476
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java
@@ -0,0 +1,174 @@
+package com.jme3.scene.plugins.blender.modifiers;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This modifier allows to array modifier to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class MirrorModifier extends Modifier {
+ private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
+
+ /** Parameters of the modifier. */
+ private Map<String, Object> modifierData = new HashMap<String, Object>();
+
+ /**
+ * This constructor reads mirror data from the modifier structure. The
+ * stored data is a map of parameters for mirror modifier. No additional data
+ * is loaded.
+ * When the modifier is applied it is necessary to get the newly created node.
+ *
+ * @param objectStructure
+ * the structure of the object
+ * @param modifierStructure
+ * the structure of the modifier
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) {
+ if(this.validate(modifierStructure, blenderContext)) {
+ modifierData.put("flag", modifierStructure.getFieldValue("flag"));
+ modifierData.put("tolerance", modifierStructure.getFieldValue("tolerance"));
+ Pointer pMirrorOb = (Pointer) modifierStructure.getFieldValue("mirror_ob");
+ if (pMirrorOb.isNotNull()) {
+ modifierData.put("mirrorob", pMirrorOb);
+ }
+ }
+ }
+
+ @Override
+ public Node apply(Node node, BlenderContext blenderContext) {
+ if(invalid) {
+ LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
+ return node;
+ }
+
+ int flag = ((Number) modifierData.get("flag")).intValue();
+ float[] mirrorFactor = new float[]{
+ (flag & 0x08) != 0 ? -1.0f : 1.0f,
+ (flag & 0x10) != 0 ? -1.0f : 1.0f,
+ (flag & 0x20) != 0 ? -1.0f : 1.0f
+ };
+ float[] center = new float[]{0.0f, 0.0f, 0.0f};
+ Pointer pObject = (Pointer) modifierData.get("mirrorob");
+ if (pObject != null) {
+ Structure objectStructure;
+ try {
+ objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+ Node object = (Node) objectHelper.toObject(objectStructure, blenderContext);
+ if (object != null) {
+ Vector3f translation = object.getWorldTranslation();
+ center[0] = translation.x;
+ center[1] = translation.y;
+ center[2] = translation.z;
+ }
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage());
+ }
+ }
+ float tolerance = ((Number) modifierData.get("tolerance")).floatValue();
+ boolean mirrorU = (flag & 0x01) != 0;
+ boolean mirrorV = (flag & 0x02) != 0;
+// boolean mirrorVGroup = (flag & 0x20) != 0;
+
+ List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
+ for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {
+ if (mirrorFactor[mirrorIndex] == -1.0f) {
+ for (Spatial spatial : node.getChildren()) {
+ if (spatial instanceof Geometry) {
+ Mesh mesh = ((Geometry) spatial).getMesh();
+ Mesh clone = mesh.deepClone();
+
+ // getting buffers
+ FloatBuffer position = mesh.getFloatBuffer(Type.Position);
+ FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition);
+
+ FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position);
+ FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition);
+ FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal);
+ FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal);
+ IntBuffer cloneIndexes = (IntBuffer) clone.getBuffer(Type.Index).getData();
+
+ // modyfying data
+ for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) {
+ float value = clonePosition.get(i);
+ float d = center[mirrorIndex] - value;
+
+ if (Math.abs(d) <= tolerance) {
+ clonePosition.put(i, center[mirrorIndex]);
+ cloneBindPosePosition.put(i, center[mirrorIndex]);
+ position.put(i, center[mirrorIndex]);
+ bindPosePosition.put(i, center[mirrorIndex]);
+ } else {
+ clonePosition.put(i, value + 2.0f * d);
+ cloneBindPosePosition.put(i, value + 2.0f * d);
+ }
+ cloneNormals.put(i, -cloneNormals.get(i));
+ cloneBindPoseNormals.put(i, -cloneNormals.get(i));
+
+ //modifying clone indexes
+ int vertexIndex = (i - mirrorIndex) / 3;
+ if (vertexIndex % 3 == 0 && vertexIndex<cloneIndexes.limit()) {
+ int index = cloneIndexes.get(vertexIndex + 2);
+ cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1));
+ cloneIndexes.put(vertexIndex + 1, index);
+ }
+ }
+
+ if (mirrorU) {
+ FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
+ for (int i = 0; i < cloneUVs.limit(); i += 2) {
+ cloneUVs.put(i, 1.0f - cloneUVs.get(i));
+ }
+ }
+ if (mirrorV) {
+ FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
+ for (int i = 1; i < cloneUVs.limit(); i += 2) {
+ cloneUVs.put(i, 1.0f - cloneUVs.get(i));
+ }
+ }
+
+ Geometry geometry = new Geometry(null, clone);
+ geometry.setMaterial(((Geometry) spatial).getMaterial());
+ geometriesToAdd.add(geometry);
+ }
+ }
+
+ // adding meshes to node
+ for (Geometry geometry : geometriesToAdd) {
+ node.attachChild(geometry);
+ }
+ geometriesToAdd.clear();
+ }
+ }
+ return node;
+ }
+
+ @Override
+ public String getType() {
+ return Modifier.MIRROR_MODIFIER_DATA;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java
new file mode 100644
index 0000000..12d9ed4
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/Modifier.java
@@ -0,0 +1,52 @@
+package com.jme3.scene.plugins.blender.modifiers;
+
+import com.jme3.scene.Node;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * This class represents an object's modifier. The modifier object can be varied
+ * and the user needs to know what is the type of it for the specified type
+ * name. For example "ArmatureModifierData" type specified in blender is
+ * represented by AnimData object from jMonkeyEngine.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public abstract class Modifier {
+
+ public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData";
+ public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData";
+ public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData";
+ public static final String MIRROR_MODIFIER_DATA = "MirrorModifierData";
+ public static final String SUBSURF_MODIFIER_DATA = "SubsurfModifierData";
+ public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData";
+
+ /** This variable indicates if the modifier is invalid (<b>true</b>) or not (<b>false</b>). */
+ protected boolean invalid;
+
+ /**
+ * This method applies the modifier to the given node.
+ *
+ * @param node
+ * the node that will have modifier applied
+ * @param blenderContext
+ * the blender context
+ * @return the node with applied modifier
+ */
+ public abstract Node apply(Node node, BlenderContext blenderContext);
+
+ /**
+ * This method returns blender's type of modifier.
+ *
+ * @return blender's type of modifier
+ */
+ public abstract String getType();
+
+ protected boolean validate(Structure modifierStructure, BlenderContext blenderContext) {
+ Structure modifierData = (Structure)modifierStructure.getFieldValue("modifier");
+ Pointer pError = (Pointer) modifierData.getFieldValue("error");
+ invalid = pError.isNotNull();
+ return !invalid;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
new file mode 100644
index 0000000..652fd57
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.modifiers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.animations.IpoHelper;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+/**
+ * A class that is used in modifiers calculations.
+ *
+ * @author Marcin Roguski
+ */
+public class ModifierHelper extends AbstractBlenderHelper {
+
+ private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());
+
+ /**
+ * This constructor parses the given blender version and stores the result.
+ * Some functionalities may differ in different blender versions.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ModifierHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method reads the given object's modifiers.
+ *
+ * @param objectStructure
+ * the object structure
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public Collection<Modifier> readModifiers(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Collection<Modifier> result = new ArrayList<Modifier>();
+ Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
+ List<Structure> modifiers = modifiersListBase.evaluateListBase(blenderContext);
+ for (Structure modifierStructure : modifiers) {
+ Modifier modifier = null;
+ if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) {
+ modifier = new ArrayModifier(modifierStructure, blenderContext);
+ } else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) {
+ modifier = new MirrorModifier(modifierStructure, blenderContext);
+ } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) {
+ modifier = new ArmatureModifier(objectStructure, modifierStructure, blenderContext);
+ } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) {
+ modifier = new ParticlesModifier(modifierStructure, blenderContext);
+ }
+
+ if (modifier != null) {
+ result.add(modifier);
+ blenderContext.addModifier(objectStructure.getOldMemoryAddress(), modifier);
+ } else {
+ LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType());
+ }
+ }
+
+ // at the end read object's animation modifier (object animation is
+ // either described by action or by ipo of the object)
+ Modifier modifier;
+ if (blenderVersion <= 249) {
+ modifier = this.readAnimationModifier249(objectStructure, blenderContext);
+ } else {
+ modifier = this.readAnimationModifier250(objectStructure, blenderContext);
+ }
+ if (modifier != null) {
+ result.add(modifier);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+
+ /**
+ * This method reads the object's animation modifier for blender version
+ * 2.49 and lower.
+ *
+ * @param objectStructure
+ * the object's structure
+ * @param blenderContext
+ * the blender context
+ * @return loaded modifier
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ private Modifier readAnimationModifier249(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Modifier result = null;
+ Pointer pAction = (Pointer) objectStructure.getFieldValue("action");
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ if (pAction.isNotNull()) {
+ Structure action = pAction.fetchData(blenderContext.getInputStream()).get(0);
+ List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext);
+ if (actionChannels.size() == 1) {// object's animtion action has
+ // only one channel
+ Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo");
+ Structure ipoStructure = pChannelIpo.fetchData(blenderContext.getInputStream()).get(0);
+ Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
+ result = new ObjectAnimationModifier(ipo, action.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
+ blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
+ } else {
+ throw new IllegalStateException("Object's action cannot have more than one channel!");
+ }
+ } else {
+ Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
+ if (pIpo.isNotNull()) {
+ Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0);
+ Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
+ result = new ObjectAnimationModifier(ipo, objectStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
+ blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method reads the object's animation modifier for blender version
+ * 2.50 and higher.
+ *
+ * @param objectStructure
+ * the object's structure
+ * @param blenderContext
+ * the blender context
+ * @return loaded modifier
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ private Modifier readAnimationModifier250(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Modifier result = null;
+ Pointer pAnimData = (Pointer) objectStructure.getFieldValue("adt");
+ if (pAnimData.isNotNull()) {
+ Structure animData = pAnimData.fetchData(blenderContext.getInputStream()).get(0);
+ Pointer pAction = (Pointer) animData.getFieldValue("action");
+ if (pAction.isNotNull()) {
+ Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
+ IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+ Ipo ipo = ipoHelper.fromAction(actionStructure, blenderContext);
+ result = new ObjectAnimationModifier(ipo, actionStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
+ blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
+ }
+ }
+ return result;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
new file mode 100644
index 0000000..a41395c
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
@@ -0,0 +1,91 @@
+package com.jme3.scene.plugins.blender.modifiers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.SpatialTrack;
+import com.jme3.scene.Node;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.Ipo;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.ogre.AnimData;
+
+/**
+ * This modifier allows to add animation to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ObjectAnimationModifier extends Modifier {
+ private static final Logger LOGGER = Logger.getLogger(ObjectAnimationModifier.class.getName());
+
+ /** Loaded animation data. */
+ private AnimData animData;
+
+ /**
+ * This constructor reads animation of the object itself (without bones) and
+ * stores it as an ArmatureModifierData modifier. The animation is returned
+ * as a modifier. It should be later applied regardless other modifiers. The
+ * reason for this is that object may not have modifiers added but it's
+ * animation should be working. The stored modifier is an anim data and
+ * additional data is given object's OMA.
+ *
+ * @param ipo
+ * the object's interpolation curves
+ * @param objectAnimationName
+ * the name of object's animation
+ * @param objectOMA
+ * the OMA of the object
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * this exception is thrown when the blender file is somehow
+ * corrupted
+ */
+ public ObjectAnimationModifier(Ipo ipo, String objectAnimationName, Long objectOMA, BlenderContext blenderContext) throws BlenderFileException {
+ int fps = blenderContext.getBlenderKey().getFps();
+
+ // calculating track
+ SpatialTrack track = (SpatialTrack) ipo.calculateTrack(-1, 0, ipo.getLastFrame(), fps, true);
+
+ Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / fps);
+ animation.setTracks(new SpatialTrack[] { track });
+ ArrayList<Animation> animations = new ArrayList<Animation>(1);
+ animations.add(animation);
+
+ animData = new AnimData(null, animations);
+ blenderContext.setAnimData(objectOMA, animData);
+ }
+
+ @Override
+ public Node apply(Node node, BlenderContext blenderContext) {
+ if (invalid) {
+ LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
+ }// if invalid, animData will be null
+ if (animData != null) {
+ // INFO: constraints for this modifier are applied in the
+ // ObjectHelper when the whole object is loaded
+ ArrayList<Animation> animList = animData.anims;
+ if (animList != null && animList.size() > 0) {
+ HashMap<String, Animation> anims = new HashMap<String, Animation>();
+ for (int i = 0; i < animList.size(); ++i) {
+ Animation animation = animList.get(i);
+ anims.put(animation.getName(), animation);
+ }
+
+ AnimControl control = new AnimControl(null);
+ control.setAnimations(anims);
+ node.addControl(control);
+ }
+ }
+ return node;
+ }
+
+ @Override
+ public String getType() {
+ return Modifier.OBJECT_ANIMATION_MODIFIER_DATA;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java
new file mode 100644
index 0000000..5a61eb6
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ParticlesModifier.java
@@ -0,0 +1,101 @@
+package com.jme3.scene.plugins.blender.modifiers;
+
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.shapes.EmitterMeshVertexShape;
+import com.jme3.effect.shapes.EmitterShape;
+import com.jme3.material.Material;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This modifier allows to add particles to the object.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class ParticlesModifier extends Modifier {
+ private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
+
+ /** Loaded particles emitter. */
+ private ParticleEmitter particleEmitter;
+
+ /**
+ * This constructor reads the particles system structure and stores it in
+ * order to apply it later to the node.
+ *
+ * @param modifierStructure
+ * the structure of the modifier
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is throw wneh there are problems with the
+ * blender file
+ */
+ public ParticlesModifier(Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
+ if(this.validate(modifierStructure, blenderContext)) {
+ Pointer pParticleSystem = (Pointer) modifierStructure.getFieldValue("psys");
+ if (pParticleSystem.isNotNull()) {
+ ParticlesHelper particlesHelper = blenderContext.getHelper(ParticlesHelper.class);
+ Structure particleSystem = pParticleSystem.fetchData(blenderContext.getInputStream()).get(0);
+ particleEmitter = particlesHelper.toParticleEmitter(particleSystem, blenderContext);
+ }
+ }
+ }
+
+ @Override
+ public Node apply(Node node, BlenderContext blenderContext) {
+ if(invalid) {
+ LOGGER.log(Level.WARNING, "Particles modifier is invalid! Cannot be applied to: {0}", node.getName());
+ return node;
+ }
+
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+ ParticleEmitter emitter = particleEmitter.clone();
+
+ // veryfying the alpha function for particles' texture
+ Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;
+ char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1);
+ if (nameSuffix == 'B' || nameSuffix == 'N') {
+ alphaFunction = MaterialHelper.ALPHA_MASK_NONE;
+ }
+ // removing the type suffix from the name
+ emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1));
+
+ // applying emitter shape
+ EmitterShape emitterShape = emitter.getShape();
+ List<Mesh> meshes = new ArrayList<Mesh>();
+ for (Spatial spatial : node.getChildren()) {
+ if (spatial instanceof Geometry) {
+ Mesh mesh = ((Geometry) spatial).getMesh();
+ if (mesh != null) {
+ meshes.add(mesh);
+ Material material = materialHelper.getParticlesMaterial(
+ ((Geometry) spatial).getMaterial(), alphaFunction, blenderContext);
+ emitter.setMaterial(material);// TODO: divide into several pieces
+ }
+ }
+ }
+ if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {
+ ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
+ }
+
+ node.attachChild(emitter);
+ return node;
+ }
+
+ @Override
+ public String getType() {
+ return Modifier.PARTICLE_MODIFIER_DATA;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
new file mode 100644
index 0000000..ed14a11
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.objects;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.cameras.CameraHelper;
+import com.jme3.scene.plugins.blender.constraints.Constraint;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
+import com.jme3.scene.plugins.blender.curves.CurvesHelper;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.lights.LightHelper;
+import com.jme3.scene.plugins.blender.meshes.MeshHelper;
+import com.jme3.scene.plugins.blender.modifiers.Modifier;
+import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
+
+/**
+ * A class that is used in object calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class ObjectHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(ObjectHelper.class.getName());
+
+ protected static final int OBJECT_TYPE_EMPTY = 0;
+ protected static final int OBJECT_TYPE_MESH = 1;
+ protected static final int OBJECT_TYPE_CURVE = 2;
+ protected static final int OBJECT_TYPE_SURF = 3;
+ protected static final int OBJECT_TYPE_TEXT = 4;
+ protected static final int OBJECT_TYPE_METABALL = 5;
+ protected static final int OBJECT_TYPE_LAMP = 10;
+ protected static final int OBJECT_TYPE_CAMERA = 11;
+ protected static final int OBJECT_TYPE_WAVE = 21;
+ protected static final int OBJECT_TYPE_LATTICE = 22;
+ protected static final int OBJECT_TYPE_ARMATURE = 25;
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+ * different blender versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ObjectHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ /**
+ * This method reads the given structure and createn an object that represents the data.
+ * @param objectStructure
+ * the object's structure
+ * @param blenderContext
+ * the blender context
+ * @return blener's object representation
+ * @throws BlenderFileException
+ * an exception is thrown when the given data is inapropriate
+ */
+ public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
+ Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if(loadedResult != null) {
+ return loadedResult;
+ }
+
+ blenderContext.pushParent(objectStructure);
+
+ //get object data
+ int type = ((Number)objectStructure.getFieldValue("type")).intValue();
+ String name = objectStructure.getName();
+ LOGGER.log(Level.INFO, "Loading obejct: {0}", name);
+
+ int restrictflag = ((Number)objectStructure.getFieldValue("restrictflag")).intValue();
+ boolean visible = (restrictflag & 0x01) != 0;
+ Object result = null;
+
+ Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
+ Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if(parent == null && pParent.isNotNull()) {
+ Structure parentStructure = pParent.fetchData(blenderContext.getInputStream()).get(0);
+ parent = this.toObject(parentStructure, blenderContext);
+ }
+
+ Transform t = this.getTransformation(objectStructure, blenderContext);
+
+ try {
+ switch(type) {
+ case OBJECT_TYPE_EMPTY:
+ LOGGER.log(Level.INFO, "Importing empty.");
+ Node empty = new Node(name);
+ empty.setLocalTransform(t);
+ if(parent instanceof Node) {
+ ((Node) parent).attachChild(empty);
+ }
+ empty.updateModelBound();
+ result = empty;
+ break;
+ case OBJECT_TYPE_MESH:
+ LOGGER.log(Level.INFO, "Importing mesh.");
+ Node node = new Node(name);
+ node.setCullHint(visible ? CullHint.Always : CullHint.Inherit);
+
+ //reading mesh
+ MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
+ Pointer pMesh = (Pointer)objectStructure.getFieldValue("data");
+ List<Structure> meshesArray = pMesh.fetchData(blenderContext.getInputStream());
+ List<Geometry> geometries = meshHelper.toMesh(meshesArray.get(0), blenderContext);
+ if (geometries != null){
+ for(Geometry geometry : geometries) {
+ node.attachChild(geometry);
+ }
+ }
+ node.setLocalTransform(t);
+
+ //reading and applying all modifiers
+ ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
+ Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
+ for(Modifier modifier : modifiers) {
+ modifier.apply(node, blenderContext);
+ }
+
+ //setting the parent
+ if(parent instanceof Node) {
+ ((Node)parent).attachChild(node);
+ }
+ node.updateModelBound();//I prefer do calculate bounding box here than read it from the file
+ result = node;
+ break;
+ case OBJECT_TYPE_SURF:
+ case OBJECT_TYPE_CURVE:
+ LOGGER.log(Level.INFO, "Importing curve/nurb.");
+ Pointer pCurve = (Pointer)objectStructure.getFieldValue("data");
+ if(pCurve.isNotNull()) {
+ CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
+ Structure curveData = pCurve.fetchData(blenderContext.getInputStream()).get(0);
+ List<Geometry> curves = curvesHelper.toCurve(curveData, blenderContext);
+ result = new Node(name);
+ for(Geometry curve : curves) {
+ ((Node)result).attachChild(curve);
+ }
+ ((Node)result).setLocalTransform(t);
+ }
+ break;
+ case OBJECT_TYPE_LAMP:
+ LOGGER.log(Level.INFO, "Importing lamp.");
+ Pointer pLamp = (Pointer)objectStructure.getFieldValue("data");
+ if(pLamp.isNotNull()) {
+ LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
+ List<Structure> lampsArray = pLamp.fetchData(blenderContext.getInputStream());
+ Light light = lightHelper.toLight(lampsArray.get(0), blenderContext);
+ if(light!=null) {
+ light.setName(name);
+ }
+ if(light instanceof PointLight) {
+ ((PointLight)light).setPosition(t.getTranslation());
+ } else if(light instanceof DirectionalLight) {
+ Quaternion quaternion = t.getRotation();
+ Vector3f[] axes = new Vector3f[3];
+ quaternion.toAxes(axes);
+ if(fixUpAxis) {
+ ((DirectionalLight)light).setDirection(axes[1].negate());//-Z is the direction axis of area lamp in blender
+ } else {
+ ((DirectionalLight)light).setDirection(axes[2].negate());
+ }
+ } else if(light instanceof SpotLight) {
+ ((SpotLight)light).setPosition(t.getTranslation());
+
+ Quaternion quaternion = t.getRotation();
+ Vector3f[] axes = new Vector3f[3];
+ quaternion.toAxes(axes);
+ if(fixUpAxis) {
+ ((SpotLight)light).setDirection(axes[1].negate());//-Z is the direction axis of area lamp in blender
+ } else {
+ ((SpotLight)light).setDirection(axes[2].negate());
+ }
+ } else {
+ LOGGER.log(Level.WARNING, "Unknown type of light: {0}", light);
+ }
+ result = light;
+ }
+ break;
+ case OBJECT_TYPE_CAMERA:
+ Pointer pCamera = (Pointer)objectStructure.getFieldValue("data");
+ if(pCamera.isNotNull()) {
+ CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
+ List<Structure> camerasArray = pCamera.fetchData(blenderContext.getInputStream());
+ Camera camera = cameraHelper.toCamera(camerasArray.get(0));
+ camera.setLocation(t.getTranslation());
+ camera.setRotation(t.getRotation());
+ result = camera;
+ }
+ break;
+ case OBJECT_TYPE_ARMATURE:
+ //need to create an empty node to properly create parent-children relationships between nodes
+ Node armature = new Node(name);
+ armature.setLocalTransform(t);
+ //TODO: modifiers for armature ????
+ if(parent instanceof Node) {
+ ((Node)parent).attachChild(armature);
+ }
+ armature.updateModelBound();//I prefer do calculate bounding box here than read it from the file
+ result = armature;
+ break;
+ default:
+ LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
+ }
+ } finally {
+ blenderContext.popParent();
+ }
+
+ if(result != null) {
+ blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
+
+ //loading constraints connected with this object
+ ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+ constraintHelper.loadConstraints(objectStructure, blenderContext);
+
+ //baking constraints
+ List<Constraint> objectConstraints = blenderContext.getConstraints(objectStructure.getOldMemoryAddress());
+ if(objectConstraints!=null) {
+ for(Constraint objectConstraint : objectConstraints) {
+ objectConstraint.bake();
+ }
+ }
+
+ //reading custom properties
+ Properties properties = this.loadProperties(objectStructure, blenderContext);
+ if(result instanceof Spatial && properties != null && properties.getValue() != null) {
+ ((Spatial)result).setUserData("properties", properties);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method calculates local transformation for the object. Parentage is taken under consideration.
+ * @param objectStructure
+ * the object's structure
+ * @return objects transformation relative to its parent
+ */
+ @SuppressWarnings("unchecked")
+ public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) {
+ //these are transformations in global space
+ DynamicArray<Number> loc = (DynamicArray<Number>)objectStructure.getFieldValue("loc");
+ DynamicArray<Number> size = (DynamicArray<Number>)objectStructure.getFieldValue("size");
+ DynamicArray<Number> rot = (DynamicArray<Number>)objectStructure.getFieldValue("rot");
+
+ //load parent inverse matrix
+ Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
+ Matrix4f parentInv = pParent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
+
+ //create the global matrix (without the scale)
+ Matrix4f globalMatrix = new Matrix4f();
+ globalMatrix.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
+ globalMatrix.setRotationQuaternion(new Quaternion().fromAngles(rot.get(0).floatValue(), rot.get(1).floatValue(), rot.get(2).floatValue()));
+ //compute local matrix
+ Matrix4f localMatrix = parentInv.mult(globalMatrix);
+
+ Vector3f translation = localMatrix.toTranslationVector();
+ Quaternion rotation = localMatrix.toRotationQuat();
+ Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
+
+ if(fixUpAxis) {
+ float y = translation.y;
+ translation.y = translation.z;
+ translation.z = -y;
+
+ y = rotation.getY();
+ float z = rotation.getZ();
+ rotation.set(rotation.getX(), z, -y, rotation.getW());
+
+ y=scale.y;
+ scale.y = scale.z;
+ scale.z = y;
+ }
+
+ //create the result
+ Transform t = new Transform(translation, rotation);
+ t.setScale(scale);
+ return t;
+ }
+
+ /**
+ * This method returns the matrix of a given name for the given structure.
+ * The matrix is NOT transformed if Y axis is up - the raw data is loaded from the blender file.
+ * @param structure
+ * the structure with matrix data
+ * @param matrixName
+ * the name of the matrix
+ * @return the required matrix
+ */
+ public Matrix4f getMatrix(Structure structure, String matrixName) {
+ return this.getMatrix(structure, matrixName, false);
+ }
+
+ /**
+ * This method returns the matrix of a given name for the given structure.
+ * It takes up axis into consideration.
+ * @param structure
+ * the structure with matrix data
+ * @param matrixName
+ * the name of the matrix
+ * @return the required matrix
+ */
+ @SuppressWarnings("unchecked")
+ public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
+ Matrix4f result = new Matrix4f();
+ DynamicArray<Number> obmat = (DynamicArray<Number>)structure.getFieldValue(matrixName);
+ int rowAndColumnSize = Math.abs((int)Math.sqrt(obmat.getTotalSize()));//the matrix must be square
+ for(int i = 0; i < rowAndColumnSize; ++i) {
+ for(int j = 0; j < rowAndColumnSize; ++j) {
+ result.set(i, j, obmat.get(j, i).floatValue());
+ }
+ }
+ if(applyFixUpAxis && fixUpAxis) {
+ Vector3f translation = result.toTranslationVector();
+ Quaternion rotation = result.toRotationQuat();
+ Vector3f scale = this.getScale(result);
+
+ float y = translation.y;
+ translation.y = translation.z;
+ translation.z = -y;
+
+ y = rotation.getY();
+ float z = rotation.getZ();
+ rotation.set(rotation.getX(), z, -y, rotation.getW());
+
+ y=scale.y;
+ scale.y = scale.z;
+ scale.z = y;
+
+ result.loadIdentity();
+ result.setTranslation(translation);
+ result.setRotationQuaternion(rotation);
+ result.setScale(scale);
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the scale from the given matrix.
+ *
+ * @param matrix
+ * the transformation matrix
+ * @return the scale from the given matrix
+ */
+ public Vector3f getScale(Matrix4f matrix) {
+ float scaleX = (float) Math.sqrt(matrix.m00 * matrix.m00 + matrix.m10 * matrix.m10 + matrix.m20 * matrix.m20);
+ float scaleY = (float) Math.sqrt(matrix.m01 * matrix.m01 + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21);
+ float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
+ return new Vector3f(scaleX, scaleY, scaleZ);
+ }
+
+ @Override
+ public void clearState() {
+ fixUpAxis = false;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ int lay = ((Number) structure.getFieldValue("lay")).intValue();
+ return ((lay & blenderContext.getBlenderKey().getLayersToLoad()) != 0
+ && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java b/engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java
new file mode 100644
index 0000000..9c4a2d6
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/objects/Properties.java
@@ -0,0 +1,467 @@
+package com.jme3.scene.plugins.blender.objects;
+
+import com.jme3.export.*;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * The blender object's custom properties.
+ * This class is valid for all versions of blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class Properties implements Cloneable, Savable {
+ private static final Logger LOGGER = Logger.getLogger(Properties.class.getName());
+
+ // property type
+ public static final int IDP_STRING = 0;
+ public static final int IDP_INT = 1;
+ public static final int IDP_FLOAT = 2;
+ public static final int IDP_ARRAY = 5;
+ public static final int IDP_GROUP = 6;
+ // public static final int IDP_ID = 7;//this is not implemented in blender (yet)
+ public static final int IDP_DOUBLE = 8;
+ // the following are valid for blender 2.5x+
+ public static final int IDP_IDPARRAY = 9;
+ public static final int IDP_NUMTYPES = 10;
+
+ protected static final String RNA_PROPERTY_NAME = "_RNA_UI";
+ /** Default name of the property (used if the name is not specified in blender file). */
+ protected static final String DEFAULT_NAME = "Unnamed property";
+
+ /** The name of the property. */
+ private String name;
+ /** The type of the property. */
+ private int type;
+ /** The subtype of the property. Defines the type of array's elements. */
+ private int subType;
+ /** The value of the property. */
+ private Object value;
+ /** The description of the property. */
+ private String description;
+
+ /**
+ * This method loads the property from the belnder file.
+ * @param idPropertyStructure
+ * the ID structure constining the property
+ * @param blenderContext
+ * the blender context
+ * @throws BlenderFileException
+ * an exception is thrown when the belnder file is somehow invalid
+ */
+ public void load(Structure idPropertyStructure, BlenderContext blenderContext) throws BlenderFileException {
+ name = idPropertyStructure.getFieldValue("name").toString();
+ if (name == null || name.length() == 0) {
+ name = DEFAULT_NAME;
+ }
+ subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue();
+ type = ((Number) idPropertyStructure.getFieldValue("type")).intValue();
+
+ // reading the data
+ Structure data = (Structure) idPropertyStructure.getFieldValue("data");
+ int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue();
+ switch (type) {
+ case IDP_STRING: {
+ Pointer pointer = (Pointer) data.getFieldValue("pointer");
+ BlenderInputStream bis = blenderContext.getInputStream();
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
+ bis.setPosition(dataFileBlock.getBlockPosition());
+ value = bis.readString();
+ break;
+ }
+ case IDP_INT:
+ int intValue = ((Number) data.getFieldValue("val")).intValue();
+ value = Integer.valueOf(intValue);
+ break;
+ case IDP_FLOAT:
+ int floatValue = ((Number) data.getFieldValue("val")).intValue();
+ value = Float.valueOf(Float.intBitsToFloat(floatValue));
+ break;
+ case IDP_ARRAY: {
+ Pointer pointer = (Pointer) data.getFieldValue("pointer");
+ BlenderInputStream bis = blenderContext.getInputStream();
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
+ bis.setPosition(dataFileBlock.getBlockPosition());
+ int elementAmount = dataFileBlock.getSize();
+ switch (subType) {
+ case IDP_INT:
+ elementAmount /= 4;
+ int[] intList = new int[elementAmount];
+ for (int i = 0; i < elementAmount; ++i) {
+ intList[i] = bis.readInt();
+ }
+ value = intList;
+ break;
+ case IDP_FLOAT:
+ elementAmount /= 4;
+ float[] floatList = new float[elementAmount];
+ for (int i = 0; i < elementAmount; ++i) {
+ floatList[i] = bis.readFloat();
+ }
+ value = floatList;
+ break;
+ case IDP_DOUBLE:
+ elementAmount /= 8;
+ double[] doubleList = new double[elementAmount];
+ for (int i = 0; i < elementAmount; ++i) {
+ doubleList[i] = bis.readDouble();
+ }
+ value = doubleList;
+ break;
+ default:
+ throw new IllegalStateException("Invalid array subtype: " + subType);
+ }
+ }
+ case IDP_GROUP:
+ Structure group = (Structure) data.getFieldValue("group");
+ List<Structure> dataList = group.evaluateListBase(blenderContext);
+ List<Properties> subProperties = new ArrayList<Properties>(len);
+ for (Structure d : dataList) {
+ Properties properties = new Properties();
+ properties.load(d, blenderContext);
+ subProperties.add(properties);
+ }
+ value = subProperties;
+ break;
+ case IDP_DOUBLE:
+ int doublePart1 = ((Number) data.getFieldValue("val")).intValue();
+ int doublePart2 = ((Number) data.getFieldValue("val2")).intValue();
+ long doubleVal = (long) doublePart2 << 32 | doublePart1;
+ value = Double.valueOf(Double.longBitsToDouble(doubleVal));
+ break;
+ case IDP_IDPARRAY: {
+ Pointer pointer = (Pointer) data.getFieldValue("pointer");
+ List<Structure> arrays = pointer.fetchData(blenderContext.getInputStream());
+ List<Object> result = new ArrayList<Object>(arrays.size());
+ Properties temp = new Properties();
+ for (Structure array : arrays) {
+ temp.load(array, blenderContext);
+ result.add(temp.value);
+ }
+ this.value = result;
+ break;
+ }
+ case IDP_NUMTYPES:
+ throw new UnsupportedOperationException();
+ // case IDP_ID://not yet implemented in blender
+ // return null;
+ default:
+ throw new IllegalStateException("Unknown custom property type: " + type);
+ }
+ this.completeLoading();
+ }
+
+ /**
+ * This method returns the name of the property.
+ * @return the name of the property
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * This method returns the description of the property.
+ * @return the description of the property
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * This method returns the type of the property.
+ * @return the type of the property
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * This method returns the value of the property.
+ * The type of the value depends on the type of the property.
+ * @return the value of the property
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * This method returns the same as getValue if the current property is of
+ * other type than IDP_GROUP and its name matches 'propertyName' param. If
+ * this property is a group property the method tries to find subproperty
+ * value of the given name. The first found value is returnes os <b>use this
+ * method wisely</b>. If no property of a given name is foung - <b>null</b>
+ * is returned.
+ *
+ * @param propertyName
+ * the name of the property
+ * @return found property value or <b>null</b>
+ */
+ @SuppressWarnings("unchecked")
+ public Object findValue(String propertyName) {
+ if (name.equals(propertyName)) {
+ return value;
+ } else {
+ if (type == IDP_GROUP) {
+ List<Properties> props = (List<Properties>) value;
+ for (Properties p : props) {
+ Object v = p.findValue(propertyName);
+ if (v != null) {
+ return v;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ this.append(sb, new StringBuilder());
+ return sb.toString();
+ }
+
+ /**
+ * This method appends the data of the property to the given string buffer.
+ * @param sb
+ * string buffer
+ * @param indent
+ * indent buffer
+ */
+ @SuppressWarnings("unchecked")
+ private void append(StringBuilder sb, StringBuilder indent) {
+ sb.append(indent).append("name: ").append(name).append("\n\r");
+ sb.append(indent).append("type: ").append(type).append("\n\r");
+ sb.append(indent).append("subType: ").append(subType).append("\n\r");
+ sb.append(indent).append("description: ").append(description).append("\n\r");
+ indent.append('\t');
+ sb.append(indent).append("value: ");
+ if (value instanceof Properties) {
+ ((Properties) value).append(sb, indent);
+ } else if (value instanceof List) {
+ for (Object v : (List<Object>) value) {
+ if (v instanceof Properties) {
+ sb.append(indent).append("{\n\r");
+ indent.append('\t');
+ ((Properties) v).append(sb, indent);
+ indent.deleteCharAt(indent.length() - 1);
+ sb.append(indent).append("}\n\r");
+ } else {
+ sb.append(v);
+ }
+ }
+ } else {
+ sb.append(value);
+ }
+ sb.append("\n\r");
+ indent.deleteCharAt(indent.length() - 1);
+ }
+
+ /**
+ * This method should be called after the properties loading.
+ * It loads the properties from the _RNA_UI property and removes this property from the
+ * result list.
+ */
+ @SuppressWarnings("unchecked")
+ protected void completeLoading() {
+ if (this.type == IDP_GROUP) {
+ List<Properties> groupProperties = (List<Properties>) this.value;
+ Properties rnaUI = null;
+ for (Properties properties : groupProperties) {
+ if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) {
+ rnaUI = properties;
+ break;
+ }
+ }
+ if (rnaUI != null) {
+ // removing the RNA from the result list
+ groupProperties.remove(rnaUI);
+
+ // loading the descriptions
+ Map<String, String> descriptions = new HashMap<String, String>(groupProperties.size());
+ List<Properties> propertiesRNA = (List<Properties>) rnaUI.value;
+ for (Properties properties : propertiesRNA) {
+ String name = properties.name;
+ String description = null;
+ List<Properties> rnaData = (List<Properties>) properties.value;
+ for (Properties rna : rnaData) {
+ if ("description".equalsIgnoreCase(rna.name)) {
+ description = (String) rna.value;
+ break;
+ }
+ }
+ descriptions.put(name, description);
+ }
+
+ // applying the descriptions
+ for (Properties properties : groupProperties) {
+ properties.description = descriptions.get(properties.name);
+ }
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", DEFAULT_NAME);
+ oc.write(type, "type", 0);
+ oc.write(subType, "subtype", 0);
+ oc.write(description, "description", null);
+ switch (type) {
+ case IDP_STRING:
+ oc.write((String) value, "value", null);
+ break;
+ case IDP_INT:
+ oc.write((Integer) value, "value", 0);
+ break;
+ case IDP_FLOAT:
+ oc.write((Float) value, "value", 0);
+ break;
+ case IDP_ARRAY:
+ switch (subType) {
+ case IDP_INT:
+ oc.write((int[]) value, "value", null);
+ break;
+ case IDP_FLOAT:
+ oc.write((float[]) value, "value", null);
+ break;
+ case IDP_DOUBLE:
+ oc.write((double[]) value, "value", null);
+ break;
+ default:
+ LOGGER.warning("Cannot save the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType);
+ }
+ case IDP_GROUP:
+ oc.writeSavableArrayList((ArrayList<Properties>) value, "value", null);
+ break;
+ case IDP_DOUBLE:
+ oc.write((Double) value, "value", 0);
+ break;
+ case IDP_IDPARRAY:
+ oc.writeSavableArrayList((ArrayList) value, "value", null);
+ break;
+ case IDP_NUMTYPES:
+ LOGGER.warning("Numtypes value not supported! Cannot write it!");
+ break;
+ // case IDP_ID://not yet implemented in blender
+ // break;
+ default:
+ LOGGER.warning("Cannot save the property's value! Invalid type! Property: name: " + name + "; type: " + type);
+ }
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", DEFAULT_NAME);
+ type = ic.readInt("type", 0);
+ subType = ic.readInt("subtype", 0);
+ description = ic.readString("description", null);
+ switch (type) {
+ case IDP_STRING:
+ value = ic.readString("value", null);
+ break;
+ case IDP_INT:
+ value = Integer.valueOf(ic.readInt("value", 0));
+ break;
+ case IDP_FLOAT:
+ value = Float.valueOf(ic.readFloat("value", 0.0f));
+ break;
+ case IDP_ARRAY:
+ switch (subType) {
+ case IDP_INT:
+ value = ic.readIntArray("value", null);
+ break;
+ case IDP_FLOAT:
+ value = ic.readFloatArray("value", null);
+ break;
+ case IDP_DOUBLE:
+ value = ic.readDoubleArray("value", null);
+ break;
+ default:
+ LOGGER.warning("Cannot read the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType);
+ }
+ case IDP_GROUP:
+ value = ic.readSavable("value", null);
+ break;
+ case IDP_DOUBLE:
+ value = Double.valueOf(ic.readDouble("value", 0.0));
+ break;
+ case IDP_IDPARRAY:
+ value = ic.readSavableArrayList("value", null);
+ break;
+ case IDP_NUMTYPES:
+ LOGGER.warning("Numtypes value not supported! Cannot read it!");
+ break;
+ // case IDP_ID://not yet implemented in blender
+ // break;
+ default:
+ LOGGER.warning("Cannot read the property's value! Invalid type! Property: name: " + name + "; type: " + type);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (description == null ? 0 : description.hashCode());
+ result = prime * result + (name == null ? 0 : name.hashCode());
+ result = prime * result + subType;
+ result = prime * result + type;
+ result = prime * result + (value == null ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (this.getClass() != obj.getClass()) {
+ return false;
+ }
+ Properties other = (Properties) obj;
+ if (description == null) {
+ if (other.description != null) {
+ return false;
+ }
+ } else if (!description.equals(other.description)) {
+ return false;
+ }
+ if (name == null) {
+ if (other.name != null) {
+ return false;
+ }
+ } else if (!name.equals(other.name)) {
+ return false;
+ }
+ if (subType != other.subType) {
+ return false;
+ }
+ if (type != other.type) {
+ return false;
+ }
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java
new file mode 100644
index 0000000..de1252a
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java
@@ -0,0 +1,196 @@
+package com.jme3.scene.plugins.blender.particles;
+
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.influencers.EmptyParticleInfluencer;
+import com.jme3.effect.influencers.NewtonianParticleInfluencer;
+import com.jme3.effect.influencers.ParticleInfluencer;
+import com.jme3.effect.shapes.EmitterMeshConvexHullShape;
+import com.jme3.effect.shapes.EmitterMeshFaceShape;
+import com.jme3.effect.shapes.EmitterMeshVertexShape;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import java.util.logging.Logger;
+
+public class ParticlesHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(ParticlesHelper.class.getName());
+
+ // part->type
+ public static final int PART_EMITTER = 0;
+ public static final int PART_REACTOR = 1;
+ public static final int PART_HAIR = 2;
+ public static final int PART_FLUID = 3;
+
+ // part->flag
+ public static final int PART_REACT_STA_END =1;
+ public static final int PART_REACT_MULTIPLE =2;
+ public static final int PART_LOOP =4;
+ //public static final int PART_LOOP_INSTANT =8;
+ public static final int PART_HAIR_GEOMETRY =16;
+ public static final int PART_UNBORN =32; //show unborn particles
+ public static final int PART_DIED =64; //show died particles
+ public static final int PART_TRAND =128;
+ public static final int PART_EDISTR =256; // particle/face from face areas
+ public static final int PART_STICKY =512; //collided particles can stick to collider
+ public static final int PART_DIE_ON_COL =1<<12;
+ public static final int PART_SIZE_DEFL =1<<13; // swept sphere deflections
+ public static final int PART_ROT_DYN =1<<14; // dynamic rotation
+ public static final int PART_SIZEMASS =1<<16;
+ public static final int PART_ABS_LENGTH =1<<15;
+ public static final int PART_ABS_TIME =1<<17;
+ public static final int PART_GLOB_TIME =1<<18;
+ public static final int PART_BOIDS_2D =1<<19;
+ public static final int PART_BRANCHING =1<<20;
+ public static final int PART_ANIM_BRANCHING =1<<21;
+ public static final int PART_SELF_EFFECT =1<<22;
+ public static final int PART_SYMM_BRANCHING =1<<24;
+ public static final int PART_HAIR_BSPLINE =1024;
+ public static final int PART_GRID_INVERT =1<<26;
+ public static final int PART_CHILD_EFFECT =1<<27;
+ public static final int PART_CHILD_SEAMS =1<<28;
+ public static final int PART_CHILD_RENDER =1<<29;
+ public static final int PART_CHILD_GUIDE =1<<30;
+
+ // part->from
+ public static final int PART_FROM_VERT =0;
+ public static final int PART_FROM_FACE =1;
+ public static final int PART_FROM_VOLUME =2;
+ public static final int PART_FROM_PARTICLE =3;
+ public static final int PART_FROM_CHILD =4;
+
+ // part->phystype
+ public static final int PART_PHYS_NO = 0;
+ public static final int PART_PHYS_NEWTON= 1;
+ public static final int PART_PHYS_KEYED = 2;
+ public static final int PART_PHYS_BOIDS = 3;
+
+ // part->draw_as
+ public static final int PART_DRAW_NOT = 0;
+ public static final int PART_DRAW_DOT = 1;
+ public static final int PART_DRAW_CIRC = 2;
+ public static final int PART_DRAW_CROSS = 3;
+ public static final int PART_DRAW_AXIS = 4;
+ public static final int PART_DRAW_LINE = 5;
+ public static final int PART_DRAW_PATH = 6;
+ public static final int PART_DRAW_OB = 7;
+ public static final int PART_DRAW_GR = 8;
+ public static final int PART_DRAW_BB = 9;
+
+ /**
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in
+ * different blender versions.
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public ParticlesHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, fixUpAxis);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ParticleEmitter toParticleEmitter(Structure particleSystem, BlenderContext blenderContext) throws BlenderFileException {
+ ParticleEmitter result = null;
+ Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part");
+ if(pParticleSettings.isNotNull()) {
+ Structure particleSettings = pParticleSettings.fetchData(blenderContext.getInputStream()).get(0);
+
+ int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue();
+
+ //draw type will be stored temporarily in the name (it is used during modifier applying operation)
+ int drawAs = ((Number)particleSettings.getFieldValue("draw_as")).intValue();
+ char nameSuffix;//P - point, L - line, N - None, B - Bilboard
+ switch(drawAs) {
+ case PART_DRAW_NOT:
+ nameSuffix = 'N';
+ totPart = 0;//no need to generate particles in this case
+ break;
+ case PART_DRAW_BB:
+ nameSuffix = 'B';
+ break;
+ case PART_DRAW_OB:
+ case PART_DRAW_GR:
+ nameSuffix = 'P';
+ LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");//TODO: support groups and aobjects
+ break;
+ case PART_DRAW_LINE:
+ nameSuffix = 'L';
+ LOGGER.warning("Lines not yet supported! Using point representation instead!");//TODO: support lines
+ default://all others are rendered as points in blender
+ nameSuffix = 'P';
+ }
+ result = new ParticleEmitter(particleSettings.getName()+nameSuffix, Type.Triangle, totPart);
+ if(nameSuffix=='N') {
+ return result;//no need to set anything else
+ }
+
+ //setting the emitters shape (the shapes meshes will be set later during modifier applying operation)
+ int from = ((Number)particleSettings.getFieldValue("from")).intValue();
+ switch(from) {
+ case PART_FROM_VERT:
+ result.setShape(new EmitterMeshVertexShape());
+ break;
+ case PART_FROM_FACE:
+ result.setShape(new EmitterMeshFaceShape());
+ break;
+ case PART_FROM_VOLUME:
+ result.setShape(new EmitterMeshConvexHullShape());
+ break;
+ default:
+ LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')');
+ }
+
+ //reading acceleration
+ DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc");
+ result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue());
+
+ //setting the colors
+ result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));
+ result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));
+
+ //reading size
+ float sizeFactor = nameSuffix=='B' ? 1.0f : 0.3f;
+ float size = ((Number)particleSettings.getFieldValue("size")).floatValue() * sizeFactor;
+ result.setStartSize(size);
+ result.setEndSize(size);
+
+ //reading lifetime
+ int fps = blenderContext.getBlenderKey().getFps();
+ float lifetime = ((Number)particleSettings.getFieldValue("lifetime")).floatValue() / fps;
+ float randlife = ((Number)particleSettings.getFieldValue("randlife")).floatValue() / fps;
+ result.setLowLife(lifetime * (1.0f - randlife));
+ result.setHighLife(lifetime);
+
+ //preparing influencer
+ ParticleInfluencer influencer;
+ int phystype = ((Number)particleSettings.getFieldValue("phystype")).intValue();
+ switch(phystype) {
+ case PART_PHYS_NEWTON:
+ influencer = new NewtonianParticleInfluencer();
+ ((NewtonianParticleInfluencer)influencer).setNormalVelocity(((Number)particleSettings.getFieldValue("normfac")).floatValue());
+ ((NewtonianParticleInfluencer)influencer).setVelocityVariation(((Number)particleSettings.getFieldValue("randfac")).floatValue());
+ ((NewtonianParticleInfluencer)influencer).setSurfaceTangentFactor(((Number)particleSettings.getFieldValue("tanfac")).floatValue());
+ ((NewtonianParticleInfluencer)influencer).setSurfaceTangentRotation(((Number)particleSettings.getFieldValue("tanphase")).floatValue());
+ break;
+ case PART_PHYS_BOIDS:
+ case PART_PHYS_KEYED://TODO: support other influencers
+ LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!");
+ case PART_PHYS_NO:
+ default:
+ influencer = new EmptyParticleInfluencer();
+ }
+ result.setParticleInfluencer(influencer);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java
new file mode 100644
index 0000000..d124616
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.texture.Image;
+import com.jme3.texture.plugins.AWTLoader;
+import com.jme3.texture.plugins.DDSLoader;
+import com.jme3.texture.plugins.TGALoader;
+import java.io.InputStream;
+import java.util.logging.Logger;
+
+/**
+ * An image loader class. It uses three loaders (AWTLoader, TGALoader and DDSLoader) in an attempt to load the image from the given
+ * input stream.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ImageLoader extends AWTLoader {
+ private static final Logger LOGGER = Logger.getLogger(ImageLoader.class.getName());
+
+ protected DDSLoader ddsLoader = new DDSLoader(); // DirectX image loader
+
+ /**
+ * This method loads the image from the blender file itself. It tries each loader to load the image.
+ *
+ * @param inputStream
+ * blender input stream
+ * @param startPosition
+ * position in the stream where the image data starts
+ * @param flipY
+ * if the image should be flipped (does not work with DirectX image)
+ * @return loaded image or null if it could not be loaded
+ */
+ public Image loadImage(BlenderInputStream inputStream, int startPosition, boolean flipY) {
+ // loading using AWT loader
+ inputStream.setPosition(startPosition);
+ Image result = this.loadImage(inputStream, ImageType.AWT, flipY);
+ // loading using TGA loader
+ if (result == null) {
+ inputStream.setPosition(startPosition);
+ result = this.loadImage(inputStream, ImageType.TGA, flipY);
+ }
+ // loading using DDS loader
+ if (result == null) {
+ inputStream.setPosition(startPosition);
+ result = this.loadImage(inputStream, ImageType.DDS, flipY);
+ }
+
+ if (result == null) {
+ LOGGER.warning("Image could not be loaded by none of available loaders!");
+ }
+
+ return result;
+ }
+
+ /**
+ * This method loads an image of a specified type from the given input stream.
+ *
+ * @param inputStream
+ * the input stream we read the image from
+ * @param imageType
+ * the type of the image {@link ImageType}
+ * @param flipY
+ * if the image should be flipped (does not work with DirectX image)
+ * @return loaded image or null if it could not be loaded
+ */
+ public Image loadImage(InputStream inputStream, ImageType imageType, boolean flipY) {
+ Image result = null;
+ switch (imageType) {
+ case AWT:
+ try {
+ result = this.load(inputStream, flipY);
+ } catch (Exception e) {
+ LOGGER.info("Unable to load image using AWT loader!");
+ }
+ break;
+ case DDS:
+ try {
+ result = ddsLoader.load(inputStream);
+ } catch (Exception e) {
+ LOGGER.info("Unable to load image using DDS loader!");
+ }
+ break;
+ case TGA:
+ try {
+ result = TGALoader.load(inputStream, flipY);
+ } catch (Exception e) {
+ LOGGER.info("Unable to load image using TGA loader!");
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown image type: " + imageType);
+ }
+ return result;
+ }
+
+ /**
+ * Image types that can be loaded. AWT: png, jpg, jped or bmp TGA: tga DDS: DirectX image files
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ private static enum ImageType {
+ AWT, TGA, DDS;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java
new file mode 100644
index 0000000..a8bd890
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TextureGeneratorMusgrave.MusgraveData;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This generator is responsible for creating various noises used to create
+ * generated textures loaded from blender.
+ * It derives from AbstractBlenderHelper but is not stored in blender context.
+ * It is only used by TextureHelper.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class NoiseGenerator extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(NoiseGenerator.class.getName());
+
+ // flag
+ protected static final int TEX_COLORBAND = 1;
+ protected static final int TEX_FLIPBLEND = 2;
+ protected static final int TEX_NEGALPHA = 4;
+ protected static final int TEX_CHECKER_ODD = 8;
+ protected static final int TEX_CHECKER_EVEN = 16;
+ protected static final int TEX_PRV_ALPHA = 32;
+ protected static final int TEX_PRV_NOR = 64;
+ protected static final int TEX_REPEAT_XMIR = 128;
+ protected static final int TEX_REPEAT_YMIR = 256;
+ protected static final int TEX_FLAG_MASK = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;
+
+ // tex->stype
+ protected static final int TEX_PLASTIC = 0;
+ protected static final int TEX_WALLIN = 1;
+ protected static final int TEX_WALLOUT = 2;
+
+ // musgrave stype
+ protected static final int TEX_MFRACTAL = 0;
+ protected static final int TEX_RIDGEDMF = 1;
+ protected static final int TEX_HYBRIDMF = 2;
+ protected static final int TEX_FBM = 3;
+ protected static final int TEX_HTERRAIN = 4;
+
+ // keyblock->type
+ protected static final int KEY_LINEAR = 0;
+ protected static final int KEY_CARDINAL = 1;
+ protected static final int KEY_BSPLINE = 2;
+
+ // CONSTANTS (read from file)
+ protected static float[] hashpntf;
+ protected static short[] hash;
+ protected static float[] hashvectf;
+ protected static short[] p;
+ protected static float[][] g;
+
+ /**
+ * Constructor. Stores the blender version number and loads the constants needed for computations.
+ * @param blenderVersion
+ * the number of blender version
+ */
+ public NoiseGenerator(String blenderVersion) {
+ super(blenderVersion, false);
+ this.loadConstants();
+ }
+
+ /**
+ * This method loads the constants needed for computations. They are exactly like the ones the blender uses. Each
+ * deriving class should override this method and load its own constraints. Be carefult with overriding though, if
+ * an exception will be thrown the class will not be instantiated.
+ */
+ protected void loadConstants() {
+ InputStream is = NoiseGenerator.class.getResourceAsStream("noiseconstants.dat");
+ try {
+ ObjectInputStream ois = new ObjectInputStream(is);
+ hashpntf = (float[]) ois.readObject();
+ hash = (short[]) ois.readObject();
+ hashvectf = (float[]) ois.readObject();
+ p = (short[]) ois.readObject();
+ g = (float[][]) ois.readObject();
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ } catch (ClassNotFoundException e) {
+ assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!";
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, e.getLocalizedMessage());
+ }
+ }
+ }
+ }
+
+ protected static Map<Integer, NoiseFunction> noiseFunctions = new HashMap<Integer, NoiseFunction>();
+ static {
+ noiseFunctions.put(Integer.valueOf(0), new NoiseFunction() {
+ // originalBlenderNoise
+ @Override
+ public float execute(float x, float y, float z) {
+ return NoiseFunctions.originalBlenderNoise(x, y, z);
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return 2.0f * NoiseFunctions.originalBlenderNoise(x, y, z) - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(1), new NoiseFunction() {
+ // orgPerlinNoise
+ @Override
+ public float execute(float x, float y, float z) {
+ return 0.5f + 0.5f * NoiseFunctions.noise3Perlin(x, y, z);
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return NoiseFunctions.noise3Perlin(x, y, z);
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(2), new NoiseFunction() {
+ // newPerlin
+ @Override
+ public float execute(float x, float y, float z) {
+ return 0.5f + 0.5f * NoiseFunctions.newPerlin(x, y, z);
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return this.execute(x, y, z);
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(3), new NoiseFunction() {
+ // voronoi_F1
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[0];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[0] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(4), new NoiseFunction() {
+ // voronoi_F2
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[1];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[1] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(5), new NoiseFunction() {
+ // voronoi_F3
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[2];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[2] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(6), new NoiseFunction() {
+ // voronoi_F4
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[3];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[3] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(7), new NoiseFunction() {
+ // voronoi_F1F2
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[1] - da[0];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * (da[1] - da[0]) - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(8), new NoiseFunction() {
+ // voronoi_Cr
+ @Override
+ public float execute(float x, float y, float z) {
+ float t = 10 * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2
+ return t > 1.0f ? 1.0f : t;
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float t = 10.0f * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2
+ return t > 1.0f ? 1.0f : 2.0f * t - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(14), new NoiseFunction() {
+ // cellNoise
+ @Override
+ public float execute(float x, float y, float z) {
+ int xi = (int) Math.floor(x);
+ int yi = (int) Math.floor(y);
+ int zi = (int) Math.floor(z);
+ long n = xi + yi * 1301 + zi * 314159;
+ n ^= n << 13;
+ return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f;
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return 2.0f * this.execute(x, y, z) - 1.0f;
+ }
+ });
+ }
+ /** Distance metrics for voronoi. e parameter only used in Minkovsky. */
+ protected static Map<Integer, DistanceFunction> distanceFunctions = new HashMap<Integer, NoiseGenerator.DistanceFunction>();
+
+ static {
+ distanceFunctions.put(Integer.valueOf(0), new DistanceFunction() {
+ // real distance
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return (float) Math.sqrt(x * x + y * y + z * z);
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(1), new DistanceFunction() {
+ // distance squared
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return x * x + y * y + z * z;
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(2), new DistanceFunction() {
+ // manhattan/taxicab/cityblock distance
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(3), new DistanceFunction() {
+ // Chebychev
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ x = FastMath.abs(x);
+ y = FastMath.abs(y);
+ z = FastMath.abs(z);
+ float t = x > y ? x : y;
+ return z > t ? z : t;
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(4), new DistanceFunction() {
+ // Minkovsky, preset exponent 0.5 (MinkovskyH)
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ float d = (float) (Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z)));
+ return d * d;
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(5), new DistanceFunction() {
+ // Minkovsky, preset exponent 0.25 (Minkovsky4)
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ x *= x;
+ y *= y;
+ z *= z;
+ return (float) Math.sqrt(Math.sqrt(x * x + y * y + z * z));
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(6), new DistanceFunction() {
+ // Minkovsky, general case
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return (float) Math.pow(Math.pow(FastMath.abs(x), e) + Math.pow(FastMath.abs(y), e) + Math.pow(FastMath.abs(z), e), 1.0f / e);
+ }
+ });
+ }
+
+ protected static Map<Integer, MusgraveFunction> musgraveFunctions = new HashMap<Integer, NoiseGenerator.MusgraveFunction>();
+ static {
+ musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ for (int i = 0; i < (int) musgraveData.octaves; ++i) {
+ value *= pwr * abstractNoiseFunc.executeSigned(x, y, z) + 1.0f;
+ pwr *= pwHL;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+ rmd = (float) (musgraveData.octaves - Math.floor(musgraveData.octaves));
+ if (rmd != 0.0f) {
+ value *= rmd * abstractNoiseFunc.executeSigned(x, y, z) * pwr + 1.0f;
+ }
+ return value;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float result, signal, weight;
+ float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ float pwr = pwHL;
+
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ signal = musgraveData.offset - FastMath.abs(abstractNoiseFunc.executeSigned(x, y, z));
+ signal *= signal;
+ result = signal;
+ weight = 1.0f;
+
+ for (int i = 1; i < (int) musgraveData.octaves; ++i) {
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ weight = signal * musgraveData.gain;
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ } else if (weight < 0.0) {
+ weight = 0.0f;
+ }
+ signal = musgraveData.offset - FastMath.abs(abstractNoiseFunc.executeSigned(x, y, z));
+ signal *= signal;
+ signal *= weight;
+ result += signal * pwr;
+ pwr *= pwHL;
+ }
+ return result;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float result, signal, weight, rmd;
+ float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ float pwr = pwHL;
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ result = abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset;
+ weight = musgraveData.gain * result;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+
+ for (int i = 1; weight > 0.001f && i < (int) musgraveData.octaves; ++i) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+ signal = (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr;
+ pwr *= pwHL;
+ result += weight * signal;
+ weight *= musgraveData.gain * signal;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+
+ rmd = musgraveData.octaves - (float) Math.floor(musgraveData.octaves);
+ if (rmd != 0.0f) {
+ result += rmd * (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr;
+ }
+ return result;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_FBM), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ for (int i = 0; i < (int) musgraveData.octaves; ++i) {
+ value += abstractNoiseFunc.executeSigned(x, y, z) * pwr;
+ pwr *= pwHL;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+
+ rmd = (float) (musgraveData.octaves - Math.floor(musgraveData.octaves));
+ if (rmd != 0.f) {
+ value += rmd * abstractNoiseFunc.executeSigned(x, y, z) * pwr;
+ }
+ return value;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float value, increment, rmd;
+ float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ float pwr = pwHL;
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ value = musgraveData.offset + abstractNoiseFunc.executeSigned(x, y, z);
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+
+ for (int i = 1; i < (int) musgraveData.octaves; ++i) {
+ increment = (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+
+ rmd = musgraveData.octaves - (float) Math.floor(musgraveData.octaves);
+ if (rmd != 0.0) {
+ increment = (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr * value;
+ value += rmd * increment;
+ }
+ return value;
+ }
+ });
+ }
+
+ public static class NoiseFunctions {
+ public static float noise(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(0);
+ noiseBasis = 0;
+ }
+
+ if (noiseBasis == 0) {
+ ++x;
+ ++y;
+ ++z;
+ }
+
+ if (noiseSize != 0.0) {
+ noiseSize = 1.0f / noiseSize;
+ x *= noiseSize;
+ y *= noiseSize;
+ z *= noiseSize;
+ }
+ float result = abstractNoiseFunc.execute(x, y, z);
+ return isHard ? Math.abs(2.0f * result - 1.0f) : result;
+ }
+
+ public static float turbulence(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(0);
+ noiseBasis = 0;
+ }
+
+ if (noiseBasis == 0) {
+ ++x;
+ ++y;
+ ++z;
+ }
+ if (noiseSize != 0.0) {
+ noiseSize = 1.0f / noiseSize;
+ x *= noiseSize;
+ y *= noiseSize;
+ z *= noiseSize;
+ }
+
+ float sum = 0, t, amp = 1, fscale = 1;
+ for (int i = 0; i <= noiseDepth; ++i, amp *= 0.5, fscale *= 2) {
+ t = abstractNoiseFunc.execute(fscale * x, fscale * y, fscale * z);
+ if (isHard) {
+ t = FastMath.abs(2.0f * t - 1.0f);
+ }
+ sum += t * amp;
+ }
+
+ sum *= (float) (1 << noiseDepth) / (float) ((1 << noiseDepth + 1) - 1);
+ return sum;
+ }
+
+ /**
+ * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa
+ */
+ public static void voronoi(float x, float y, float z, float[] da, float[] pa, float distanceExponent, int distanceType) {
+ float xd, yd, zd, d, p[] = new float[3];
+
+ DistanceFunction distanceFunc = distanceFunctions.get(Integer.valueOf(distanceType));
+ if (distanceFunc == null) {
+ distanceFunc = distanceFunctions.get(Integer.valueOf(0));
+ }
+
+ int xi = (int) FastMath.floor(x);
+ int yi = (int) FastMath.floor(y);
+ int zi = (int) FastMath.floor(z);
+ da[0] = da[1] = da[2] = da[3] = 1e10f;
+ for (int i = xi - 1; i <= xi + 1; ++i) {
+ for (int j = yi - 1; j <= yi + 1; ++j) {
+ for (int k = zi - 1; k <= zi + 1; ++k) {
+ NoiseMath.hash(i, j, k, p);
+ xd = x - (p[0] + i);
+ yd = y - (p[1] + j);
+ zd = z - (p[2] + k);
+ d = distanceFunc.execute(xd, yd, zd, distanceExponent);
+ if (d < da[0]) {
+ da[3] = da[2];
+ da[2] = da[1];
+ da[1] = da[0];
+ da[0] = d;
+ pa[9] = pa[6];
+ pa[10] = pa[7];
+ pa[11] = pa[8];
+ pa[6] = pa[3];
+ pa[7] = pa[4];
+ pa[8] = pa[5];
+ pa[3] = pa[0];
+ pa[4] = pa[1];
+ pa[5] = pa[2];
+ pa[0] = p[0] + i;
+ pa[1] = p[1] + j;
+ pa[2] = p[2] + k;
+ } else if (d < da[1]) {
+ da[3] = da[2];
+ da[2] = da[1];
+ da[1] = d;
+ pa[9] = pa[6];
+ pa[10] = pa[7];
+ pa[11] = pa[8];
+ pa[6] = pa[3];
+ pa[7] = pa[4];
+ pa[8] = pa[5];
+ pa[3] = p[0] + i;
+ pa[4] = p[1] + j;
+ pa[5] = p[2] + k;
+ } else if (d < da[2]) {
+ da[3] = da[2];
+ da[2] = d;
+ pa[9] = pa[6];
+ pa[10] = pa[7];
+ pa[11] = pa[8];
+ pa[6] = p[0] + i;
+ pa[7] = p[1] + j;
+ pa[8] = p[2] + k;
+ } else if (d < da[3]) {
+ da[3] = d;
+ pa[9] = p[0] + i;
+ pa[10] = p[1] + j;
+ pa[11] = p[2] + k;
+ }
+ }
+ }
+ }
+ }
+
+ // instead of adding another permutation array, just use hash table defined above
+ public static float newPerlin(float x, float y, float z) {
+ int A, AA, AB, B, BA, BB;
+ float floorX = (float) Math.floor(x), floorY = (float) Math.floor(y), floorZ = (float) Math.floor(z);
+ int intX = (int) floorX & 0xFF, intY = (int) floorY & 0xFF, intZ = (int) floorZ & 0xFF;
+ x -= floorX;
+ y -= floorY;
+ z -= floorZ;
+ //computing fading curves
+ floorX = NoiseMath.npfade(x);
+ floorY = NoiseMath.npfade(y);
+ floorZ = NoiseMath.npfade(z);
+ A = hash[intX] + intY;
+ AA = hash[A] + intZ;
+ AB = hash[A + 1] + intZ;
+ B = hash[intX + 1] + intY;
+ BA = hash[B] + intZ;
+ BB = hash[B + 1] + intZ;
+ return NoiseMath.lerp(floorZ, NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA], x, y, z),
+ NoiseMath.grad(hash[BA], x - 1, y, z)),
+ NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB], x, y - 1, z),
+ NoiseMath.grad(hash[BB], x - 1, y - 1, z))),
+ NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA + 1], x, y, z - 1),
+ NoiseMath.grad(hash[BA + 1], x - 1, y, z - 1)),
+ NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB + 1], x, y - 1, z - 1),
+ NoiseMath.grad(hash[BB + 1], x - 1, y - 1, z - 1))));
+ }
+
+ public static float noise3Perlin(float x, float y, float z) {
+ float t = x + 10000.0f;
+ int bx0 = (int) t & 0xFF;
+ int bx1 = bx0 + 1 & 0xFF;
+ float rx0 = t - (int) t;
+ float rx1 = rx0 - 1.0f;
+
+ t = y + 10000.0f;
+ int by0 = (int) t & 0xFF;
+ int by1 = by0 + 1 & 0xFF;
+ float ry0 = t - (int) t;
+ float ry1 = ry0 - 1.0f;
+
+ t = z + 10000.0f;
+ int bz0 = (int) t & 0xFF;
+ int bz1 = bz0 + 1 & 0xFF;
+ float rz0 = t - (int) t;
+ float rz1 = rz0 - 1.0f;
+
+ int i = p[bx0];
+ int j = p[bx1];
+
+ int b00 = p[i + by0];
+ int b10 = p[j + by0];
+ int b01 = p[i + by1];
+ int b11 = p[j + by1];
+
+ float sx = NoiseMath.surve(rx0);
+ float sy = NoiseMath.surve(ry0);
+ float sz = NoiseMath.surve(rz0);
+
+ float[] q = g[b00 + bz0];
+ float u = NoiseMath.at(rx0, ry0, rz0, q);
+ q = g[b10 + bz0];
+ float v = NoiseMath.at(rx1, ry0, rz0, q);
+ float a = NoiseMath.lerp(sx, u, v);
+
+ q = g[b01 + bz0];
+ u = NoiseMath.at(rx0, ry1, rz0, q);
+ q = g[b11 + bz0];
+ v = NoiseMath.at(rx1, ry1, rz0, q);
+ float b = NoiseMath.lerp(sx, u, v);
+
+ float c = NoiseMath.lerp(sy, a, b);
+
+ q = g[b00 + bz1];
+ u = NoiseMath.at(rx0, ry0, rz1, q);
+ q = g[b10 + bz1];
+ v = NoiseMath.at(rx1, ry0, rz1, q);
+ a = NoiseMath.lerp(sx, u, v);
+
+ q = g[b01 + bz1];
+ u = NoiseMath.at(rx0, ry1, rz1, q);
+ q = g[b11 + bz1];
+ v = NoiseMath.at(rx1, ry1, rz1, q);
+ b = NoiseMath.lerp(sx, u, v);
+
+ float d = NoiseMath.lerp(sy, a, b);
+ return 1.5f * NoiseMath.lerp(sz, c, d);
+ }
+
+ public static float originalBlenderNoise(float x, float y, float z) {
+ float n = 0.5f;
+
+ int ix = (int) Math.floor(x);
+ int iy = (int) Math.floor(y);
+ int iz = (int) Math.floor(z);
+
+ float ox = x - ix;
+ float oy = y - iy;
+ float oz = z - iz;
+
+ float jx = ox - 1;
+ float jy = oy - 1;
+ float jz = oz - 1;
+
+ float cn1 = ox * ox;
+ float cn2 = oy * oy;
+ float cn3 = oz * oz;
+ float cn4 = jx * jx;
+ float cn5 = jy * jy;
+ float cn6 = jz * jz;
+
+ cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox;
+ cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy;
+ cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz;
+ cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx;
+ cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy;
+ cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz;
+ float[] cn = new float[] {cn1 * cn2 * cn3, cn1 * cn2 * cn6, cn1 * cn5 * cn3, cn1 * cn5 * cn6,
+ cn4 * cn2 * cn3, cn4 * cn2 * cn6, cn4 * cn5 * cn3, cn4 * cn5 * cn6,};
+
+ int b00 = hash[hash[ix & 0xFF] + (iy & 0xFF)];
+ int b01 = hash[hash[ix & 0xFF] + (iy + 1 & 0xFF)];
+ int b10 = hash[hash[ix + 1 & 0xFF] + (iy & 0xFF)];
+ int b11 = hash[hash[ix + 1 & 0xFF] + (iy + 1 & 0xFF)];
+ int[] b1 = new int[] {b00, b00, b01, b01, b10, b10, b11, b11};
+
+ int[] b2 = new int[] {iz & 0xFF, iz + 1 & 0xFF};
+
+ float[] xFactor = new float[] {ox, ox, ox, ox, jx, jx, jx, jx};
+ float[] yFactor = new float[] {oy, oy, jy, jy, oy, oy, jy, jy};
+ float[] zFactor = new float[] {oz, jz, oz, jz, oz, jz, oz, jz};
+
+ for(int i=0;i<8;++i) {
+ int hIndex = 3 * hash[b1[i] + b2[i%2]];
+ n += cn[i] * (hashvectf[hIndex] * xFactor[i] + hashvectf[hIndex + 1] * yFactor[i] + hashvectf[hIndex + 2] * zFactor[i]);
+ }
+
+ if (n < 0.0f) {
+ n = 0.0f;
+ } else if (n > 1.0f) {
+ n = 1.0f;
+ }
+ return n;
+ }
+ }
+
+ /**
+ * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with
+ * 'S' at the end) and the other Unsigned value.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ interface NoiseFunction {
+
+ /**
+ * This method calculates the unsigned value of the noise.
+ * @param x
+ * the x texture coordinate
+ * @param y
+ * the y texture coordinate
+ * @param z
+ * the z texture coordinate
+ * @return value of the noise
+ */
+ float execute(float x, float y, float z);
+
+ /**
+ * This method calculates the signed value of the noise.
+ * @param x
+ * the x texture coordinate
+ * @param y
+ * the y texture coordinate
+ * @param z
+ * the z texture coordinate
+ * @return value of the noise
+ */
+ float executeSigned(float x, float y, float z);
+ }
+
+ public static class NoiseMath {
+ public static float lerp(float t, float a, float b) {
+ return a + t * (b - a);
+ }
+
+ public static float npfade(float t) {
+ return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
+ }
+
+ public static float grad(int hash, float x, float y, float z) {
+ int h = hash & 0x0F;
+ float u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
+ return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
+ }
+
+ public static float surve(float t) {
+ return t * t * (3.0f - 2.0f * t);
+ }
+
+ public static float at(float x, float y, float z, float[] q) {
+ return x * q[0] + y * q[1] + z * q[2];
+ }
+
+ public static void hash(int x, int y, int z, float[] result) {
+ result[0] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF]];
+ result[1] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 1];
+ result[2] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 2];
+ }
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+
+ /**
+ * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in
+ * Minkovsky.
+ */
+ interface DistanceFunction {
+
+ /**
+ * This method calculates the distance for voronoi algorithms.
+ * @param x
+ * the x coordinate
+ * @param y
+ * the y coordinate
+ * @param z
+ * the z coordinate
+ * @param e
+ * this parameter used in Monkovsky (no idea what it really is ;)
+ * @return
+ */
+ float execute(float x, float y, float z, float e);
+ }
+
+ interface MusgraveFunction {
+
+ float execute(MusgraveData musgraveData, float x, float y, float z);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java
new file mode 100644
index 0000000..a727277
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Texture;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is a base class for texture generators.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */abstract class TextureGenerator {
+ private static final Logger LOGGER = Logger.getLogger(TextureGenerator.class.getName());
+
+ protected NoiseGenerator noiseGenerator;
+
+ public TextureGenerator(NoiseGenerator noiseGenerator) {
+ this.noiseGenerator = noiseGenerator;
+ }
+
+ /**
+ * This method generates the texture.
+ * @param tex
+ * texture's structure
+ * @param width
+ * the width of the result texture
+ * @param height
+ * the height of the result texture
+ * @param depth
+ * the depth of the texture
+ * @param blenderContext
+ * the blender context
+ * @return newly generated texture
+ */
+ protected abstract Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext);
+
+ /**
+ * This method reads the colorband data from the given texture structure.
+ *
+ * @param tex
+ * the texture structure
+ * @param blenderContext
+ * the blender context
+ * @return read colorband or null if not present
+ */
+ private ColorBand readColorband(Structure tex, BlenderContext blenderContext) {
+ ColorBand result = null;
+ int flag = ((Number) tex.getFieldValue("flag")).intValue();
+ if ((flag & NoiseGenerator.TEX_COLORBAND) != 0) {
+ Pointer pColorband = (Pointer) tex.getFieldValue("coba");
+ Structure colorbandStructure;
+ try {
+ colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
+ result = new ColorBand(colorbandStructure);
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
+ }
+ }
+ return result;
+ }
+
+ protected float[][] computeColorband(Structure tex, BlenderContext blenderContext) {
+ ColorBand colorBand = this.readColorband(tex, blenderContext);
+ float[][] result = null;
+ if(colorBand!=null) {
+ result = new float[1001][4];//1001 - amount of possible cursor positions; 4 = [r, g, b, a]
+ ColorBandData[] dataArray = colorBand.data;
+
+ if(dataArray.length==1) {//special case; use only one color for all types of colorband interpolation
+ for(int i=0;i<result.length;++i) {
+ result[i][0] = dataArray[0].r;
+ result[i][1] = dataArray[0].g;
+ result[i][2] = dataArray[0].b;
+ result[i][3] = dataArray[0].a;
+ }
+ } else {
+ int currentCursor = 0;
+ ColorBandData currentData = dataArray[0];
+ ColorBandData nextData = dataArray[0];
+ switch(colorBand.ipoType) {
+ case ColorBand.IPO_LINEAR:
+ float rDiff = 0, gDiff = 0, bDiff = 0, aDiff = 0, posDiff;
+ for(int i=0;i<result.length;++i) {
+ posDiff = i - currentData.pos;
+ result[i][0] = currentData.r + rDiff * posDiff;
+ result[i][1] = currentData.g + gDiff * posDiff;
+ result[i][2] = currentData.b + bDiff * posDiff;
+ result[i][3] = currentData.a + aDiff * posDiff;
+ if(nextData.pos==i) {
+ currentData = dataArray[currentCursor++];
+ if(currentCursor < dataArray.length) {
+ nextData = dataArray[currentCursor];
+ //calculate differences
+ int d = nextData.pos - currentData.pos;
+ rDiff = (nextData.r - currentData.r)/d;
+ gDiff = (nextData.g - currentData.g)/d;
+ bDiff = (nextData.b - currentData.b)/d;
+ aDiff = (nextData.a - currentData.a)/d;
+ } else {
+ rDiff = gDiff = bDiff = aDiff = 0;
+ }
+ }
+ }
+ break;
+ case ColorBand.IPO_BSPLINE:
+ case ColorBand.IPO_CARDINAL:
+ Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
+ for(int i=0;i<colorBand.data.length;++i) {
+ cbDataMap.put(Integer.valueOf(i), colorBand.data[i]);
+ }
+
+ if(colorBand.data[0].pos==0) {
+ cbDataMap.put(Integer.valueOf(-1), colorBand.data[0]);
+ } else {
+ ColorBandData cbData = colorBand.data[0].clone();
+ cbData.pos = 0;
+ cbDataMap.put(Integer.valueOf(-1), cbData);
+ cbDataMap.put(Integer.valueOf(-2), cbData);
+ }
+
+ if(colorBand.data[colorBand.data.length - 1].pos==1000) {
+ cbDataMap.put(Integer.valueOf(colorBand.data.length), colorBand.data[colorBand.data.length - 1]);
+ } else {
+ ColorBandData cbData = colorBand.data[colorBand.data.length - 1].clone();
+ cbData.pos = 1000;
+ cbDataMap.put(Integer.valueOf(colorBand.data.length), cbData);
+ cbDataMap.put(Integer.valueOf(colorBand.data.length + 1), cbData);
+ }
+
+ float[] ipoFactors = new float[4];
+ float f;
+
+ ColorBandData data0 = cbDataMap.get(currentCursor - 2);
+ ColorBandData data1 = cbDataMap.get(currentCursor - 1);
+ ColorBandData data2 = cbDataMap.get(currentCursor);
+ ColorBandData data3 = cbDataMap.get(currentCursor + 1);
+
+ for(int i=0;i<result.length;++i) {
+ if (data2.pos != data1.pos) {
+ f = (i - data2.pos) / (float)(data1.pos - data2.pos);
+ } else {
+ f = 0.0f;
+ }
+
+ f = FastMath.clamp(f, 0.0f, 1.0f);
+
+ this.getIpoData(colorBand, f, ipoFactors);
+ result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
+ result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
+ result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
+ result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
+ result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
+ result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
+ result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
+ result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
+
+ if(nextData.pos==i) {
+ ++currentCursor;
+ data0 = cbDataMap.get(currentCursor - 2);
+ data1 = cbDataMap.get(currentCursor - 1);
+ data2 = cbDataMap.get(currentCursor);
+ data3 = cbDataMap.get(currentCursor + 1);
+ }
+ }
+ break;
+ case ColorBand.IPO_EASE:
+ float d, a, b, d2;
+ for(int i=0;i<result.length;++i) {
+ if(nextData.pos != currentData.pos) {
+ d = (i - currentData.pos) / (float)(nextData.pos - currentData.pos);
+ d2 = d * d;
+ a = 3.0f * d2 - 2.0f * d * d2;
+ b = 1.0f - a;
+ } else {
+ d = a = 0.0f;
+ b = 1.0f;
+ }
+
+ result[i][0] = b * currentData.r + a * nextData.r;
+ result[i][1] = b * currentData.g + a * nextData.g;
+ result[i][2] = b * currentData.b + a * nextData.b;
+ result[i][3] = b * currentData.a + a * nextData.a;
+ if(nextData.pos==i) {
+ currentData = dataArray[currentCursor++];
+ if(currentCursor < dataArray.length) {
+ nextData = dataArray[currentCursor];
+ }
+ }
+ }
+ break;
+ case ColorBand.IPO_CONSTANT:
+ for(int i=0;i<result.length;++i) {
+ result[i][0] = currentData.r;
+ result[i][1] = currentData.g;
+ result[i][2] = currentData.b;
+ result[i][3] = currentData.a;
+ if(nextData.pos==i) {
+ currentData = dataArray[currentCursor++];
+ if(currentCursor < dataArray.length) {
+ nextData = dataArray[currentCursor];
+ }
+ }
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown interpolation type: " + colorBand.ipoType);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the data for either B-spline of Cardinal interpolation.
+ * @param colorBand the color band
+ * @param d distance factor for the current intensity
+ * @param ipoFactors table to store the results (size of the table must be at least 4)
+ */
+ private void getIpoData(ColorBand colorBand, float d, float[] ipoFactors) {
+ float d2 = d * d;
+ float d3 = d2 * d;
+ if(colorBand.ipoType==ColorBand.IPO_BSPLINE) {
+ ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
+ ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
+ ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
+ ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
+ } else if(colorBand.ipoType==ColorBand.IPO_CARDINAL) {
+ ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
+ ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
+ ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
+ ipoFactors[3] = 0.16666666f * d3;
+ } else {
+ throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
+ }
+ }
+
+ /**
+ * This method applies brightness and contrast for RGB textures.
+ * @param tex texture structure
+ * @param texres
+ */
+ protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) {
+ texres.red = (texres.red - 0.5f) * bacd.contrast + bacd.brightness;
+ if (texres.red < 0.0f) {
+ texres.red = 0.0f;
+ }
+ texres.green =(texres.green - 0.5f) * bacd.contrast + bacd.brightness;
+ if (texres.green < 0.0f) {
+ texres.green = 0.0f;
+ }
+ texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness;
+ if (texres.blue < 0.0f) {
+ texres.blue = 0.0f;
+ }
+ }
+
+ /**
+ * This method applies brightness and contrast for Luminance textures.
+ * @param texres
+ * @param contrast
+ * @param brightness
+ */
+ protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) {
+ texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
+ if (texres.intensity < 0.0f) {
+ texres.intensity = 0.0f;
+ } else if (texres.intensity > 1.0f) {
+ texres.intensity = 1.0f;
+ }
+ }
+
+ /**
+ * A class constaining the colorband data.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ protected static class ColorBand {
+ //interpolation types
+ public static final int IPO_LINEAR = 0;
+ public static final int IPO_EASE = 1;
+ public static final int IPO_BSPLINE = 2;
+ public static final int IPO_CARDINAL = 3;
+ public static final int IPO_CONSTANT = 4;
+
+ public int cursorsAmount, ipoType;
+ public ColorBandData[] data;
+
+ /**
+ * Constructor. Loads the data from the given structure.
+ *
+ * @param cbdataStructure
+ * the colorband structure
+ */
+ @SuppressWarnings("unchecked")
+ public ColorBand(Structure colorbandStructure) {
+ this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
+ this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
+ this.data = new ColorBandData[this.cursorsAmount];
+ DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
+ for (int i = 0; i < this.cursorsAmount; ++i) {
+ this.data[i] = new ColorBandData(data.get(i));
+ }
+ }
+ }
+
+ /**
+ * Class to store the single colorband cursor data.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ protected static class ColorBandData implements Cloneable {
+ public final float r, g, b, a;
+ public int pos;
+
+ /**
+ * Copy constructor.
+ */
+ private ColorBandData(ColorBandData data) {
+ this.r = data.r;
+ this.g = data.g;
+ this.b = data.b;
+ this.a = data.a;
+ this.pos = data.pos;
+ }
+
+ /**
+ * Constructor. Loads the data from the given structure.
+ *
+ * @param cbdataStructure
+ * the structure containing the CBData object
+ */
+ public ColorBandData(Structure cbdataStructure) {
+ this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
+ this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
+ this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
+ this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
+ this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
+ }
+
+ @Override
+ public ColorBandData clone() {
+ try {
+ return (ColorBandData) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return new ColorBandData(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "P: " + this.pos + " [" + this.r+", "+this.g+", "+this.b+", "+this.a+"]";
+ }
+ }
+
+ /**
+ * This class contains brightness and contrast data.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ protected static class BrightnessAndContrastData {
+ public final float contrast;
+ public final float brightness;
+ public final float rFactor;
+ public final float gFactor;
+ public final float bFactor;
+
+ /**
+ * Constructor reads the required data from the given structure.
+ * @param tex texture structure
+ */
+ public BrightnessAndContrastData(Structure tex) {
+ contrast = ((Number) tex.getFieldValue("contrast")).floatValue();
+ brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f;
+ rFactor = ((Number) tex.getFieldValue("rfac")).floatValue();
+ gFactor = ((Number) tex.getFieldValue("gfac")).floatValue();
+ bFactor = ((Number) tex.getFieldValue("bfac")).floatValue();
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java
new file mode 100644
index 0000000..18ef409
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'blend' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public final class TextureGeneratorBlend extends TextureGenerator {
+
+ private static final IntensityFunction INTENSITY_FUNCTION[] = new IntensityFunction[7];
+ static {
+ INTENSITY_FUNCTION[0] = new IntensityFunction() {//Linear: stype = 0 (TEX_LIN)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ return (1.0f + x) * 0.5f;
+ }
+ };
+ INTENSITY_FUNCTION[1] = new IntensityFunction() {//Quad: stype = 1 (TEX_QUAD)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = (1.0f + x) * 0.5f;
+ return result * result;
+ }
+ };
+ INTENSITY_FUNCTION[2] = new IntensityFunction() {//Ease: stype = 2 (TEX_EASE)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = (1.0f + x) * 0.5f;
+ if (result <= 0.0f) {
+ return 0.0f;
+ } else if (result >= 1.0f) {
+ return 1.0f;
+ } else {
+ return result * result *(3.0f - 2.0f * result);
+ }
+ }
+ };
+ INTENSITY_FUNCTION[3] = new IntensityFunction() {//Diagonal: stype = 3 (TEX_DIAG)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ return (2.0f + x + y) * 0.25f;
+ }
+ };
+ INTENSITY_FUNCTION[4] = new IntensityFunction() {//Sphere: stype = 4 (TEX_SPHERE)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
+ return result < 0.0f ? 0.0f : result;
+ }
+ };
+ INTENSITY_FUNCTION[5] = new IntensityFunction() {//Halo: stype = 5 (TEX_HALO)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
+ return result <= 0.0f ? 0.0f : result * result;
+ }
+ };
+ INTENSITY_FUNCTION[6] = new IntensityFunction() {//Radial: stype = 6 (TEX_RAD)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ return (float) Math.atan2(y, x) * FastMath.INV_TWO_PI + 0.5f;
+ }
+ };
+ }
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorBlend(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ int flag = ((Number) tex.getFieldValue("flag")).intValue();
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, x, y;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ boolean flipped = (flag & NoiseGenerator.TEX_FLIPBLEND) != 0;
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ x = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ if (flipped) {
+ y = x;
+ x = hDelta * j;
+ } else {
+ y = hDelta * j;
+ }
+ for (int k = -halfD; k < halfD; ++k) {
+ texres.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, dDelta * k);
+
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ private static interface IntensityFunction {
+ float getIntensity(float x, float y, float z);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java
new file mode 100644
index 0000000..9ac24e5
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'clouds' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorClouds extends TextureGenerator {
+ // tex->noisetype
+ protected static final int TEX_NOISESOFT = 0;
+ protected static final int TEX_NOISEPERL = 1;
+
+ // tex->stype
+ protected static final int TEX_DEFAULT = 0;
+ protected static final int TEX_COLOR = 1;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorClouds(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+
+ // reading the data from the texture structure
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ int noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ int noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ int noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
+ boolean isHard = noiseType != TEX_NOISESOFT;
+ int sType = ((Number) tex.getFieldValue("stype")).intValue();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = sType == TEX_COLOR || colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = sType == TEX_COLOR || colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = NoiseGenerator.NoiseFunctions.turbulence(texvec[0], texvec[1], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
+ texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else if (sType == TEX_COLOR) {
+ texres.red = texres.intensity;
+ texres.green = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[0], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
+ texres.blue = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[2], texvec[0], noisesize, noiseDepth, noiseBasis, isHard);
+
+ texres.green = FastMath.clamp(texres.green, 0.0f, 1.0f);
+ texres.blue = FastMath.clamp(texres.blue, 0.0f, 1.0f);
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (255);//1.0f * 255.0f
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java
new file mode 100644
index 0000000..f66beb6
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseFunction;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'distorted noise' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorDistnoise extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorDistnoise(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ float distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue();
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ int noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();
+
+ TexturePixel texres = new TexturePixel();
+ float[] texvec = new float[] { 0, 0, 0 };
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i / noisesize;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j / noisesize;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = this.musgraveVariableLunacrityNoise(texvec[0], texvec[1], texvec[2], distAmount, noisebasis, noisebasis2);
+ texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ /**
+ * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise
+ * texture.
+ * @param x
+ * @param y
+ * @param z
+ * @param distortion
+ * @param nbas1
+ * @param nbas2
+ * @return
+ */
+ private float musgraveVariableLunacrityNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) {
+ NoiseFunction abstractNoiseFunc1 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(nbas1));
+ if (abstractNoiseFunc1 == null) {
+ abstractNoiseFunc1 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(0));
+ }
+ NoiseFunction abstractNoiseFunc2 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(nbas2));
+ if (abstractNoiseFunc2 == null) {
+ abstractNoiseFunc2 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(0));
+ }
+ // get a random vector and scale the randomization
+ float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion;
+ float ry = abstractNoiseFunc1.execute(x, y, z) * distortion;
+ float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion;
+ return abstractNoiseFunc2.executeSigned(x + rx, y + ry, z + rz); //distorted-domain noise
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java
new file mode 100644
index 0000000..e8a1637
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'magic' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorMagic extends TextureGenerator {
+ private static NoiseDepthFunction[] noiseDepthFunctions = new NoiseDepthFunction[10];
+ static {
+ noiseDepthFunctions[0] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.cos(xyz[0] - xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[1] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = (float) Math.cos(xyz[0] - xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[2] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[2] = (float) Math.sin(-xyz[0] - xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[3] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = -(float) Math.cos(-xyz[0] + xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[4] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.sin(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[5] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.cos(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[6] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = (float) Math.cos(xyz[0] + xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[7] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[2] = (float) Math.sin(xyz[0] + xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[8] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = -(float) Math.cos(-xyz[0] - xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[9] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.sin(xyz[0] - xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ }
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorMagic(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float xyz[] = new float[3], turb;
+ int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ float turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f;
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * 4];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ turb = turbul;
+ texvec[2] = dDelta * k;
+ xyz[0] = (float) Math.sin((texvec[0] + texvec[1] + texvec[2]) * 5.0f);
+ xyz[1] = (float) Math.cos((-texvec[0] + texvec[1] - texvec[2]) * 5.0f);
+ xyz[2] = -(float) Math.cos((-texvec[0] - texvec[1] + texvec[2]) * 5.0f);
+
+ if (colorBand != null) {
+ texres.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+ texres.alpha = colorBand[colorbandIndex][3];
+ } else {
+ if (noisedepth > 0) {
+ xyz[0] *= turb;
+ xyz[1] *= turb;
+ xyz[2] *= turb;
+ for (int m=0;m<noisedepth;++m) {
+ noiseDepthFunctions[m].compute(xyz, turb);
+ }
+ }
+
+ if (turb != 0.0f) {
+ turb *= 2.0f;
+ xyz[0] /= turb;
+ xyz[1] /= turb;
+ xyz[2] /= turb;
+ }
+ texres.red = 0.5f - xyz[0];
+ texres.green = 0.5f - xyz[1];
+ texres.blue = 0.5f - xyz[2];
+ texres.alpha = 1.0f;
+ }
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (texres.alpha * 255.0f);
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+ }
+
+ private static interface NoiseDepthFunction {
+ void compute(float[] xyz, float turbulence);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java
new file mode 100644
index 0000000..06c08a9
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'marble' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorMarble extends TextureGeneratorWood {
+ // tex->stype
+ protected static final int TEX_SOFT = 0;
+ protected static final int TEX_SHARP = 1;
+ protected static final int TEX_SHARPER = 2;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorMarble(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+ MarbleData marbleData = new MarbleData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = this.marbleInt(marbleData, texvec[0], texvec[1], texvec[2]);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ public float marbleInt(MarbleData marbleData, float x, float y, float z) {
+ int waveform;
+ if (marbleData.waveform > TEX_TRI || marbleData.waveform < TEX_SIN) {
+ waveform = 0;
+ } else {
+ waveform = marbleData.waveform;
+ }
+
+ float n = 5.0f * (x + y + z);
+ float mi = n + marbleData.turbul * NoiseGenerator.NoiseFunctions.turbulence(x, y, z, marbleData.noisesize, marbleData.noisedepth, marbleData.noisebasis, marbleData.isHard);
+
+ if (marbleData.stype >= TEX_SOFT) {
+ mi = waveformFunctions[waveform].execute(mi);
+ if (marbleData.stype == TEX_SHARP) {
+ mi = (float) Math.sqrt(mi);
+ } else if (marbleData.stype == TEX_SHARPER) {
+ mi = (float) Math.sqrt(Math.sqrt(mi));
+ }
+ }
+ return mi;
+ }
+
+ private static class MarbleData {
+ public final float noisesize;
+ public final int noisebasis;
+ public final int noisedepth;
+ public final int stype;
+ public final float turbul;
+ public final int waveform;
+ public final boolean isHard;
+
+ public MarbleData(Structure tex) {
+ noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ stype = ((Number) tex.getFieldValue("stype")).intValue();
+ turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+ int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
+ waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue();
+ isHard = noisetype != TEX_NOISESOFT;
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java
new file mode 100644
index 0000000..667063f
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.NoiseGenerator.MusgraveFunction;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'musgrave' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorMusgrave extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorMusgrave(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ TexturePixel texres = new TexturePixel();
+ float[] texvec = new float[] { 0, 0, 0 };
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ MusgraveData musgraveData = new MusgraveData(tex);
+ MusgraveFunction musgraveFunction;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i / noisesize;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j / noisesize;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k / noisesize;
+ musgraveFunction = NoiseGenerator.musgraveFunctions.get(Integer.valueOf(musgraveData.stype));
+ if(musgraveFunction==null) {
+ throw new IllegalStateException("Unknown type of musgrave texture: " + stype);
+ }
+ texres.intensity = musgraveData.outscale * musgraveFunction.execute(musgraveData, texvec[0], texvec[1], texvec[2]);
+ if(texres.intensity>1) {
+ texres.intensity = 1.0f;
+ } else if(texres.intensity < 0) {
+ texres.intensity = 0.0f;
+ }
+
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ protected static class MusgraveData {
+ public final int stype;
+ public final float outscale;
+ public final float h;
+ public final float lacunarity;
+ public final float octaves;
+ public final int noisebasis;
+ public final float offset;
+ public final float gain;
+
+ public MusgraveData(Structure tex) {
+ stype = ((Number) tex.getFieldValue("stype")).intValue();
+ outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
+ h = ((Number) tex.getFieldValue("mg_H")).floatValue();
+ lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();
+ octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();
+ noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ offset = ((Number) tex.getFieldValue("mg_offset")).floatValue();
+ gain = ((Number) tex.getFieldValue("mg_gain")).floatValue();
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java
new file mode 100644
index 0000000..02d4687
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'noise' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorNoise extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorNoise(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ int val, random, loop;
+ int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ for (int j = -halfH; j < halfH; ++j) {
+ for (int k = -halfD; k < halfD; ++k) {
+ random = FastMath.rand.nextInt();
+ val = random & 3;
+
+ loop = noisedepth;
+ while (loop-- != 0) {
+ random >>= 2;
+ val *= random & 3;
+ }
+ texres.intensity = FastMath.clamp(val, 0.0f, 1.0f);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java
new file mode 100644
index 0000000..76a6614
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'stucci' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorStucci extends TextureGenerator {
+ protected static final int TEX_NOISESOFT = 0;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorStucci(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
+ float turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+ boolean isHard = noisetype != TEX_NOISESOFT;
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();
+
+ if(noisesize<=0.001f) {//the texture goes black if this value is lower than 0.001f
+ noisesize = 0.001f;
+ }
+
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, noiseValue, ofs;;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ noiseValue = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2], noisesize, 0, noisebasis, isHard);
+ ofs = turbul / 200.0f;
+ if (stype != 0) {
+ ofs *= noiseValue * noiseValue;
+ }
+
+ texres.intensity = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2] + ofs, noisesize, 0, noisebasis, isHard);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+ texres.alpha = colorBand[colorbandIndex][3];
+ }
+
+ if (stype == NoiseGenerator.TEX_WALLOUT) {
+ texres.intensity = 1.0f - texres.intensity;
+ }
+ if (texres.intensity < 0.0f) {
+ texres.intensity = 0.0f;
+ }
+ //no brightness and contrast needed for stucci (it doesn't affect the texture)
+ if (colorBand != null) {
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (texres.alpha * 255.0f);
+ } else {
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java
new file mode 100644
index 0000000..1dee158
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'voronoi' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorVoronoi extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorVoronoi(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float voronoiWeight1 = ((Number) tex.getFieldValue("vn_w1")).floatValue();
+ float voronoiWeight2 = ((Number) tex.getFieldValue("vn_w2")).floatValue();
+ float voronoiWeight3 = ((Number) tex.getFieldValue("vn_w3")).floatValue();
+ float voronoiWeight4 = ((Number) tex.getFieldValue("vn_w4")).floatValue();
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ float outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
+ float mexp = ((Number) tex.getFieldValue("vn_mexp")).floatValue();
+ int distm = ((Number) tex.getFieldValue("vn_distm")).intValue();
+ int voronoiColorType = ((Number) tex.getFieldValue("vn_coltype")).intValue();
+
+ TexturePixel texres = new TexturePixel();
+ float[] texvec = new float[] { 0, 0, 0 };
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = voronoiColorType != 0 || colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = voronoiColorType != 0 || colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ float[] da = new float[4], pa = new float[12];
+ float[] hashPoint = voronoiColorType != 0 ? new float[3] : null;
+ float[] voronoiWeights = new float[] {FastMath.abs(voronoiWeight1), FastMath.abs(voronoiWeight2),
+ FastMath.abs(voronoiWeight3), FastMath.abs(voronoiWeight4)};
+ float weight;
+ float sc = voronoiWeights[0] + voronoiWeights[1] + voronoiWeights[2] + voronoiWeights[3];
+ if (sc != 0.0f) {
+ sc = outscale / sc;
+ }
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i / noisesize;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j / noisesize;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ NoiseGenerator.NoiseFunctions.voronoi(texvec[0], texvec[1], texvec[2], da, pa, mexp, distm);
+ texres.intensity = sc * FastMath.abs(voronoiWeight1 * da[0] + voronoiWeight2 * da[1] + voronoiWeight3 * da[2] + voronoiWeight4 * da[3]);
+ if(texres.intensity>1.0f) {
+ texres.intensity = 1.0f;
+ } else if(texres.intensity<0.0f) {
+ texres.intensity = 0.0f;
+ }
+
+ if (colorBand != null) {//colorband ALWAYS goes first and covers the color (if set)
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+ texres.alpha = colorBand[colorbandIndex][3];
+ } else if (voronoiColorType != 0) {
+ texres.red = texres.green = texres.blue = 0.0f;
+ texres.alpha = 1.0f;
+ for(int m=0; m<12; m+=3) {
+ weight = voronoiWeights[m/3];
+ this.cellNoiseV(pa[m], pa[m + 1], pa[m + 2], hashPoint);
+ texres.red += weight * hashPoint[0];
+ texres.green += weight * hashPoint[1];
+ texres.blue += weight * hashPoint[2];
+ }
+ if (voronoiColorType >= 2) {
+ float t1 = (da[1] - da[0]) * 10.0f;
+ if (t1 > 1.0f) {
+ t1 = 1.0f;
+ }
+ if (voronoiColorType == 3) {
+ t1 *= texres.intensity;
+ } else {
+ t1 *= sc;
+ }
+ texres.red *= t1;
+ texres.green *= t1;
+ texres.blue *= t1;
+ } else {
+ texres.red *= sc;
+ texres.green *= sc;
+ texres.blue *= sc;
+ }
+ }
+
+ if (voronoiColorType != 0 || colorBand != null) {
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (texres.alpha * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ /**
+ * Returns a vector/point/color in ca, using point hasharray directly
+ */
+ private void cellNoiseV(float x, float y, float z, float[] hashPoint) {
+ int xi = (int) Math.floor(x);
+ int yi = (int) Math.floor(y);
+ int zi = (int) Math.floor(z);
+ NoiseMath.hash(xi, yi, zi, hashPoint);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java
new file mode 100644
index 0000000..bf5e050
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java
@@ -0,0 +1,216 @@
+/*
+ *
+ * $Id: noise.c 14611 2008-04-29 08:24:33Z campbellbarton $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'wood' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorWood extends TextureGenerator {
+ // tex->noisebasis2
+ protected static final int TEX_SIN = 0;
+ protected static final int TEX_SAW = 1;
+ protected static final int TEX_TRI = 2;
+
+ // tex->stype
+ protected static final int TEX_BAND = 0;
+ protected static final int TEX_RING = 1;
+ protected static final int TEX_BANDNOISE = 2;
+ protected static final int TEX_RINGNOISE = 3;
+
+ // tex->noisetype
+ protected static final int TEX_NOISESOFT = 0;
+ protected static final int TEX_NOISEPERL = 1;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator the noise generator
+ */
+ public TextureGeneratorWood(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1;
+ int halfH = height >> 1;
+ int halfD = depth >> 1;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ WoodIntensityData woodIntensityData = new WoodIntensityData(tex);
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ int index = 0;
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for(int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = this.woodIntensity(woodIntensityData, texvec[0], texvec[1], texvec[2]);
+
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ protected static WaveForm[] waveformFunctions = new WaveForm[3];
+ static {
+ waveformFunctions[0] = new WaveForm() {// sinus (TEX_SIN)
+
+ @Override
+ public float execute(float x) {
+ return 0.5f + 0.5f * (float) Math.sin(x);
+ }
+ };
+ waveformFunctions[1] = new WaveForm() {// saw (TEX_SAW)
+
+ @Override
+ public float execute(float x) {
+ int n = (int) (x * FastMath.INV_TWO_PI);
+ x -= n * FastMath.TWO_PI;
+ if (x < 0.0f) {
+ x += FastMath.TWO_PI;
+ }
+ return x * FastMath.INV_TWO_PI;
+ }
+ };
+ waveformFunctions[2] = new WaveForm() {// triangle (TEX_TRI)
+
+ @Override
+ public float execute(float x) {
+ return 1.0f - 2.0f * FastMath.abs((float) Math.floor(x * FastMath.INV_TWO_PI + 0.5f) - x * FastMath.INV_TWO_PI);
+ }
+ };
+ }
+
+ /**
+ * Computes basic wood intensity value at x,y,z.
+ * @param woodIntData
+ * @param x X coordinate of the texture pixel
+ * @param y Y coordinate of the texture pixel
+ * @param z Z coordinate of the texture pixel
+ * @return wood intensity at position [x, y, z]
+ */
+ public float woodIntensity(WoodIntensityData woodIntData, float x, float y, float z) {
+ float result;
+
+ switch(woodIntData.woodType) {
+ case TEX_BAND:
+ result = woodIntData.waveformFunction.execute((x + y + z) * 10.0f);
+ break;
+ case TEX_RING:
+ result = woodIntData.waveformFunction.execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f);
+ break;
+ case TEX_BANDNOISE:
+ result = woodIntData.turbul * NoiseGenerator.NoiseFunctions.noise(x, y, z, woodIntData.noisesize, 0, woodIntData.noisebasis, woodIntData.isHard);
+ result = woodIntData.waveformFunction.execute((x + y + z) * 10.0f + result);
+ break;
+ case TEX_RINGNOISE:
+ result = woodIntData.turbul * NoiseGenerator.NoiseFunctions.noise(x, y, z, woodIntData.noisesize, 0, woodIntData.noisebasis, woodIntData.isHard);
+ result = woodIntData.waveformFunction.execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f + result);
+ break;
+ default:
+ result = 0;
+ }
+ return result;
+ }
+
+ /**
+ * A class that collects the data for wood intensity calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ private static class WoodIntensityData {
+ public final WaveForm waveformFunction;
+ public final int noisebasis;
+ public final float noisesize;
+ public final float turbul;
+ public final int noiseType;
+ public final int woodType;
+ public final boolean isHard;
+
+ public WoodIntensityData(Structure tex) {
+ int waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue();//wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2
+ if (waveform > TEX_TRI || waveform < TEX_SIN) {
+ waveform = 0; // check to be sure noisebasis2 is initialized ahead of time
+ }
+ waveformFunction = waveformFunctions[waveform];
+ noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ woodType = ((Number) tex.getFieldValue("stype")).intValue();
+ noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+ noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
+ isHard = noiseType != TEX_NOISESOFT;
+ }
+ }
+
+ protected static interface WaveForm {
+
+ float execute(float x);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
new file mode 100644
index 0000000..0e0861d
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorConvertOp;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jme3tools.converters.ImageToAwt;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.BlenderKey;
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.asset.GeneratedTextureKey;
+import com.jme3.asset.TextureKey;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.materials.MaterialContext;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+
+/**
+ * A class that is used in texture calculations.
+ *
+ * @author Marcin Roguski
+ */
+public class TextureHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName());
+
+ // texture types
+ public static final int TEX_NONE = 0;
+ public static final int TEX_CLOUDS = 1;
+ public static final int TEX_WOOD = 2;
+ public static final int TEX_MARBLE = 3;
+ public static final int TEX_MAGIC = 4;
+ public static final int TEX_BLEND = 5;
+ public static final int TEX_STUCCI = 6;
+ public static final int TEX_NOISE = 7;
+ public static final int TEX_IMAGE = 8;
+ public static final int TEX_PLUGIN = 9;
+ public static final int TEX_ENVMAP = 10;
+ public static final int TEX_MUSGRAVE = 11;
+ public static final int TEX_VORONOI = 12;
+ public static final int TEX_DISTNOISE = 13;
+ public static final int TEX_POINTDENSITY = 14;//v. 25+
+ public static final int TEX_VOXELDATA = 15;//v. 25+
+
+ // mapto
+ public static final int MAP_COL = 1;
+ public static final int MAP_NORM = 2;
+ public static final int MAP_COLSPEC = 4;
+ public static final int MAP_COLMIR = 8;
+ public static final int MAP_VARS = 0xFFF0;
+ public static final int MAP_REF = 16;
+ public static final int MAP_SPEC = 32;
+ public static final int MAP_EMIT = 64;
+ public static final int MAP_ALPHA = 128;
+ public static final int MAP_HAR = 256;
+ public static final int MAP_RAYMIRR = 512;
+ public static final int MAP_TRANSLU = 1024;
+ public static final int MAP_AMB = 2048;
+ public static final int MAP_DISPLACE = 4096;
+ public static final int MAP_WARP = 8192;
+ public static final int MAP_LAYER = 16384;
+
+ protected NoiseGenerator noiseGenerator;
+ private Map<Integer, TextureGenerator> textureGenerators = new HashMap<Integer, TextureGenerator>();
+
+ /**
+ * This constructor parses the given blender version and stores the result.
+ * It creates noise generator and texture generators.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public TextureHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, false);
+ noiseGenerator = new NoiseGenerator(blenderVersion);
+ textureGenerators.put(Integer.valueOf(TEX_BLEND), new TextureGeneratorBlend(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_CLOUDS), new TextureGeneratorClouds(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_DISTNOISE), new TextureGeneratorDistnoise(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_MAGIC), new TextureGeneratorMagic(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_MARBLE), new TextureGeneratorMarble(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_MUSGRAVE), new TextureGeneratorMusgrave(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_NOISE), new TextureGeneratorNoise(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_STUCCI), new TextureGeneratorStucci(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_VORONOI), new TextureGeneratorVoronoi(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_WOOD), new TextureGeneratorWood(noiseGenerator));
+ }
+
+ /**
+ * This class returns a texture read from the file or from packed blender data. The returned texture has the name set to the value of
+ * its blender type.
+ *
+ * @param tex
+ * texture structure filled with data
+ * @param blenderContext
+ * the blender context
+ * @return the texture that can be used by JME engine
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public Texture getTexture(Structure tex, BlenderContext blenderContext) throws BlenderFileException {
+ Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (result != null) {
+ return result;
+ }
+ int type = ((Number) tex.getFieldValue("type")).intValue();
+ int width = blenderContext.getBlenderKey().getGeneratedTextureWidth();
+ int height = blenderContext.getBlenderKey().getGeneratedTextureHeight();
+ int depth = blenderContext.getBlenderKey().getGeneratedTextureDepth();
+
+ switch (type) {
+ case TEX_IMAGE:// (it is first because probably this will be most commonly used)
+ Pointer pImage = (Pointer) tex.getFieldValue("ima");
+ if (pImage.isNotNull()){
+ Structure image = pImage.fetchData(blenderContext.getInputStream()).get(0);
+ result = this.getTextureFromImage(image, blenderContext);
+ }
+ break;
+ case TEX_CLOUDS:
+ case TEX_WOOD:
+ case TEX_MARBLE:
+ case TEX_MAGIC:
+ case TEX_BLEND:
+ case TEX_STUCCI:
+ case TEX_NOISE:
+ case TEX_MUSGRAVE:
+ case TEX_VORONOI:
+ case TEX_DISTNOISE:
+ TextureGenerator textureGenerator = textureGenerators.get(Integer.valueOf(type));
+ result = textureGenerator.generate(tex, width, height, depth, blenderContext);
+ break;
+ case TEX_NONE:// No texture, do nothing
+ break;
+ case TEX_POINTDENSITY:
+ LOGGER.warning("Point density texture loading currently not supported!");
+ break;
+ case TEX_VOXELDATA:
+ LOGGER.warning("Voxel data texture loading currently not supported!");
+ break;
+ case TEX_PLUGIN:
+ case TEX_ENVMAP:// TODO: implement envmap texture
+ LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[]{type, tex.getName()});
+ break;
+ default:
+ throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());
+ }
+ if (result != null) {
+ result.setName(tex.getName());
+ result.setWrap(WrapMode.Repeat);
+ // NOTE: Enable mipmaps FOR ALL TEXTURES EVER
+ result.setMinFilter(MinFilter.Trilinear);
+ if(type != TEX_IMAGE) {//only generated textures should have this key
+ result.setKey(new GeneratedTextureKey(tex.getName()));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method merges the given textures. The result texture has no alpha
+ * factor (is always opaque).
+ *
+ * @param sources
+ * the textures to be merged
+ * @param materialContext
+ * the context of the material
+ * @return merged textures
+ */
+ public Texture mergeTextures(List<Texture> sources, MaterialContext materialContext) {
+ Texture result = null;
+ if(sources!=null && sources.size()>0) {
+ if(sources.size() == 1) {
+ return sources.get(0);//just return the texture
+ }
+ //checking the sizes of the textures (tehy should perfectly match)
+ int lastTextureWithoutAlphaIndex = 0;
+ int width = sources.get(0).getImage().getWidth();
+ int height = sources.get(0).getImage().getHeight();
+ int depth = sources.get(0).getImage().getDepth();
+
+ for(Texture source : sources) {
+ if(source.getImage().getWidth() != width) {
+ throw new IllegalArgumentException("The texture " + source.getName() + " has invalid width! It should be: " + width + '!');
+ }
+ if(source.getImage().getHeight() != height) {
+ throw new IllegalArgumentException("The texture " + source.getName() + " has invalid height! It should be: " + height + '!');
+ }
+ if(source.getImage().getDepth() != depth) {
+ throw new IllegalArgumentException("The texture " + source.getName() + " has invalid depth! It should be: " + depth + '!');
+ }
+ //support for more formats is not necessary at the moment
+ if(source.getImage().getFormat()!=Format.RGB8 && source.getImage().getFormat()!=Format.BGR8) {
+ ++lastTextureWithoutAlphaIndex;
+ }
+ }
+ if(depth==0) {
+ depth = 1;
+ }
+
+ //remove textures before the one without alpha (they will be covered anyway)
+ if(lastTextureWithoutAlphaIndex > 0 && lastTextureWithoutAlphaIndex<sources.size()-1) {
+ sources = sources.subList(lastTextureWithoutAlphaIndex, sources.size()-1);
+ }
+ int pixelsAmount = width * height * depth;
+
+ ByteBuffer data = BufferUtils.createByteBuffer(pixelsAmount * 3);
+ TexturePixel resultPixel = new TexturePixel();
+ TexturePixel sourcePixel = new TexturePixel();
+ ColorRGBA diffuseColor = materialContext.getDiffuseColor();
+ for (int i = 0; i < pixelsAmount; ++i) {
+ for (int j = 0; j < sources.size(); ++j) {
+ Image image = sources.get(j).getImage();
+ ByteBuffer sourceData = image.getData(0);
+ if(j==0) {
+ resultPixel.fromColor(diffuseColor);
+ sourcePixel.fromImage(image.getFormat(), sourceData, i);
+ resultPixel.merge(sourcePixel);
+ } else {
+ sourcePixel.fromImage(image.getFormat(), sourceData, i);
+ resultPixel.merge(sourcePixel);
+ }
+ }
+ data.put((byte)(255 * resultPixel.red));
+ data.put((byte)(255 * resultPixel.green));
+ data.put((byte)(255 * resultPixel.blue));
+ resultPixel.clear();
+ }
+
+ if(depth==1) {
+ result = new Texture2D(new Image(Format.RGB8, width, height, data));
+ } else {
+ ArrayList<ByteBuffer> arrayData = new ArrayList<ByteBuffer>(1);
+ arrayData.add(data);
+ result = new Texture3D(new Image(Format.RGB8, width, height, depth, arrayData));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method converts the given texture into normal-map texture.
+ * @param source
+ * the source texture
+ * @param strengthFactor
+ * the normal strength factor
+ * @return normal-map texture
+ */
+ public Texture convertToNormalMapTexture(Texture source, float strengthFactor) {
+ Image image = source.getImage();
+ BufferedImage sourceImage = ImageToAwt.convert(image, false, false, 0);
+ BufferedImage heightMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ BufferedImage bumpMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ ColorConvertOp gscale = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
+ gscale.filter(sourceImage, heightMap);
+
+ Vector3f S = new Vector3f();
+ Vector3f T = new Vector3f();
+ Vector3f N = new Vector3f();
+
+ for (int x = 0; x < bumpMap.getWidth(); ++x) {
+ for (int y = 0; y < bumpMap.getHeight(); ++y) {
+ // generating bump pixel
+ S.x = 1;
+ S.y = 0;
+ S.z = strengthFactor * this.getHeight(heightMap, x + 1, y) - strengthFactor * this.getHeight(heightMap, x - 1, y);
+ T.x = 0;
+ T.y = 1;
+ T.z = strengthFactor * this.getHeight(heightMap, x, y + 1) - strengthFactor * this.getHeight(heightMap, x, y - 1);
+
+ float den = (float) Math.sqrt(S.z * S.z + T.z * T.z + 1);
+ N.x = -S.z;
+ N.y = -T.z;
+ N.z = 1;
+ N.divideLocal(den);
+
+ // setting thge pixel in the result image
+ bumpMap.setRGB(x, y, this.vectorToColor(N.x, N.y, N.z));
+ }
+ }
+ ByteBuffer byteBuffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * 3);
+ ImageToAwt.convert(bumpMap, Format.RGB8, byteBuffer);
+ return new Texture2D(new Image(Format.RGB8, image.getWidth(), image.getHeight(), byteBuffer));
+ }
+
+ /**
+ * This method returns the height represented by the specified pixel in the given texture.
+ * The given texture should be a height-map.
+ * @param image
+ * the height-map texture
+ * @param x
+ * pixel's X coordinate
+ * @param y
+ * pixel's Y coordinate
+ * @return height reprezented by the given texture in the specified location
+ */
+ protected int getHeight(BufferedImage image, int x, int y) {
+ if (x < 0) {
+ x = 0;
+ } else if (x >= image.getWidth()) {
+ x = image.getWidth() - 1;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= image.getHeight()) {
+ y = image.getHeight() - 1;
+ }
+ return image.getRGB(x, y) & 0xff;
+ }
+
+ /**
+ * This method transforms given vector's coordinates into ARGB color (A is always = 255).
+ * @param x X factor of the vector
+ * @param y Y factor of the vector
+ * @param z Z factor of the vector
+ * @return color representation of the given vector
+ */
+ protected int vectorToColor(float x, float y, float z) {
+ int r = Math.round(255 * (x + 1f) / 2f);
+ int g = Math.round(255 * (y + 1f) / 2f);
+ int b = Math.round(255 * (z + 1f) / 2f);
+ return (255 << 24) + (r << 16) + (g << 8) + b;
+ }
+
+ /**
+ * This class returns a texture read from the file or from packed blender data.
+ *
+ * @param image
+ * image structure filled with data
+ * @param blenderContext
+ * the blender context
+ * @return the texture that can be used by JME engine
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public Texture getTextureFromImage(Structure image, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", image.getOldMemoryAddress());
+ Texture result = (Texture) blenderContext.getLoadedFeature(image.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (result == null) {
+ String texturePath = image.getFieldValue("name").toString();
+ Pointer pPackedFile = (Pointer) image.getFieldValue("packedfile");
+ if (pPackedFile.isNull()) {
+ LOGGER.log(Level.INFO, "Reading texture from file: {0}", texturePath);
+ result = this.loadTextureFromFile(texturePath, blenderContext);
+ } else {
+ LOGGER.info("Packed texture. Reading directly from the blend file!");
+ Structure packedFile = pPackedFile.fetchData(blenderContext.getInputStream()).get(0);
+ Pointer pData = (Pointer) packedFile.getFieldValue("data");
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress());
+ blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition());
+ ImageLoader imageLoader = new ImageLoader();
+
+ // Should the texture be flipped? It works for sinbad ..
+ Image im = imageLoader.loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true);
+ if (im != null) {
+ result = new Texture2D(im);
+ }
+ }
+ if (result != null) {
+ result.setName(texturePath);
+ result.setWrap(Texture.WrapMode.Repeat);
+ if(LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] {texturePath, image.getOldMemoryAddress()});
+ }
+ blenderContext.addLoadedFeatures(image.getOldMemoryAddress(), image.getName(), image, result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method loads the textre from outside the blend file.
+ *
+ * @param name
+ * the path to the image
+ * @param blenderContext
+ * the blender context
+ * @return the loaded image or null if the image cannot be found
+ */
+ protected Texture loadTextureFromFile(String name, BlenderContext blenderContext) {
+ if (!name.contains(".")){
+ return null; // no extension means not a valid image
+ }
+
+ AssetManager assetManager = blenderContext.getAssetManager();
+ name = name.replaceAll("\\\\", "\\/");
+ Texture result = null;
+
+ List<String> assetNames = new ArrayList<String>();
+ if (name.startsWith("//")) {
+ String relativePath = name.substring(2);
+ //augument the path with blender key path
+ BlenderKey blenderKey = blenderContext.getBlenderKey();
+ int idx = blenderKey.getName().lastIndexOf('/');
+ String blenderAssetFolder = blenderKey.getName().substring(0, idx != -1 ? idx : 0);
+ assetNames.add(blenderAssetFolder+'/'+relativePath);
+ } else {//use every path from the asset name to the root (absolute path)
+ String[] paths = name.split("\\/");
+ StringBuilder sb = new StringBuilder(paths[paths.length-1]);//the asset name
+ assetNames.add(paths[paths.length-1]);
+
+ for(int i=paths.length-2;i>=0;--i) {
+ sb.insert(0, '/');
+ sb.insert(0, paths[i]);
+ assetNames.add(0, sb.toString());
+ }
+ }
+
+ //now try to locate the asset
+ for(String assetName : assetNames) {
+ try {
+ TextureKey key = new TextureKey(assetName);
+ key.setGenerateMips(true);
+ key.setAsCube(false);
+ result = assetManager.loadTexture(key);
+ break;//if no exception is thrown then accept the located asset and break the loop
+ } catch(AssetNotFoundException e) {
+ LOGGER.fine(e.getLocalizedMessage());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0;
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
new file mode 100644
index 0000000..37c0122
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
@@ -0,0 +1,294 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image.Format;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The class that stores the pixel values of a texture.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TexturePixel implements Cloneable {
+ private static final Logger LOGGER = Logger.getLogger(TexturePixel.class.getName());
+
+ /** The pixel data. */
+ public float intensity, red, green, blue, alpha;
+
+ /**
+ * Copies the values from the given pixel.
+ *
+ * @param pixel
+ * the pixel that we read from
+ */
+ public void fromPixel(TexturePixel pixel) {
+ this.intensity = pixel.intensity;
+ this.red = pixel.red;
+ this.green = pixel.green;
+ this.blue = pixel.blue;
+ this.alpha = pixel.alpha;
+ }
+
+ /**
+ * Copies the values from the given color.
+ *
+ * @param colorRGBA
+ * the color that we read from
+ */
+ public void fromColor(ColorRGBA colorRGBA) {
+ this.red = colorRGBA.r;
+ this.green = colorRGBA.g;
+ this.blue = colorRGBA.b;
+ this.alpha = colorRGBA.a;
+ }
+
+ /**
+ * Copies the values from the given values.
+ *
+ * @param a
+ * the alpha value
+ * @param r
+ * the red value
+ * @param g
+ * the green value
+ * @param b
+ * the blue value
+ */
+ public void fromARGB8(float a, float r, float g, float b) {
+ this.alpha = a;
+ this.red = r;
+ this.green = g;
+ this.blue = b;
+ }
+
+ /**
+ * Copies the values from the given integer that stores the ARGB8 data.
+ *
+ * @param argb8
+ * the data stored in an integer
+ */
+ public void fromARGB8(int argb8) {
+ byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
+ this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = (byte) ((argb8 & 0xFF0000) >> 16);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = (byte) ((argb8 & 0xFF00) >> 8);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = (byte) (argb8 & 0xFF);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ }
+
+ /**
+ * Copies the data from the given image.
+ *
+ * @param imageFormat
+ * the image format
+ * @param data
+ * the image data
+ * @param pixelIndex
+ * the index of the required pixel
+ */
+ public void fromImage(Format imageFormat, ByteBuffer data, int pixelIndex) {
+ int firstByteIndex;
+ byte pixelValue;
+ switch (imageFormat) {
+ case ABGR8:
+ firstByteIndex = pixelIndex << 2;
+ pixelValue = data.get(firstByteIndex);
+ this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 3);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case RGBA8:
+ firstByteIndex = pixelIndex << 2;
+ pixelValue = data.get(firstByteIndex);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 3);
+ this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case BGR8:
+ firstByteIndex = pixelIndex * 3;
+ pixelValue = data.get(firstByteIndex);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ this.alpha = 1.0f;
+ break;
+ case RGB8:
+ firstByteIndex = pixelIndex * 3;
+ pixelValue = data.get(firstByteIndex);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ this.alpha = 1.0f;
+ break;
+ case Luminance8:
+ pixelValue = data.get(pixelIndex);
+ this.intensity = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ default:
+ LOGGER.log(Level.FINEST, "Unknown type of texture: {0}. Black pixel used!", imageFormat);
+ this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
+ }
+ }
+
+ /**
+ * Stores RGBA values in the given array.
+ *
+ * @param result
+ * the array to store values
+ */
+ public void toRGBA(float[] result) {
+ result[0] = this.red;
+ result[1] = this.green;
+ result[2] = this.blue;
+ result[3] = this.alpha;
+ }
+
+ /**
+ * Stores the data in the given table.
+ *
+ * @param result
+ * the result table
+ */
+ public void toRGBA8(byte[] result) {
+ result[0] = (byte) (this.red * 255.0f);
+ result[1] = (byte) (this.green * 255.0f);
+ result[2] = (byte) (this.blue * 255.0f);
+ result[3] = (byte) (this.alpha * 255.0f);
+ }
+
+ /**
+ * Stores the pixel values in the integer.
+ *
+ * @return the integer that stores the pixel values
+ */
+ public int toARGB8() {
+ int result = 0;
+ int b = (int) (this.alpha * 255.0f);
+ result |= b << 24;
+ b = (int) (this.red * 255.0f);
+ result |= b << 16;
+ b = (int) (this.green * 255.0f);
+ result |= b << 8;
+ b = (int) (this.blue * 255.0f);
+ result |= b;
+ return result;
+ }
+
+ /**
+ * Merges two pixels (adds the values of each color).
+ *
+ * @param pixel
+ * the pixel we merge with
+ */
+ public void merge(TexturePixel pixel) {
+ float oneMinusAlpha = 1 - pixel.alpha;
+ this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red;
+ this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green;
+ this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue;
+ // alpha should be always 1.0f as a result
+ }
+
+ /**
+ * This method negates the colors.
+ */
+ public void negate() {
+ this.red = 1.0f - this.red;
+ this.green = 1.0f - this.green;
+ this.blue = 1.0f - this.blue;
+ this.alpha = 1.0f - this.alpha;
+ }
+
+ /**
+ * This method clears the pixel values.
+ */
+ public void clear() {
+ this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
+ }
+
+ /**
+ * This method adds the calues of the given pixel to the current pixel.
+ *
+ * @param pixel
+ * the pixel we add
+ */
+ public void add(TexturePixel pixel) {
+ this.red += pixel.red;
+ this.green += pixel.green;
+ this.blue += pixel.blue;
+ this.alpha += pixel.alpha;
+ this.intensity += pixel.intensity;
+ }
+
+ /**
+ * This method multiplies the values of the given pixel by the given value.
+ *
+ * @param value
+ * multiplication factor
+ */
+ public void mult(float value) {
+ this.red *= value;
+ this.green *= value;
+ this.blue *= value;
+ this.alpha *= value;
+ this.intensity *= value;
+ }
+
+ /**
+ * This method divides the values of the given pixel by the given value.
+ * ATTENTION! Beware of the zero value. This will cause you NaN's in the
+ * pixel values.
+ *
+ * @param value
+ * division factor
+ */
+ public void divide(float value) {
+ this.red /= value;
+ this.green /= value;
+ this.blue /= value;
+ this.alpha /= value;
+ this.intensity /= value;
+ }
+
+ /**
+ * This method clamps the pixel values to the given borders.
+ *
+ * @param min
+ * the minimum value
+ * @param max
+ * the maximum value
+ */
+ public void clamp(float min, float max) {
+ this.red = FastMath.clamp(this.red, min, max);
+ this.green = FastMath.clamp(this.green, min, max);
+ this.blue = FastMath.clamp(this.blue, min, max);
+ this.alpha = FastMath.clamp(this.alpha, min, max);
+ this.intensity = FastMath.clamp(this.intensity, min, max);
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ @Override
+ public String toString() {
+ return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]";
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java
new file mode 100644
index 0000000..56e4293
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * This class is used for UV coordinates generation.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class UVCoordinatesGenerator {
+ private static final Logger LOGGER = Logger.getLogger(UVCoordinatesGenerator.class.getName());
+
+ // texture UV coordinates types
+ public static final int TEXCO_ORCO = 1;
+ public static final int TEXCO_REFL = 2;
+ public static final int TEXCO_NORM = 4;
+ public static final int TEXCO_GLOB = 8;
+ public static final int TEXCO_UV = 16;
+ public static final int TEXCO_OBJECT = 32;
+ public static final int TEXCO_LAVECTOR = 64;
+ public static final int TEXCO_VIEW = 128;
+ public static final int TEXCO_STICKY = 256;
+ public static final int TEXCO_OSA = 512;
+ public static final int TEXCO_WINDOW = 1024;
+ public static final int NEED_UV = 2048;
+ public static final int TEXCO_TANGENT = 4096;
+ // still stored in vertex->accum, 1 D
+ public static final int TEXCO_PARTICLE_OR_STRAND = 8192; // strand is used
+ public static final int TEXCO_STRESS = 16384;
+ public static final int TEXCO_SPEED = 32768;
+
+ // 2D texture mapping (projection)
+ public static final int PROJECTION_FLAT = 0;
+ public static final int PROJECTION_CUBE = 1;
+ public static final int PROJECTION_TUBE = 2;
+ public static final int PROJECTION_SPHERE = 3;
+
+ /**
+ * This method generates UV coordinates for the given mesh.
+ * IMPORTANT! This method assumes that all geometries represent one node.
+ * Each containing mesh with separate material.
+ * So all meshes have the same reference to vertex table which stores all their vertices.
+ * @param texco
+ * texture coordinates type
+ * @param projection
+ * the projection type for 2D textures
+ * @param textureDimension
+ * the dimension of the texture (only 2D and 3D)
+ * @param coordinatesSwappingIndexes
+ * an array that tells how UV-coordinates need to be swapped
+ * @param geometries
+ * a list of geometries the UV coordinates will be applied to
+ * @return created UV-coordinates buffer
+ */
+ public static VertexBuffer generateUVCoordinates(int texco, int projection, int textureDimension, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
+ if (textureDimension != 2 && textureDimension != 3) {
+ throw new IllegalStateException("Unsupported texture dimension: " + textureDimension);
+ }
+
+ VertexBuffer result = new VertexBuffer(VertexBuffer.Type.TexCoord);
+ Mesh mesh = geometries.get(0).getMesh();
+ BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
+ float[] inputData = null;// positions, normals, reflection vectors, etc.
+
+ switch (texco) {
+ case TEXCO_ORCO:
+ inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
+ break;
+ case TEXCO_UV:
+ FloatBuffer uvCoordinatesBuffer = BufferUtils.createFloatBuffer(mesh.getVertexCount() * textureDimension);
+ Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
+ for (int i = 0; i < mesh.getVertexCount(); ++i) {
+ Vector2f uv = data[i % 3];
+ uvCoordinatesBuffer.put(uv.x);
+ uvCoordinatesBuffer.put(uv.y);
+ if(textureDimension == 3) {
+ uvCoordinatesBuffer.put(0);
+ }
+ }
+ result.setupData(Usage.Static, textureDimension, Format.Float, uvCoordinatesBuffer);
+ break;
+ case TEXCO_NORM:
+ inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
+ break;
+ case TEXCO_REFL:
+ case TEXCO_GLOB:
+ case TEXCO_TANGENT:
+ case TEXCO_STRESS:
+ case TEXCO_LAVECTOR:
+ case TEXCO_OBJECT:
+ case TEXCO_OSA:
+ case TEXCO_PARTICLE_OR_STRAND:
+ case TEXCO_SPEED:
+ case TEXCO_STICKY:
+ case TEXCO_VIEW:
+ case TEXCO_WINDOW:
+ LOGGER.warning("Texture coordinates type not currently supported: " + texco);
+ break;
+ default:
+ throw new IllegalStateException("Unknown texture coordinates value: " + texco);
+ }
+
+ if (inputData != null) {// make calculations
+ if (textureDimension == 2) {
+ switch (projection) {
+ case PROJECTION_FLAT:
+ inputData = UVProjectionGenerator.flatProjection(mesh, bb);
+ break;
+ case PROJECTION_CUBE:
+ inputData = UVProjectionGenerator.cubeProjection(mesh, bb);
+ break;
+ case PROJECTION_TUBE:
+ BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
+ inputData = UVProjectionGenerator.tubeProjection(mesh, bt);
+ break;
+ case PROJECTION_SPHERE:
+ BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
+ inputData = UVProjectionGenerator.sphereProjection(mesh, bs);
+ break;
+ default:
+ throw new IllegalStateException("Unknown projection type: " + projection);
+ }
+ } else {
+ Vector3f min = bb.getMin(null);
+ float[] uvCoordsResults = new float[4];//used for coordinates swapping
+ float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
+
+ // now transform the coordinates so that they are in the range of <0; 1>
+ for (int i = 0; i < inputData.length; i += 3) {
+ uvCoordsResults[1] = (inputData[i] - min.x) / ext[0];
+ uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1];
+ uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2];
+
+
+ inputData[i] = uvCoordsResults[coordinatesSwappingIndexes[0]];
+ inputData[i + 1] = uvCoordsResults[coordinatesSwappingIndexes[1]];
+ inputData[i + 2] = uvCoordsResults[coordinatesSwappingIndexes[2]];
+ }
+ }
+ result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(inputData));
+ }
+
+ // each mesh will have the same coordinates
+ for (Geometry geometry : geometries) {
+ mesh = geometry.getMesh();
+ mesh.clearBuffer(VertexBuffer.Type.TexCoord);// in case there are coordinates already set
+ mesh.setBuffer(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * This method returns the bounding box of the given geometries.
+ * @param geometries
+ * the list of geometries
+ * @return bounding box of the given geometries
+ */
+ /* package */static BoundingBox getBoundingBox(List<Geometry> geometries) {
+ BoundingBox result = null;
+ for (Geometry geometry : geometries) {
+ BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
+ if (result == null) {
+ result = bb;
+ } else {
+ result.merge(bb);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the bounding box of the given mesh.
+ * @param mesh
+ * the mesh
+ * @return bounding box of the given mesh
+ */
+ /* package */static BoundingBox getBoundingBox(Mesh mesh) {
+ mesh.updateBound();
+ BoundingVolume bv = mesh.getBound();
+ if (bv instanceof BoundingBox) {
+ return (BoundingBox) bv;
+ } else if (bv instanceof BoundingSphere) {
+ BoundingSphere bs = (BoundingSphere) bv;
+ float r = bs.getRadius();
+ return new BoundingBox(bs.getCenter(), r, r, r);
+ } else {
+ throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
+ }
+ }
+
+ /**
+ * This method returns the bounding sphere of the given geometries.
+ * @param geometries
+ * the list of geometries
+ * @return bounding sphere of the given geometries
+ */
+ /* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
+ BoundingSphere result = null;
+ for (Geometry geometry : geometries) {
+ BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometry.getMesh());
+ if (result == null) {
+ result = bs;
+ } else {
+ result.merge(bs);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the bounding sphere of the given mesh.
+ * @param mesh
+ * the mesh
+ * @return bounding sphere of the given mesh
+ */
+ /* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
+ mesh.updateBound();
+ BoundingVolume bv = mesh.getBound();
+ if (bv instanceof BoundingBox) {
+ BoundingBox bb = (BoundingBox) bv;
+ float r = Math.max(bb.getXExtent(), bb.getYExtent());
+ r = Math.max(r, bb.getZExtent());
+ return new BoundingSphere(r, bb.getCenter());
+ } else if (bv instanceof BoundingSphere) {
+ return (BoundingSphere) bv;
+ } else {
+ throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
+ }
+ }
+
+ /**
+ * This method returns the bounding tube of the given mesh.
+ * @param mesh
+ * the mesh
+ * @return bounding tube of the given mesh
+ */
+ /* package */static BoundingTube getBoundingTube(Mesh mesh) {
+ Vector3f center = new Vector3f();
+ float maxx = -Float.MAX_VALUE, minx = Float.MAX_VALUE;
+ float maxy = -Float.MAX_VALUE, miny = Float.MAX_VALUE;
+ float maxz = -Float.MAX_VALUE, minz = Float.MAX_VALUE;
+
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ int limit = positions.limit();
+ for (int i = 0; i < limit; i += 3) {
+ float x = positions.get(i);
+ float y = positions.get(i + 1);
+ float z = positions.get(i + 2);
+ center.addLocal(x, y, z);
+ maxx = x > maxx ? x : maxx;
+ minx = x < minx ? x : minx;
+ maxy = y > maxy ? y : maxy;
+ miny = y < miny ? y : miny;
+ maxz = z > maxz ? z : maxz;
+ minz = z < minz ? z : minz;
+ }
+ center.divideLocal(limit / 3);
+
+ float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
+ return new BoundingTube(radius, maxz - minz, center);
+ }
+
+ /**
+ * This method returns the bounding tube of the given geometries.
+ * @param geometries
+ * the list of geometries
+ * @return bounding tube of the given geometries
+ */
+ /* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
+ BoundingTube result = null;
+ for (Geometry geometry : geometries) {
+ BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometry.getMesh());
+ if (result == null) {
+ result = bt;
+ } else {
+ result.merge(bt);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A very simple bounding tube. Id holds only the basic data bout the bounding tube
+ * and does not provide full functionality of a BoundingVolume.
+ * Should be replaced with a bounding tube that extends the BoundingVolume if it is ever created.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ /* package */static class BoundingTube {
+ private float radius;
+ private float height;
+ private Vector3f center;
+
+ /**
+ * Constructor creates the tube with the given params.
+ * @param radius
+ * the radius of the tube
+ * @param height
+ * the height of the tube
+ * @param center
+ * the center of the tube
+ */
+ public BoundingTube(float radius, float height, Vector3f center) {
+ this.radius = radius;
+ this.height = height;
+ this.center = center;
+ }
+
+ /**
+ * This method merges two bounding tubes.
+ * @param boundingTube
+ * bounding tube to be merged woth the current one
+ * @return new instance of bounding tube representing the tubes' merge
+ */
+ public BoundingTube merge(BoundingTube boundingTube) {
+ // get tubes (tube1.radius >= tube2.radius)
+ BoundingTube tube1, tube2;
+ if (this.radius >= boundingTube.radius) {
+ tube1 = this;
+ tube2 = boundingTube;
+ } else {
+ tube1 = boundingTube;
+ tube2 = this;
+ }
+ float r1 = tube1.radius;
+ float r2 = tube2.radius;
+
+ float minZ = Math.min(tube1.center.z - tube1.height * 0.5f, tube2.center.z - tube2.height * 0.5f);
+ float maxZ = Math.max(tube1.center.z + tube1.height * 0.5f, tube2.center.z + tube2.height * 0.5f);
+ float height = maxZ - minZ;
+ Vector3f distance = tube2.center.subtract(tube1.center);
+ Vector3f center = tube1.center.add(distance.mult(0.5f));
+ distance.z = 0;// projecting this vector on XY plane
+ float d = distance.length();
+ // d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the inside
+ // d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1
+ float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f;
+ return new BoundingTube(radius, height, center);
+ }
+
+ /**
+ * This method returns the radius of the tube.
+ * @return the radius of the tube
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * This method returns the height of the tube.
+ * @return the height of the tube
+ */
+ public float getHeight() {
+ return height;
+ }
+
+ /**
+ * This method returns the center of the tube.
+ * @return the center of the tube
+ */
+ public Vector3f getCenter() {
+ return center;
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java
new file mode 100644
index 0000000..4412d60
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java
@@ -0,0 +1,226 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.math.FastMath;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTube;
+import java.nio.FloatBuffer;
+
+/**
+ * This class helps with projection calculations.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class UVProjectionGenerator {
+ /**
+ * Flat projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bb
+ * the bounding box for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] flatProjection(Mesh mesh, BoundingBox bb) {
+ if (bb == null) {
+ bb = UVCoordinatesGenerator.getBoundingBox(mesh);
+ }
+ Vector3f min = bb.getMin(null);
+ float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f };
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+ for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+ uvCoordinates[j] = (positions.get(i) - min.x) / ext[0];
+ uvCoordinates[j + 1] = (positions.get(i + 1) - min.y) / ext[1];
+ // skip the Z-coordinate
+ }
+ return uvCoordinates;
+ }
+
+ /**
+ * Cube projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bb
+ * the bounding box for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] cubeProjection(Mesh mesh, BoundingBox bb) {
+ Triangle triangle = new Triangle();
+ Vector3f x = new Vector3f(1, 0, 0);
+ Vector3f y = new Vector3f(0, 1, 0);
+ Vector3f z = new Vector3f(0, 0, 1);
+ Vector3f min = bb.getMin(null);
+ float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f };
+
+ float[] uvCoordinates = new float[mesh.getTriangleCount() * 6];// 6 == 3 * 2
+ float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
+ for (int i = 0, pointIndex = 0; i < mesh.getTriangleCount(); ++i) {
+ mesh.getTriangle(i, triangle);
+ Vector3f n = triangle.getNormal();
+ float dotNX = Math.abs(n.dot(x));
+ float dorNY = Math.abs(n.dot(y));
+ float dotNZ = Math.abs(n.dot(z));
+ if (dotNX > borderAngle) {
+ if (dotNZ < borderAngle) {// discard X-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
+ } else {// discard Z-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+ }
+ } else {
+ if (dorNY > borderAngle) {// discard Y-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
+ } else {// discard Z-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+ }
+ }
+ triangle.setNormal(null);// clear the previous normal vector
+ }
+ return uvCoordinates;
+ }
+
+ /**
+ * Tube projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bt
+ * the bounding tube for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] tubeProjection(Mesh mesh, BoundingTube bt) {
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+ Vector3f v = new Vector3f();
+ float cx = bt.getCenter().x, cy = bt.getCenter().y;
+ Vector3f uBase = new Vector3f(0, -1, 0);
+
+ float vBase = bt.getCenter().z - bt.getHeight() * 0.5f;
+ for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+ // calculating U
+ v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
+ v.normalizeLocal();
+ float angle = v.angleBetween(uBase);// result between [0; PI]
+ if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
+ angle = FastMath.TWO_PI - angle;
+ }
+ uvCoordinates[j] = angle / FastMath.TWO_PI;
+
+ // calculating V
+ float z = positions.get(i + 2);
+ uvCoordinates[j + 1] = (z - vBase) / bt.getHeight();
+ }
+
+ //looking for splitted triangles
+ Triangle triangle = new Triangle();
+ for(int i=0;i<mesh.getTriangleCount();++i) {
+ mesh.getTriangle(i, triangle);
+ float sgn1 = Math.signum(triangle.get1().x-cx);
+ float sgn2 = Math.signum(triangle.get2().x-cx);
+ float sgn3 = Math.signum(triangle.get3().x-cx);
+ float xSideFactor = sgn1 + sgn2 + sgn3;
+ float ySideFactor = Math.signum(triangle.get1().y-cy)+
+ Math.signum(triangle.get2().y-cy)+
+ Math.signum(triangle.get3().y-cy);
+ if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
+ //indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
+ if(sgn1==1.0f) {
+ uvCoordinates[i*3*2] += 1.0f;
+ }
+ if(sgn2==1.0f) {
+ uvCoordinates[(i*3+1)*2] += 1.0f;
+ }
+ if(sgn3==1.0f) {
+ uvCoordinates[(i*3+2)*2] += 1.0f;
+ }
+ }
+ }
+ return uvCoordinates;
+ }
+
+ /**
+ * Sphere projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bb
+ * the bounding box for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] sphereProjection(Mesh mesh, BoundingSphere bs) {
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+ Vector3f v = new Vector3f();
+ float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z;
+ Vector3f uBase = new Vector3f(0, -1, 0);
+ Vector3f vBase = new Vector3f(0, 0, -1);
+
+ for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+ // calculating U
+ v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
+ v.normalizeLocal();
+ float angle = v.angleBetween(uBase);// result between [0; PI]
+ if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
+ angle = FastMath.TWO_PI - angle;
+ }
+ uvCoordinates[j] = angle / FastMath.TWO_PI;
+
+ // calculating V
+ v.set(positions.get(i)-cx, positions.get(i + 1)-cy, positions.get(i + 2)-cz);
+ v.normalizeLocal();
+ angle = v.angleBetween(vBase);// result between [0; PI]
+ uvCoordinates[j+1] = angle / FastMath.PI;
+ }
+
+ //looking for splitted triangles
+ Triangle triangle = new Triangle();
+ for(int i=0;i<mesh.getTriangleCount();++i) {
+ mesh.getTriangle(i, triangle);
+ float sgn1 = Math.signum(triangle.get1().x-cx);
+ float sgn2 = Math.signum(triangle.get2().x-cx);
+ float sgn3 = Math.signum(triangle.get3().x-cx);
+ float xSideFactor = sgn1 + sgn2 + sgn3;
+ float ySideFactor = Math.signum(triangle.get1().y-cy)+
+ Math.signum(triangle.get2().y-cy)+
+ Math.signum(triangle.get3().y-cy);
+ if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
+ //indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
+ if(sgn1==1.0f) {
+ uvCoordinates[i*3*2] += 1.0f;
+ }
+ if(sgn2==1.0f) {
+ uvCoordinates[(i*3+1)*2] += 1.0f;
+ }
+ if(sgn3==1.0f) {
+ uvCoordinates[(i*3+2)*2] += 1.0f;
+ }
+ }
+ }
+ return uvCoordinates;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
new file mode 100644
index 0000000..505c94b
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
@@ -0,0 +1,195 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+
+/**
+ * An abstract class that contains the basic methods used by the classes that
+ * will derive from it.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */abstract class AbstractTextureBlender implements TextureBlender {
+ /**
+ * This method blends the single pixel depending on the blending type.
+ *
+ * @param result
+ * the result pixel
+ * @param materialColor
+ * the material color
+ * @param pixelColor
+ * the pixel color
+ * @param blendFactor
+ * the blending factor
+ * @param blendtype
+ * the blending type
+ * @param blenderContext
+ * the blender context
+ */
+ protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, float blendFactor, int blendtype, BlenderContext blenderContext) {
+ float oneMinusFactor = 1.0f - blendFactor, col;
+
+ switch (blendtype) {
+ case MTEX_BLEND:
+ result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];
+ result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1];
+ result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2];
+ break;
+ case MTEX_MUL:
+ result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0];
+ result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1];
+ result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2];
+ break;
+ case MTEX_DIV:
+ if (pixelColor[0] != 0.0) {
+ result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f;
+ }
+ if (pixelColor[1] != 0.0) {
+ result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f;
+ }
+ if (pixelColor[2] != 0.0) {
+ result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f;
+ }
+ break;
+ case MTEX_SCREEN:
+ result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
+ result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
+ result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
+ break;
+ case MTEX_OVERLAY:
+ if (materialColor[0] < 0.5f) {
+ result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]);
+ } else {
+ result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
+ }
+ if (materialColor[1] < 0.5f) {
+ result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]);
+ } else {
+ result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
+ }
+ if (materialColor[2] < 0.5f) {
+ result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]);
+ } else {
+ result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
+ }
+ break;
+ case MTEX_SUB:
+ result[0] = materialColor[0] - blendFactor * pixelColor[0];
+ result[1] = materialColor[1] - blendFactor * pixelColor[1];
+ result[2] = materialColor[2] - blendFactor * pixelColor[2];
+ result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
+ result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
+ result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
+ break;
+ case MTEX_ADD:
+ result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f;
+ result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f;
+ result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f;
+ break;
+ case MTEX_DIFF:
+ result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]);
+ result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]);
+ result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]);
+ break;
+ case MTEX_DARK:
+ col = blendFactor * pixelColor[0];
+ result[0] = col < materialColor[0] ? col : materialColor[0];
+ col = blendFactor * pixelColor[1];
+ result[1] = col < materialColor[1] ? col : materialColor[1];
+ col = blendFactor * pixelColor[2];
+ result[2] = col < materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_LIGHT:
+ col = blendFactor * pixelColor[0];
+ result[0] = col > materialColor[0] ? col : materialColor[0];
+ col = blendFactor * pixelColor[1];
+ result[1] = col > materialColor[1] ? col : materialColor[1];
+ col = blendFactor * pixelColor[2];
+ result[2] = col > materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_BLEND_HUE:
+ case MTEX_BLEND_SAT:
+ case MTEX_BLEND_VAL:
+ case MTEX_BLEND_COLOR:
+ System.arraycopy(materialColor, 0, result, 0, 3);
+ this.blendHSV(blendtype, result, blendFactor, pixelColor, blenderContext);
+ break;
+ default:
+ throw new IllegalStateException("Unknown blend type: " + blendtype);
+ }
+ }
+
+ /**
+ * The method that performs the ramp blending.
+ *
+ * @param type
+ * the blend type
+ * @param materialRGB
+ * the rgb value of the material, here the result is stored too
+ * @param fac
+ * color affection factor
+ * @param pixelColor
+ * the texture color
+ * @param blenderContext
+ * the blender context
+ */
+ protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) {
+ float oneMinusFactor = 1.0f - fac;
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+
+ switch (type) {
+ case MTEX_BLEND_HUE: {// FIXME: not working well for image textures
+ // (works fine for generated textures)
+ float[] colorTransformResult = new float[3];
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
+ if (colorTransformResult[0] != 0.0f) {
+ float colH = colorTransformResult[0];
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
+ materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
+ materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0];
+ materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1];
+ materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2];
+ }
+ break;
+ }
+ case MTEX_BLEND_SAT: {
+ float[] colorTransformResult = new float[3];
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
+ float h = colorTransformResult[0];
+ float s = colorTransformResult[1];
+ float v = colorTransformResult[2];
+ if (s != 0.0f) {
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
+ materialHelper.hsvToRgb(h, (oneMinusFactor * s + fac * colorTransformResult[1]), v, materialRGB);
+ }
+ break;
+ }
+ case MTEX_BLEND_VAL: {
+ float[] rgbToHsv = new float[3];
+ float[] colToHsv = new float[3];
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
+ materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], (oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2]), materialRGB);
+ break;
+ }
+ case MTEX_BLEND_COLOR: {// FIXME: not working well for image
+ // textures (works fine for generated
+ // textures)
+ float[] rgbToHsv = new float[3];
+ float[] colToHsv = new float[3];
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
+ if (colToHsv[2] != 0) {
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
+ materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
+ materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0];
+ materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1];
+ materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2];
+ }
+ break;
+ }
+ default:
+ throw new IllegalStateException("Unknown ramp type: " + type);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
new file mode 100644
index 0000000..08e25d3
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
@@ -0,0 +1,51 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Texture;
+
+/**
+ * An interface for texture blending classes (the classes that mix the texture
+ * pixels with the material colors).
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public interface TextureBlender {
+ // types of blending
+ int MTEX_BLEND = 0;
+ int MTEX_MUL = 1;
+ int MTEX_ADD = 2;
+ int MTEX_SUB = 3;
+ int MTEX_DIV = 4;
+ int MTEX_DARK = 5;
+ int MTEX_DIFF = 6;
+ int MTEX_LIGHT = 7;
+ int MTEX_SCREEN = 8;
+ int MTEX_OVERLAY = 9;
+ int MTEX_BLEND_HUE = 10;
+ int MTEX_BLEND_SAT = 11;
+ int MTEX_BLEND_VAL = 12;
+ int MTEX_BLEND_COLOR = 13;
+ int MTEX_NUM_BLENDTYPES = 14;
+
+ /**
+ * This method blends the given texture with material color and the defined
+ * color in 'map to' panel. As a result of this method a new texture is
+ * created. The input texture is NOT.
+ *
+ * @param materialColor
+ * the material diffuse color
+ * @param texture
+ * the texture we use in blending
+ * @param color
+ * the color defined for the texture
+ * @param affectFactor
+ * the factor that the color affects the texture (value form 0.0
+ * to 1.0)
+ * @param blendType
+ * the blending type
+ * @param blenderContext
+ * the blender context
+ * @return new texture that was created after the blending
+ */
+ Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext);
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
new file mode 100644
index 0000000..75fc0c5
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
@@ -0,0 +1,162 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> RGBA8
+ * <li> ABGR8
+ * <li> BGR8
+ * <li> RGB8
+ * Not yet supported (but will be):
+ * <li> ARGB4444:
+ * <li> RGB10:
+ * <li> RGB111110F:
+ * <li> RGB16:
+ * <li> RGB16F:
+ * <li> RGB16F_to_RGB111110F:
+ * <li> RGB16F_to_RGB9E5:
+ * <li> RGB32F:
+ * <li> RGB565:
+ * <li> RGB5A1:
+ * <li> RGB9E5:
+ * <li> RGBA16:
+ * <li> RGBA16F
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderAWT extends AbstractTextureBlender {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderAWT.class.getName());
+
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
+ Format format = texture.getImage().getFormat();
+ ByteBuffer data = texture.getImage().getData(0);
+ data.rewind();
+
+ int width = texture.getImage().getWidth();
+ int height = texture.getImage().getHeight();
+ int depth = texture.getImage().getDepth();
+ if (depth == 0) {
+ depth = 1;
+ }
+ ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
+
+ float[] resultPixel = new float[4];
+ int dataIndex = 0;
+ while (data.hasRemaining()) {
+ float tin = this.setupMaterialColor(data, format, neg, pixelColor);
+ this.blendPixel(resultPixel, materialColor, color, tin, blendType, blenderContext);
+ newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
+ newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f));
+ }
+ if (texture.getType() == Texture.Type.TwoDimensional) {
+ return new Texture2D(new Image(Format.RGBA8, width, height, newData));
+ } else {
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(newData);
+ return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+ }
+ }
+
+ /**
+ * This method alters the material color in a way dependent on the type of
+ * the image. For example the color remains untouched if the texture is of
+ * Luminance type. The luminance defines the interaction between the
+ * material color and color defined for texture blending. If the type has 3
+ * or more color channels then the material color is replaced with the
+ * texture's color and later blended with the defined blend color. All alpha
+ * values (if present) are ignored and not used during blending.
+ *
+ * @param data
+ * the image data
+ * @param imageFormat
+ * the format of the image
+ * @param neg
+ * defines it the result color should be nagated
+ * @param materialColor
+ * the material's color (value may be changed)
+ * @return texture intensity for the current pixel
+ */
+ protected float setupMaterialColor(ByteBuffer data, Format imageFormat, boolean neg, float[] materialColor) {
+ float tin = 0.0f;
+ byte pixelValue = data.get();// at least one byte is always taken :)
+ float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ switch (imageFormat) {
+ case RGBA8:
+ materialColor[0] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case ABGR8:
+ materialColor[3] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case BGR8:
+ materialColor[2] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ materialColor[3] = 1.0f;
+ break;
+ case RGB8:
+ materialColor[0] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ materialColor[3] = 1.0f;
+ break;
+ case ARGB4444:
+ case RGB10:
+ case RGB111110F:
+ case RGB16:
+ case RGB16F:
+ case RGB16F_to_RGB111110F:
+ case RGB16F_to_RGB9E5:
+ case RGB32F:
+ case RGB565:
+ case RGB5A1:
+ case RGB9E5:
+ case RGBA16:
+ case RGBA16F:
+ case RGBA32F:// TODO: implement these textures
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
+ break;
+ default:
+ throw new IllegalStateException("Invalid image format type for AWT texture blender: " + imageFormat);
+ }
+ if (neg) {
+ materialColor[0] = 1.0f - materialColor[0];
+ materialColor[1] = 1.0f - materialColor[1];
+ materialColor[2] = 1.0f - materialColor[2];
+ }
+ // Blender formula for texture intensity calculation:
+ // 0.35*texres.tr+0.45*texres.tg+0.2*texres.tb
+ tin = 0.35f * materialColor[0] + 0.45f * materialColor[1] + 0.2f * materialColor[2];
+ return tin;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
new file mode 100644
index 0000000..a532cfb
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
@@ -0,0 +1,96 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jme3tools.converters.RGB565;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> DXT1
+ * <li> DXT3
+ * <li> DXT5
+ * Not yet supported (but will be):
+ * <li> DXT1A:
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderDDS extends AbstractTextureBlender {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderDDS.class.getName());
+
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ Format format = texture.getImage().getFormat();
+ ByteBuffer data = texture.getImage().getData(0);
+ data.rewind();
+
+ int width = texture.getImage().getWidth();
+ int height = texture.getImage().getHeight();
+ int depth = texture.getImage().getDepth();
+ if (depth == 0) {
+ depth = 1;
+ }
+ ByteBuffer newData = BufferUtils.createByteBuffer(data.remaining());
+
+ float[] resultPixel = new float[4];
+ float[] pixelColor = new float[4];
+ TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
+ int dataIndex = 0;
+ while (data.hasRemaining()) {
+ switch (format) {
+ case DXT3:
+ case DXT5:
+ newData.putLong(dataIndex, data.getLong());// just copy the
+ // 8 bytes of
+ // alphas
+ dataIndex += 8;
+ case DXT1:
+ int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
+ int col1 = RGB565.RGB565_to_ARGB8(data.getShort());
+ colors[0].fromARGB8(col0);
+ colors[1].fromARGB8(col1);
+ break;
+ case DXT1A:
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", format);
+ break;
+ default:
+ throw new IllegalStateException("Invalid image format type for DDS texture blender: " + format);
+ }
+
+ // blending colors
+ for (int i = 0; i < colors.length; ++i) {
+ if (neg) {
+ colors[i].negate();
+ }
+ colors[i].toRGBA(pixelColor);
+ this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
+ colors[i].fromARGB8(1, resultPixel[0], resultPixel[1], resultPixel[2]);
+ int argb8 = colors[i].toARGB8();
+ short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
+ newData.putShort(dataIndex, rgb565);
+ dataIndex += 2;
+ }
+
+ // just copy the remaining 4 bytes of the current texel
+ newData.putInt(dataIndex, data.getInt());
+ dataIndex += 4;
+ }
+ if (texture.getType() == Texture.Type.TwoDimensional) {
+ return new Texture2D(new Image(format, width, height, newData));
+ } else {
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(newData);
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
new file mode 100644
index 0000000..c9ad61a
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
@@ -0,0 +1,81 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class creates the texture blending class depending on the texture type.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderFactory {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderFactory.class.getName());
+
+ /**
+ * This method creates the blending class.
+ *
+ * @param format
+ * the texture format
+ * @returntexture blending class
+ */
+ public static TextureBlender createTextureBlender(Format format) {
+ switch (format) {
+ case Luminance8:
+ case Luminance8Alpha8:
+ case Luminance16:
+ case Luminance16Alpha16:
+ case Luminance16F:
+ case Luminance16FAlpha16F:
+ case Luminance32F:
+ return new TextureBlenderLuminance();
+ case RGBA8:
+ case ABGR8:
+ case BGR8:
+ case RGB8:
+ case RGB10:
+ case RGB111110F:
+ case RGB16:
+ case RGB16F:
+ case RGB16F_to_RGB111110F:
+ case RGB16F_to_RGB9E5:
+ case RGB32F:
+ case RGB565:
+ case RGB5A1:
+ case RGB9E5:
+ case RGBA16:
+ case RGBA16F:
+ case RGBA32F:
+ return new TextureBlenderAWT();
+ case DXT1:
+ case DXT1A:
+ case DXT3:
+ case DXT5:
+ return new TextureBlenderDDS();
+ case Alpha16:
+ case Alpha8:
+ case ARGB4444:
+ case Depth:
+ case Depth16:
+ case Depth24:
+ case Depth32:
+ case Depth32F:
+ case Intensity16:
+ case Intensity8:
+ case LATC:
+ case LTC:
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}. Returning a blender that does not change the texture.", format);
+ return new TextureBlender() {
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ return texture;
+ }
+ };
+ default:
+ throw new IllegalStateException("Unknown image format type: " + format);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
new file mode 100644
index 0000000..a616685
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
@@ -0,0 +1,221 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> Luminance8
+ * <li> Luminance8Alpha8
+ * Not yet supported (but will be):
+ * <li> Luminance16:
+ * <li> Luminance16Alpha16:
+ * <li> Luminance16F:
+ * <li> Luminance16FAlpha16F:
+ * <li> Luminance32F:
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderLuminance extends AbstractTextureBlender {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderLuminance.class.getName());
+
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ Format format = texture.getImage().getFormat();
+ ByteBuffer data = texture.getImage().getData(0);
+ data.rewind();
+
+ int width = texture.getImage().getWidth();
+ int height = texture.getImage().getHeight();
+ int depth = texture.getImage().getDepth();
+ if (depth == 0) {
+ depth = 1;
+ }
+ ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
+
+ float[] resultPixel = new float[4];
+ float[] tinAndAlpha = new float[2];
+ int dataIndex = 0;
+ while (data.hasRemaining()) {
+ this.getTinAndAlpha(data, format, neg, tinAndAlpha);
+ this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], affectFactor, blendType, blenderContext);
+ newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
+ newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
+ }
+ if (texture.getType() == Texture.Type.TwoDimensional) {
+ return new Texture2D(new Image(Format.RGBA8, width, height, newData));
+ } else {
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(newData);
+ return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+ }
+ }
+
+ /**
+ * This method return texture intensity and alpha value.
+ *
+ * @param data
+ * the texture data
+ * @param imageFormat
+ * the image format
+ * @param neg
+ * indicates if the texture is negated
+ * @param result
+ * the table (2 elements) where the result is being stored
+ */
+ protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
+ byte pixelValue = data.get();// at least one byte is always taken
+ float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ switch (imageFormat) {
+ case Luminance8:
+ result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
+ result[1] = 1.0f;
+ break;
+ case Luminance8Alpha8:
+ result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
+ pixelValue = data.get();
+ result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case Luminance16:
+ case Luminance16Alpha16:
+ case Luminance16F:
+ case Luminance16FAlpha16F:
+ case Luminance32F:
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
+ break;
+ default:
+ throw new IllegalStateException("Invalid image format type for DDS texture blender: " + imageFormat);
+ }
+ }
+
+ /**
+ * This method blends the texture with an appropriate color.
+ *
+ * @param result
+ * the result color (variable 'in' in blender source code)
+ * @param materialColor
+ * the texture color (variable 'out' in blender source coude)
+ * @param color
+ * the previous color (variable 'tex' in blender source code)
+ * @param textureIntensity
+ * texture intensity (variable 'fact' in blender source code)
+ * @param textureFactor
+ * texture affection factor (variable 'facg' in blender source
+ * code)
+ * @param blendtype
+ * the blend type
+ * @param blenderContext
+ * the blender context
+ */
+ protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
+ float oneMinusFactor, col;
+ textureIntensity *= textureFactor;
+
+ switch (blendtype) {
+ case MTEX_BLEND:
+ oneMinusFactor = 1.0f - textureIntensity;
+ result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
+ result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
+ result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
+ break;
+ case MTEX_MUL:
+ oneMinusFactor = 1.0f - textureFactor;
+ result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
+ result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
+ result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
+ break;
+ case MTEX_DIV:
+ oneMinusFactor = 1.0f - textureIntensity;
+ if (color[0] != 0.0) {
+ result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
+ }
+ if (color[1] != 0.0) {
+ result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
+ }
+ if (color[2] != 0.0) {
+ result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
+ }
+ break;
+ case MTEX_SCREEN:
+ oneMinusFactor = 1.0f - textureFactor;
+ result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
+ result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
+ result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
+ break;
+ case MTEX_OVERLAY:
+ oneMinusFactor = 1.0f - textureFactor;
+ if (materialColor[0] < 0.5f) {
+ result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
+ } else {
+ result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
+ }
+ if (materialColor[1] < 0.5f) {
+ result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
+ } else {
+ result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
+ }
+ if (materialColor[2] < 0.5f) {
+ result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
+ } else {
+ result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
+ }
+ break;
+ case MTEX_SUB:
+ result[0] = materialColor[0] - textureIntensity * color[0];
+ result[1] = materialColor[1] - textureIntensity * color[1];
+ result[2] = materialColor[2] - textureIntensity * color[2];
+ result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
+ result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
+ result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
+ break;
+ case MTEX_ADD:
+ result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
+ result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
+ result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
+ break;
+ case MTEX_DIFF:
+ oneMinusFactor = 1.0f - textureIntensity;
+ result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
+ result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
+ result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
+ break;
+ case MTEX_DARK:
+ col = textureIntensity * color[0];
+ result[0] = col < materialColor[0] ? col : materialColor[0];
+ col = textureIntensity * color[1];
+ result[1] = col < materialColor[1] ? col : materialColor[1];
+ col = textureIntensity * color[2];
+ result[2] = col < materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_LIGHT:
+ col = textureIntensity * color[0];
+ result[0] = col > materialColor[0] ? col : materialColor[0];
+ col = textureIntensity * color[1];
+ result[1] = col > materialColor[1] ? col : materialColor[1];
+ col = textureIntensity * color[2];
+ result[2] = col > materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_BLEND_HUE:
+ case MTEX_BLEND_SAT:
+ case MTEX_BLEND_VAL:
+ case MTEX_BLEND_COLOR:
+ System.arraycopy(materialColor, 0, result, 0, 3);
+ this.blendHSV(blendtype, result, textureIntensity, color, blenderContext);
+ break;
+ default:
+ throw new IllegalStateException("Unknown blend type: " + blendtype);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat b/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat
new file mode 100644
index 0000000..81fea0b
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat
Binary files differ
diff --git a/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java b/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java
new file mode 100644
index 0000000..f6d24f4
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java
@@ -0,0 +1,274 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet;
+
+import com.jme3.app.Application;
+import com.jme3.app.state.AppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.bullet.PhysicsSpace.BroadphaseType;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import java.util.concurrent.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>BulletAppState</code> allows using bullet physics in an Application.
+ * @author normenhansen
+ */
+public class BulletAppState implements AppState, PhysicsTickListener {
+
+ protected boolean initialized = false;
+ protected Application app;
+ protected AppStateManager stateManager;
+ protected ScheduledThreadPoolExecutor executor;
+ protected PhysicsSpace pSpace;
+ protected ThreadingType threadingType = ThreadingType.SEQUENTIAL;
+ protected BroadphaseType broadphaseType = BroadphaseType.DBVT;
+ protected Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f);
+ protected Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
+ private float speed = 1;
+ protected boolean active = true;
+ protected float tpf;
+ protected Future physicsFuture;
+
+ /**
+ * Creates a new BulletAppState running a PhysicsSpace for physics simulation,
+ * use getStateManager().addState(bulletAppState) to enable physics for an Application.
+ */
+ public BulletAppState() {
+ }
+
+ /**
+ * Creates a new BulletAppState running a PhysicsSpace for physics simulation,
+ * use getStateManager().addState(bulletAppState) to enable physics for an Application.
+ * @param broadphaseType The type of broadphase collision detection, BroadphaseType.DVBT is the default
+ */
+ public BulletAppState(BroadphaseType broadphaseType) {
+ this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
+ }
+
+ /**
+ * Creates a new BulletAppState running a PhysicsSpace for physics simulation,
+ * use getStateManager().addState(bulletAppState) to enable physics for an Application.
+ * An AxisSweep broadphase is used.
+ * @param worldMin The minimum world extent
+ * @param worldMax The maximum world extent
+ */
+ public BulletAppState(Vector3f worldMin, Vector3f worldMax) {
+ this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
+ }
+
+ public BulletAppState(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) {
+ this.worldMin.set(worldMin);
+ this.worldMax.set(worldMax);
+ this.broadphaseType = broadphaseType;
+ }
+
+ private boolean startPhysicsOnExecutor() {
+ if (executor != null) {
+ executor.shutdown();
+ }
+ executor = new ScheduledThreadPoolExecutor(1);
+ final BulletAppState app = this;
+ Callable<Boolean> call = new Callable<Boolean>() {
+
+ public Boolean call() throws Exception {
+ detachedPhysicsLastUpdate = System.currentTimeMillis();
+ pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType);
+ pSpace.addTickListener(app);
+ return true;
+ }
+ };
+ try {
+ return executor.submit(call).get();
+ } catch (InterruptedException ex) {
+ Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
+ return false;
+ } catch (ExecutionException ex) {
+ Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
+ return false;
+ }
+ }
+ private Callable<Boolean> parallelPhysicsUpdate = new Callable<Boolean>() {
+
+ public Boolean call() throws Exception {
+ pSpace.update(tpf * getSpeed());
+ return true;
+ }
+ };
+ long detachedPhysicsLastUpdate = 0;
+ private Callable<Boolean> detachedPhysicsUpdate = new Callable<Boolean>() {
+
+ public Boolean call() throws Exception {
+ pSpace.update(getPhysicsSpace().getAccuracy() * getSpeed());
+ pSpace.distributeEvents();
+ long update = System.currentTimeMillis() - detachedPhysicsLastUpdate;
+ detachedPhysicsLastUpdate = System.currentTimeMillis();
+ executor.schedule(detachedPhysicsUpdate, Math.round(getPhysicsSpace().getAccuracy() * 1000000.0f) - (update * 1000), TimeUnit.MICROSECONDS);
+ return true;
+ }
+ };
+
+ public PhysicsSpace getPhysicsSpace() {
+ return pSpace;
+ }
+
+ /**
+ * The physics system is started automatically on attaching, if you want to start it
+ * before for some reason, you can use this method.
+ */
+ public void startPhysics() {
+ //start physics thread(pool)
+ if (threadingType == ThreadingType.PARALLEL) {
+ startPhysicsOnExecutor();
+// } else if (threadingType == ThreadingType.DETACHED) {
+// startPhysicsOnExecutor();
+// executor.submit(detachedPhysicsUpdate);
+ } else {
+ pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType);
+ }
+ pSpace.addTickListener(this);
+ initialized = true;
+ }
+
+ public void initialize(AppStateManager stateManager, Application app) {
+ if (!initialized) {
+ startPhysics();
+ }
+ initialized = true;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.active = enabled;
+ }
+
+ public boolean isEnabled() {
+ return active;
+ }
+
+ public void stateAttached(AppStateManager stateManager) {
+ if (!initialized) {
+ startPhysics();
+ }
+ if (threadingType == ThreadingType.PARALLEL) {
+ PhysicsSpace.setLocalThreadPhysicsSpace(pSpace);
+ }
+ }
+
+ public void stateDetached(AppStateManager stateManager) {
+ }
+
+ public void update(float tpf) {
+ if (!active) {
+ return;
+ }
+// if (threadingType != ThreadingType.DETACHED) {
+ pSpace.distributeEvents();
+// }
+ this.tpf = tpf;
+ }
+
+ public void render(RenderManager rm) {
+ if (!active) {
+ return;
+ }
+ if (threadingType == ThreadingType.PARALLEL) {
+ physicsFuture = executor.submit(parallelPhysicsUpdate);
+ } else if (threadingType == ThreadingType.SEQUENTIAL) {
+ pSpace.update(active ? tpf * speed : 0);
+ } else {
+ }
+ }
+
+ public void postRender() {
+ if (physicsFuture != null) {
+ try {
+ physicsFuture.get();
+ physicsFuture = null;
+ } catch (InterruptedException ex) {
+ Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (ExecutionException ex) {
+ Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ public void cleanup() {
+ if (executor != null) {
+ executor.shutdown();
+ executor = null;
+ }
+ pSpace.removeTickListener(this);
+ pSpace.destroy();
+ }
+
+ /**
+ * @return the threadingType
+ */
+ public ThreadingType getThreadingType() {
+ return threadingType;
+ }
+
+ /**
+ * Use before attaching state
+ * @param threadingType the threadingType to set
+ */
+ public void setThreadingType(ThreadingType threadingType) {
+ this.threadingType = threadingType;
+ }
+
+ /**
+ * Use before attaching state
+ */
+ public void setBroadphaseType(BroadphaseType broadphaseType) {
+ this.broadphaseType = broadphaseType;
+ }
+
+ /**
+ * Use before attaching state
+ */
+ public void setWorldMin(Vector3f worldMin) {
+ this.worldMin = worldMin;
+ }
+
+ /**
+ * Use before attaching state
+ */
+ public void setWorldMax(Vector3f worldMax) {
+ this.worldMax = worldMax;
+ }
+
+ public float getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(float speed) {
+ this.speed = speed;
+ }
+
+ public void prePhysicsTick(PhysicsSpace space, float f) {
+ }
+
+ public void physicsTick(PhysicsSpace space, float f) {
+ }
+
+ public enum ThreadingType {
+
+ /**
+ * Default mode; user update, physics update and rendering happen sequentially (single threaded)
+ */
+ SEQUENTIAL,
+ /**
+ * Parallel threaded mode; physics update and rendering are executed in parallel, update order is kept.<br/>
+ * Multiple BulletAppStates will execute in parallel in this mode.
+ */
+ PARALLEL,
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/PhysicsTickListener.java b/engine/src/bullet-common/com/jme3/bullet/PhysicsTickListener.java
new file mode 100644
index 0000000..0f3bbca
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/PhysicsTickListener.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet;
+
+/**
+ * Implement this interface to be called from the physics thread on a physics update.
+ * @author normenhansen
+ */
+public interface PhysicsTickListener {
+
+ /**
+ * Called before the physics is actually stepped, use to apply forces etc.
+ * @param space
+ * @param f
+ */
+ public void prePhysicsTick(PhysicsSpace space, float f);
+
+ /**
+ * Called after the physics has been stepped, use to check for forces etc.
+ * @param space
+ * @param f
+ */
+ public void physicsTick(PhysicsSpace space, float f);
+
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java b/engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java
new file mode 100644
index 0000000..739598c
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java
@@ -0,0 +1,25 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.jme3.bullet.collision;
+
+/**
+ *
+ * @author normenhansen
+ */
+public interface PhysicsCollisionGroupListener {
+
+ /**
+ * Called when two physics objects of the registered group are about to collide, <i>called from physics thread</i>.<br>
+ * This is only called when the collision will happen based on the collisionGroup and collideWithGroups
+ * settings in the PhysicsCollisionObject. That is the case when <b>one</b> of the partys has the
+ * collisionGroup of the other in its collideWithGroups set.<br>
+ * @param nodeA CollisionObject #1
+ * @param nodeB CollisionObject #2
+ * @return true if the collision should happen, false otherwise
+ */
+ public boolean collide(PhysicsCollisionObject nodeA, PhysicsCollisionObject nodeB);
+
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionListener.java b/engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionListener.java
new file mode 100644
index 0000000..2739c04
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/collision/PhysicsCollisionListener.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+/**
+ * Interface for Objects that want to be informed about collision events in the physics space
+ * @author normenhansen
+ */
+public interface PhysicsCollisionListener {
+
+ /**
+ * Called when a collision happened in the PhysicsSpace, <i>called from render thread</i>.<br/>
+ * Do not store the event object as it will be cleared after the method has finished.
+ * @param event the CollisionEvent
+ */
+ public void collision(PhysicsCollisionEvent event);
+
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/collision/RagdollCollisionListener.java b/engine/src/bullet-common/com/jme3/bullet/collision/RagdollCollisionListener.java
new file mode 100644
index 0000000..44805a4
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/collision/RagdollCollisionListener.java
@@ -0,0 +1,17 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.animation.Bone;
+
+/**
+ *
+ * @author Nehon
+ */
+public interface RagdollCollisionListener {
+
+ public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event);
+
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java b/engine/src/bullet-common/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java
new file mode 100644
index 0000000..0223018
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java
@@ -0,0 +1,46 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision.shapes.infos;
+
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.export.*;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class ChildCollisionShape implements Savable {
+
+ public Vector3f location;
+ public Matrix3f rotation;
+ public CollisionShape shape;
+
+ public ChildCollisionShape() {
+ }
+
+ public ChildCollisionShape(Vector3f location, Matrix3f rotation, CollisionShape shape) {
+ this.location = location;
+ this.rotation = rotation;
+ this.shape = shape;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(location, "location", new Vector3f());
+ capsule.write(rotation, "rotation", new Matrix3f());
+ capsule.write(shape, "shape", new BoxCollisionShape(new Vector3f(1, 1, 1)));
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ location = (Vector3f) capsule.readSavable("location", new Vector3f());
+ rotation = (Matrix3f) capsule.readSavable("rotation", new Matrix3f());
+ shape = (CollisionShape) capsule.readSavable("shape", new BoxCollisionShape(new Vector3f(1, 1, 1)));
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/CharacterControl.java b/engine/src/bullet-common/com/jme3/bullet/control/CharacterControl.java
new file mode 100644
index 0000000..2acac53
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/CharacterControl.java
@@ -0,0 +1,208 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.objects.PhysicsCharacter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import java.io.IOException;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class CharacterControl extends PhysicsCharacter implements PhysicsControl {
+
+ protected Spatial spatial;
+ protected boolean enabled = true;
+ protected boolean added = false;
+ protected PhysicsSpace space = null;
+ protected Vector3f viewDirection = new Vector3f(Vector3f.UNIT_Z);
+ protected boolean useViewDirection = true;
+ protected boolean applyLocal = false;
+
+ public CharacterControl() {
+ }
+
+ public CharacterControl(CollisionShape shape, float stepHeight) {
+ super(shape, stepHeight);
+ }
+
+ public boolean isApplyPhysicsLocal() {
+ return applyLocal;
+ }
+
+ /**
+ * When set to true, the physics coordinates will be applied to the local
+ * translation of the Spatial
+ * @param applyPhysicsLocal
+ */
+ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
+ applyLocal = applyPhysicsLocal;
+ }
+
+ private Vector3f getSpatialTranslation() {
+ if (applyLocal) {
+ return spatial.getLocalTranslation();
+ }
+ return spatial.getWorldTranslation();
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ CharacterControl control = new CharacterControl(collisionShape, stepHeight);
+ control.setCcdMotionThreshold(getCcdMotionThreshold());
+ control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
+ control.setCollideWithGroups(getCollideWithGroups());
+ control.setCollisionGroup(getCollisionGroup());
+ control.setFallSpeed(getFallSpeed());
+ control.setGravity(getGravity());
+ control.setJumpSpeed(getJumpSpeed());
+ control.setMaxSlope(getMaxSlope());
+ control.setPhysicsLocation(getPhysicsLocation());
+ control.setUpAxis(getUpAxis());
+ control.setApplyPhysicsLocal(isApplyPhysicsLocal());
+
+ control.setSpatial(spatial);
+ return control;
+ }
+
+ public void setSpatial(Spatial spatial) {
+ if (getUserObject() == null || getUserObject() == this.spatial) {
+ setUserObject(spatial);
+ }
+ this.spatial = spatial;
+ if (spatial == null) {
+ if (getUserObject() == spatial) {
+ setUserObject(null);
+ }
+ return;
+ }
+ setPhysicsLocation(getSpatialTranslation());
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (space != null) {
+ if (enabled && !added) {
+ if (spatial != null) {
+ warp(getSpatialTranslation());
+ }
+ space.addCollisionObject(this);
+ added = true;
+ } else if (!enabled && added) {
+ space.removeCollisionObject(this);
+ added = false;
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setViewDirection(Vector3f vec) {
+ viewDirection.set(vec);
+ }
+
+ public Vector3f getViewDirection() {
+ return viewDirection;
+ }
+
+ public boolean isUseViewDirection() {
+ return useViewDirection;
+ }
+
+ public void setUseViewDirection(boolean viewDirectionEnabled) {
+ this.useViewDirection = viewDirectionEnabled;
+ }
+
+ public void update(float tpf) {
+ if (enabled && spatial != null) {
+ Quaternion localRotationQuat = spatial.getLocalRotation();
+ Vector3f localLocation = spatial.getLocalTranslation();
+ if (!applyLocal && spatial.getParent() != null) {
+ getPhysicsLocation(localLocation);
+ localLocation.subtractLocal(spatial.getParent().getWorldTranslation());
+ localLocation.divideLocal(spatial.getParent().getWorldScale());
+ tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
+ spatial.setLocalTranslation(localLocation);
+
+ if (useViewDirection) {
+ localRotationQuat.lookAt(viewDirection, Vector3f.UNIT_Y);
+ spatial.setLocalRotation(localRotationQuat);
+ }
+ } else {
+ spatial.setLocalTranslation(getPhysicsLocation());
+ localRotationQuat.lookAt(viewDirection, Vector3f.UNIT_Y);
+ spatial.setLocalRotation(localRotationQuat);
+ }
+ }
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ if (enabled && space != null && space.getDebugManager() != null) {
+ if (debugShape == null) {
+ attachDebugShape(space.getDebugManager());
+ }
+ debugShape.setLocalTranslation(getPhysicsLocation());
+ debugShape.updateLogicalState(0);
+ debugShape.updateGeometricState();
+ rm.renderScene(debugShape, vp);
+ }
+ }
+
+ public void setPhysicsSpace(PhysicsSpace space) {
+ if (space == null) {
+ if (this.space != null) {
+ this.space.removeCollisionObject(this);
+ added = false;
+ }
+ } else {
+ if (this.space == space) {
+ return;
+ }
+ space.addCollisionObject(this);
+ added = true;
+ }
+ this.space = space;
+ }
+
+ public PhysicsSpace getPhysicsSpace() {
+ return space;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(enabled, "enabled", true);
+ oc.write(applyLocal, "applyLocalPhysics", false);
+ oc.write(useViewDirection, "viewDirectionEnabled", true);
+ oc.write(viewDirection, "viewDirection", new Vector3f(Vector3f.UNIT_Z));
+ oc.write(spatial, "spatial", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ enabled = ic.readBoolean("enabled", true);
+ useViewDirection = ic.readBoolean("viewDirectionEnabled", true);
+ viewDirection = (Vector3f) ic.readSavable("viewDirection", new Vector3f(Vector3f.UNIT_Z));
+ applyLocal = ic.readBoolean("applyLocalPhysics", false);
+ spatial = (Spatial) ic.readSavable("spatial", null);
+ setUserObject(spatial);
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/GhostControl.java b/engine/src/bullet-common/com/jme3/bullet/control/GhostControl.java
new file mode 100644
index 0000000..99e5984
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/GhostControl.java
@@ -0,0 +1,178 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.objects.PhysicsGhostObject;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import java.io.IOException;
+
+/**
+ * A GhostControl moves with the spatial it is attached to and can be used to check
+ * overlaps with other physics objects (e.g. aggro radius).
+ * @author normenhansen
+ */
+public class GhostControl extends PhysicsGhostObject implements PhysicsControl {
+
+ protected Spatial spatial;
+ protected boolean enabled = true;
+ protected boolean added = false;
+ protected PhysicsSpace space = null;
+ protected boolean applyLocal = false;
+
+ public GhostControl() {
+ }
+
+ public GhostControl(CollisionShape shape) {
+ super(shape);
+ }
+
+ public boolean isApplyPhysicsLocal() {
+ return applyLocal;
+ }
+
+ /**
+ * When set to true, the physics coordinates will be applied to the local
+ * translation of the Spatial
+ * @param applyPhysicsLocal
+ */
+ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
+ applyLocal = applyPhysicsLocal;
+ }
+
+ private Vector3f getSpatialTranslation() {
+ if (applyLocal) {
+ return spatial.getLocalTranslation();
+ }
+ return spatial.getWorldTranslation();
+ }
+
+ private Quaternion getSpatialRotation() {
+ if (applyLocal) {
+ return spatial.getLocalRotation();
+ }
+ return spatial.getWorldRotation();
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ GhostControl control = new GhostControl(collisionShape);
+ control.setCcdMotionThreshold(getCcdMotionThreshold());
+ control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
+ control.setCollideWithGroups(getCollideWithGroups());
+ control.setCollisionGroup(getCollisionGroup());
+ control.setPhysicsLocation(getPhysicsLocation());
+ control.setPhysicsRotation(getPhysicsRotationMatrix());
+ control.setApplyPhysicsLocal(isApplyPhysicsLocal());
+
+ control.setSpatial(spatial);
+ return control;
+ }
+
+ public void setSpatial(Spatial spatial) {
+ if (getUserObject() == null || getUserObject() == this.spatial) {
+ setUserObject(spatial);
+ }
+ this.spatial = spatial;
+ if (spatial == null) {
+ if (getUserObject() == spatial) {
+ setUserObject(null);
+ }
+ return;
+ }
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (space != null) {
+ if (enabled && !added) {
+ if (spatial != null) {
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+ space.addCollisionObject(this);
+ added = true;
+ } else if (!enabled && added) {
+ space.removeCollisionObject(this);
+ added = false;
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void update(float tpf) {
+ if (!enabled) {
+ return;
+ }
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ if (enabled && space != null && space.getDebugManager() != null) {
+ if (debugShape == null) {
+ attachDebugShape(space.getDebugManager());
+ }
+ debugShape.setLocalTranslation(spatial.getWorldTranslation());
+ debugShape.setLocalRotation(spatial.getWorldRotation());
+ debugShape.updateLogicalState(0);
+ debugShape.updateGeometricState();
+ rm.renderScene(debugShape, vp);
+ }
+ }
+
+ public void setPhysicsSpace(PhysicsSpace space) {
+ if (space == null) {
+ if (this.space != null) {
+ this.space.removeCollisionObject(this);
+ added = false;
+ }
+ } else {
+ if (this.space == space) {
+ return;
+ }
+ space.addCollisionObject(this);
+ added = true;
+ }
+ this.space = space;
+ }
+
+ public PhysicsSpace getPhysicsSpace() {
+ return space;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(enabled, "enabled", true);
+ oc.write(applyLocal, "applyLocalPhysics", false);
+ oc.write(spatial, "spatial", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ enabled = ic.readBoolean("enabled", true);
+ spatial = (Spatial) ic.readSavable("spatial", null);
+ applyLocal = ic.readBoolean("applyLocalPhysics", false);
+ setUserObject(spatial);
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java b/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java
new file mode 100644
index 0000000..1da0442
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java
@@ -0,0 +1,867 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.control;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionListener;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.RagdollCollisionListener;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.HullCollisionShape;
+import com.jme3.bullet.control.ragdoll.HumanoidRagdollPreset;
+import com.jme3.bullet.control.ragdoll.RagdollPreset;
+import com.jme3.bullet.control.ragdoll.RagdollUtils;
+import com.jme3.bullet.joints.SixDofJoint;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**<strong>This control is still a WIP, use it at your own risk</strong><br>
+ * To use this control you need a model with an AnimControl and a SkeletonControl.<br>
+ * This should be the case if you imported an animated model from Ogre or blender.<br>
+ * Note enabling/disabling the control add/removes it from the physic space<br>
+ * <p>
+ * This control creates collision shapes for each bones of the skeleton when you call spatial.addControl(ragdollControl).
+ * <ul>
+ * <li>The shape is HullCollision shape based on the vertices associated with each bone and based on a tweakable weight threshold (see setWeightThreshold)</li>
+ * <li>If you don't want each bone to be a collision shape, you can specify what bone to use by using the addBoneName method<br>
+ * By using this method, bone that are not used to create a shape, are "merged" to their parent to create the collision shape.
+ * </li>
+ * </ul>
+ *</p>
+ *<p>
+ *There are 2 modes for this control :
+ * <ul>
+ * <li><strong>The kinematic modes :</strong><br>
+ * this is the default behavior, this means that the collision shapes of the body are able to interact with physics enabled objects.
+ * in this mode physic shapes follow the moovements of the animated skeleton (for example animated by a key framed animation)
+ * this mode is enabled by calling setKinematicMode();
+ * </li>
+ * <li><strong>The ragdoll modes :</strong><br>
+ * To enable this behavior, you need to call setRagdollMode() method.
+ * In this mode the charater is entirely controled by physics, so it will fall under the gravity and move if any force is applied to it.
+ * </li>
+ * </ul>
+ *</p>
+ *
+ * @author Normen Hansen and Rémy Bouquet (Nehon)
+ */
+public class KinematicRagdollControl implements PhysicsControl, PhysicsCollisionListener {
+
+ protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName());
+ protected Map<String, PhysicsBoneLink> boneLinks = new HashMap<String, PhysicsBoneLink>();
+ protected Skeleton skeleton;
+ protected PhysicsSpace space;
+ protected boolean enabled = true;
+ protected boolean debug = false;
+ protected PhysicsRigidBody baseRigidBody;
+ protected float weightThreshold = -1.0f;
+ protected Spatial targetModel;
+ protected Vector3f initScale;
+ protected Mode mode = Mode.Kinetmatic;
+ protected boolean blendedControl = false;
+ protected float blendTime = 1.0f;
+ protected float blendStart = 0.0f;
+ protected List<RagdollCollisionListener> listeners;
+ protected float eventDispatchImpulseThreshold = 10;
+ protected RagdollPreset preset = new HumanoidRagdollPreset();
+ protected Set<String> boneList = new TreeSet<String>();
+ protected Vector3f modelPosition = new Vector3f();
+ protected Quaternion modelRotation = new Quaternion();
+ protected float rootMass = 15;
+ protected float totalMass = 0;
+ protected boolean added = false;
+
+ public static enum Mode {
+
+ Kinetmatic,
+ Ragdoll
+ }
+
+ protected class PhysicsBoneLink {
+
+ protected Bone bone;
+ protected Quaternion initalWorldRotation;
+ protected SixDofJoint joint;
+ protected PhysicsRigidBody rigidBody;
+ protected Quaternion startBlendingRot = new Quaternion();
+ protected Vector3f startBlendingPos = new Vector3f();
+ }
+
+ /**
+ * contruct a KinematicRagdollControl
+ */
+ public KinematicRagdollControl() {
+ }
+
+ public KinematicRagdollControl(float weightThreshold) {
+ this.weightThreshold = weightThreshold;
+ }
+
+ public KinematicRagdollControl(RagdollPreset preset, float weightThreshold) {
+ this.preset = preset;
+ this.weightThreshold = weightThreshold;
+ }
+
+ public KinematicRagdollControl(RagdollPreset preset) {
+ this.preset = preset;
+ }
+
+ public void update(float tpf) {
+ if (!enabled) {
+ return;
+ }
+ TempVars vars = TempVars.get();
+
+ Quaternion tmpRot1 = vars.quat1;
+ Quaternion tmpRot2 = vars.quat2;
+
+ //if the ragdoll has the control of the skeleton, we update each bone with its position in physic world space.
+ if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
+ for (PhysicsBoneLink link : boneLinks.values()) {
+
+ Vector3f position = vars.vect1;
+
+ //retrieving bone position in physic world space
+ Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
+ //transforming this position with inverse transforms of the model
+ targetModel.getWorldTransform().transformInverseVector(p, position);
+
+ //retrieving bone rotation in physic world space
+ Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
+
+ //multiplying this rotation by the initialWorld rotation of the bone,
+ //then transforming it with the inverse world rotation of the model
+ tmpRot1.set(q).multLocal(link.initalWorldRotation);
+ tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1);
+ tmpRot1.normalizeLocal();
+
+ //if the bone is the root bone, we apply the physic's transform to the model, so its position and rotation are correctly updated
+ if (link.bone.getParent() == null) {
+
+ //offsetting the physic's position/rotation by the root bone inverse model space position/rotaion
+ modelPosition.set(p).subtractLocal(link.bone.getWorldBindPosition());
+ targetModel.getParent().getWorldTransform().transformInverseVector(modelPosition, modelPosition);
+ modelRotation.set(q).multLocal(tmpRot2.set(link.bone.getWorldBindRotation()).inverseLocal());
+
+
+ //applying transforms to the model
+ targetModel.setLocalTranslation(modelPosition);
+
+ targetModel.setLocalRotation(modelRotation);
+
+ //Applying computed transforms to the bone
+ link.bone.setUserTransformsWorld(position, tmpRot1);
+
+ } else {
+ //if boneList is empty, this means that every bone in the ragdoll has a collision shape,
+ //so we just update the bone position
+ if (boneList.isEmpty()) {
+ link.bone.setUserTransformsWorld(position, tmpRot1);
+ } else {
+ //boneList is not empty, this means some bones of the skeleton might not be associated with a collision shape.
+ //So we update them recusively
+ RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
+ }
+ }
+ }
+ } else {
+ //the ragdoll does not have the controll, so the keyframed animation updates the physic position of the physic bonces
+ for (PhysicsBoneLink link : boneLinks.values()) {
+
+ Vector3f position = vars.vect1;
+
+ //if blended control this means, keyframed animation is updating the skeleton,
+ //but to allow smooth transition, we blend this transformation with the saved position of the ragdoll
+ if (blendedControl) {
+ Vector3f position2 = vars.vect2;
+ //initializing tmp vars with the start position/rotation of the ragdoll
+ position.set(link.startBlendingPos);
+ tmpRot1.set(link.startBlendingRot);
+
+ //interpolating between ragdoll position/rotation and keyframed position/rotation
+ tmpRot2.set(tmpRot1).nlerp(link.bone.getModelSpaceRotation(), blendStart / blendTime);
+ position2.set(position).interpolate(link.bone.getModelSpacePosition(), blendStart / blendTime);
+ tmpRot1.set(tmpRot2);
+ position.set(position2);
+
+ //updating bones transforms
+ if (boneList.isEmpty()) {
+ //we ensure we have the control to update the bone
+ link.bone.setUserControl(true);
+ link.bone.setUserTransformsWorld(position, tmpRot1);
+ //we give control back to the key framed animation.
+ link.bone.setUserControl(false);
+ } else {
+ RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
+ }
+
+ }
+ //setting skeleton transforms to the ragdoll
+ matchPhysicObjectToBone(link, position, tmpRot1);
+ modelPosition.set(targetModel.getLocalTranslation());
+
+ }
+
+ //time control for blending
+ if (blendedControl) {
+ blendStart += tpf;
+ if (blendStart > blendTime) {
+ blendedControl = false;
+ }
+ }
+ }
+ vars.release();
+
+ }
+
+ /**
+ * Set the transforms of a rigidBody to match the transforms of a bone.
+ * this is used to make the ragdoll follow the skeleton motion while in Kinematic mode
+ * @param link the link containing the bone and the rigidBody
+ * @param position just a temp vector for position
+ * @param tmpRot1 just a temp quaternion for rotation
+ */
+ private void matchPhysicObjectToBone(PhysicsBoneLink link, Vector3f position, Quaternion tmpRot1) {
+ //computing position from rotation and scale
+ targetModel.getWorldTransform().transformVector(link.bone.getModelSpacePosition(), position);
+
+ //computing rotation
+ tmpRot1.set(link.bone.getModelSpaceRotation()).multLocal(link.bone.getWorldBindInverseRotation());
+ targetModel.getWorldRotation().mult(tmpRot1, tmpRot1);
+ tmpRot1.normalizeLocal();
+
+ //updating physic location/rotation of the physic bone
+ link.rigidBody.setPhysicsLocation(position);
+ link.rigidBody.setPhysicsRotation(tmpRot1);
+
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ /**
+ * rebuild the ragdoll
+ * this is useful if you applied scale on the ragdoll after it's been initialized
+ */
+ public void reBuild() {
+ setSpatial(targetModel);
+ addToPhysicsSpace();
+ }
+
+ public void setSpatial(Spatial model) {
+ if (model == null) {
+ removeFromPhysicsSpace();
+ clearData();
+ return;
+ }
+ targetModel = model;
+ Node parent = model.getParent();
+
+
+ Vector3f initPosition = model.getLocalTranslation().clone();
+ Quaternion initRotation = model.getLocalRotation().clone();
+ initScale = model.getLocalScale().clone();
+
+ model.removeFromParent();
+ model.setLocalTranslation(Vector3f.ZERO);
+ model.setLocalRotation(Quaternion.IDENTITY);
+ model.setLocalScale(1);
+ //HACK ALERT change this
+ //I remove the skeletonControl and readd it to the spatial to make sure it's after the ragdollControl in the stack
+ //Find a proper way to order the controls.
+ SkeletonControl sc = model.getControl(SkeletonControl.class);
+ model.removeControl(sc);
+ model.addControl(sc);
+ //----
+
+ removeFromPhysicsSpace();
+ clearData();
+ // put into bind pose and compute bone transforms in model space
+ // maybe dont reset to ragdoll out of animations?
+ scanSpatial(model);
+
+
+ if (parent != null) {
+ parent.attachChild(model);
+
+ }
+ model.setLocalTranslation(initPosition);
+ model.setLocalRotation(initRotation);
+ model.setLocalScale(initScale);
+
+ logger.log(Level.INFO, "Created physics ragdoll for skeleton {0}", skeleton);
+ }
+
+ /**
+ * Add a bone name to this control
+ * Using this method you can specify which bones of the skeleton will be used to build the collision shapes.
+ * @param name
+ */
+ public void addBoneName(String name) {
+ boneList.add(name);
+ }
+
+ private void scanSpatial(Spatial model) {
+ AnimControl animControl = model.getControl(AnimControl.class);
+ Map<Integer, List<Float>> pointsMap = null;
+ if (weightThreshold == -1.0f) {
+ pointsMap = RagdollUtils.buildPointMap(model);
+ }
+
+ skeleton = animControl.getSkeleton();
+ skeleton.resetAndUpdate();
+ for (int i = 0; i < skeleton.getRoots().length; i++) {
+ Bone childBone = skeleton.getRoots()[i];
+ if (childBone.getParent() == null) {
+ logger.log(Level.INFO, "Found root bone in skeleton {0}", skeleton);
+ baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1);
+ baseRigidBody.setKinematic(mode == Mode.Kinetmatic);
+ boneRecursion(model, childBone, baseRigidBody, 1, pointsMap);
+ }
+ }
+ }
+
+ private void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map<Integer, List<Float>> pointsMap) {
+ PhysicsRigidBody parentShape = parent;
+ if (boneList.isEmpty() || boneList.contains(bone.getName())) {
+
+ PhysicsBoneLink link = new PhysicsBoneLink();
+ link.bone = bone;
+
+ //creating the collision shape
+ HullCollisionShape shape = null;
+ if (pointsMap != null) {
+ //build a shape for the bone, using the vertices that are most influenced by this bone
+ shape = RagdollUtils.makeShapeFromPointMap(pointsMap, RagdollUtils.getBoneIndices(link.bone, skeleton, boneList), initScale, link.bone.getModelSpacePosition());
+ } else {
+ //build a shape for the bone, using the vertices associated with this bone with a weight above the threshold
+ shape = RagdollUtils.makeShapeFromVerticeWeights(model, RagdollUtils.getBoneIndices(link.bone, skeleton, boneList), initScale, link.bone.getModelSpacePosition(), weightThreshold);
+ }
+
+ PhysicsRigidBody shapeNode = new PhysicsRigidBody(shape, rootMass / (float) reccount);
+
+ shapeNode.setKinematic(mode == Mode.Kinetmatic);
+ totalMass += rootMass / (float) reccount;
+
+ link.rigidBody = shapeNode;
+ link.initalWorldRotation = bone.getModelSpaceRotation().clone();
+
+ if (parent != null) {
+ //get joint position for parent
+ Vector3f posToParent = new Vector3f();
+ if (bone.getParent() != null) {
+ bone.getModelSpacePosition().subtract(bone.getParent().getModelSpacePosition(), posToParent).multLocal(initScale);
+ }
+
+ SixDofJoint joint = new SixDofJoint(parent, shapeNode, posToParent, new Vector3f(0, 0, 0f), true);
+ preset.setupJointForBone(bone.getName(), joint);
+
+ link.joint = joint;
+ joint.setCollisionBetweenLinkedBodys(false);
+ }
+ boneLinks.put(bone.getName(), link);
+ shapeNode.setUserObject(link);
+ parentShape = shapeNode;
+ }
+
+ for (Iterator<Bone> it = bone.getChildren().iterator(); it.hasNext();) {
+ Bone childBone = it.next();
+ boneRecursion(model, childBone, parentShape, reccount + 1, pointsMap);
+ }
+ }
+
+ /**
+ * Set the joint limits for the joint between the given bone and its parent.
+ * This method can't work before attaching the control to a spatial
+ * @param boneName the name of the bone
+ * @param maxX the maximum rotation on the x axis (in radians)
+ * @param minX the minimum rotation on the x axis (in radians)
+ * @param maxY the maximum rotation on the y axis (in radians)
+ * @param minY the minimum rotation on the z axis (in radians)
+ * @param maxZ the maximum rotation on the z axis (in radians)
+ * @param minZ the minimum rotation on the z axis (in radians)
+ */
+ public void setJointLimit(String boneName, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
+ PhysicsBoneLink link = boneLinks.get(boneName);
+ if (link != null) {
+ RagdollUtils.setJointLimit(link.joint, maxX, minX, maxY, minY, maxZ, minZ);
+ } else {
+ logger.log(Level.WARNING, "Not joint was found for bone {0}. make sure you call spatial.addControl(ragdoll) before setting joints limit", boneName);
+ }
+ }
+
+ /**
+ * Return the joint between the given bone and its parent.
+ * This return null if it's called before attaching the control to a spatial
+ * @param boneName the name of the bone
+ * @return the joint between the given bone and its parent
+ */
+ public SixDofJoint getJoint(String boneName) {
+ PhysicsBoneLink link = boneLinks.get(boneName);
+ if (link != null) {
+ return link.joint;
+ } else {
+ logger.log(Level.WARNING, "Not joint was found for bone {0}. make sure you call spatial.addControl(ragdoll) before setting joints limit", boneName);
+ return null;
+ }
+ }
+
+ private void clearData() {
+ boneLinks.clear();
+ baseRigidBody = null;
+ }
+
+ private void addToPhysicsSpace() {
+ if (space == null) {
+ return;
+ }
+ if (baseRigidBody != null) {
+ space.add(baseRigidBody);
+ added = true;
+ }
+ for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
+ PhysicsBoneLink physicsBoneLink = it.next();
+ if (physicsBoneLink.rigidBody != null) {
+ space.add(physicsBoneLink.rigidBody);
+ if (physicsBoneLink.joint != null) {
+ space.add(physicsBoneLink.joint);
+
+ }
+ added = true;
+ }
+ }
+ }
+
+ protected void removeFromPhysicsSpace() {
+ if (space == null) {
+ return;
+ }
+ if (baseRigidBody != null) {
+ space.remove(baseRigidBody);
+ }
+ for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
+ PhysicsBoneLink physicsBoneLink = it.next();
+ if (physicsBoneLink.joint != null) {
+ space.remove(physicsBoneLink.joint);
+ if (physicsBoneLink.rigidBody != null) {
+ space.remove(physicsBoneLink.rigidBody);
+ }
+ }
+ }
+ added = false;
+ }
+
+ /**
+ * enable or disable the control
+ * note that if enabled is true and that the physic space has been set on the ragdoll, the ragdoll is added to the physic space
+ * if enabled is false the ragdoll is removed from physic space.
+ * @param enabled
+ */
+ public void setEnabled(boolean enabled) {
+ if (this.enabled == enabled) {
+ return;
+ }
+ this.enabled = enabled;
+ if (!enabled && space != null) {
+ removeFromPhysicsSpace();
+ } else if (enabled && space != null) {
+ addToPhysicsSpace();
+ }
+ }
+
+ /**
+ * returns true if the control is enabled
+ * @return
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ protected void attachDebugShape(AssetManager manager) {
+ for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
+ PhysicsBoneLink physicsBoneLink = it.next();
+ physicsBoneLink.rigidBody.createDebugShape(manager);
+ }
+ debug = true;
+ }
+
+ protected void detachDebugShape() {
+ for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
+ PhysicsBoneLink physicsBoneLink = it.next();
+ physicsBoneLink.rigidBody.detachDebugShape();
+ }
+ debug = false;
+ }
+
+ /**
+ * For internal use only
+ * specific render for the ragdoll(if debugging)
+ * @param rm
+ * @param vp
+ */
+ public void render(RenderManager rm, ViewPort vp) {
+ if (enabled && space != null && space.getDebugManager() != null) {
+ if (!debug) {
+ attachDebugShape(space.getDebugManager());
+ }
+ for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
+ PhysicsBoneLink physicsBoneLink = it.next();
+ Spatial debugShape = physicsBoneLink.rigidBody.debugShape();
+ if (debugShape != null) {
+ debugShape.setLocalTranslation(physicsBoneLink.rigidBody.getMotionState().getWorldLocation());
+ debugShape.setLocalRotation(physicsBoneLink.rigidBody.getMotionState().getWorldRotationQuat());
+ debugShape.updateGeometricState();
+ rm.renderScene(debugShape, vp);
+ }
+ }
+ }
+ }
+
+ /**
+ * set the physic space to this ragdoll
+ * @param space
+ */
+ public void setPhysicsSpace(PhysicsSpace space) {
+ if (space == null) {
+ removeFromPhysicsSpace();
+ this.space = space;
+ } else {
+ if (this.space == space) {
+ return;
+ }
+ this.space = space;
+ addToPhysicsSpace();
+ this.space.addCollisionListener(this);
+ }
+ }
+
+ /**
+ * returns the physic space
+ * @return
+ */
+ public PhysicsSpace getPhysicsSpace() {
+ return space;
+ }
+
+ /**
+ * serialize this control
+ * @param ex
+ * @throws IOException
+ */
+ public void write(JmeExporter ex) throws IOException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ /**
+ * de-serialize this control
+ * @param im
+ * @throws IOException
+ */
+ public void read(JmeImporter im) throws IOException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ /**
+ * For internal use only
+ * callback for collisionevent
+ * @param event
+ */
+ public void collision(PhysicsCollisionEvent event) {
+ PhysicsCollisionObject objA = event.getObjectA();
+ PhysicsCollisionObject objB = event.getObjectB();
+
+ //excluding collisions that involve 2 parts of the ragdoll
+ if (event.getNodeA() == null && event.getNodeB() == null) {
+ return;
+ }
+
+ //discarding low impulse collision
+ if (event.getAppliedImpulse() < eventDispatchImpulseThreshold) {
+ return;
+ }
+
+ boolean hit = false;
+ Bone hitBone = null;
+ PhysicsCollisionObject hitObject = null;
+
+ //Computing which bone has been hit
+ if (objA.getUserObject() instanceof PhysicsBoneLink) {
+ PhysicsBoneLink link = (PhysicsBoneLink) objA.getUserObject();
+ if (link != null) {
+ hit = true;
+ hitBone = link.bone;
+ hitObject = objB;
+ }
+ }
+
+ if (objB.getUserObject() instanceof PhysicsBoneLink) {
+ PhysicsBoneLink link = (PhysicsBoneLink) objB.getUserObject();
+ if (link != null) {
+ hit = true;
+ hitBone = link.bone;
+ hitObject = objA;
+
+ }
+ }
+
+ //dispatching the event if the ragdoll has been hit
+ if (hit && listeners != null) {
+ for (RagdollCollisionListener listener : listeners) {
+ listener.collide(hitBone, hitObject, event);
+ }
+ }
+
+ }
+
+ /**
+ * Enable or disable the ragdoll behaviour.
+ * if ragdollEnabled is true, the character motion will only be powerd by physics
+ * else, the characted will be animated by the keyframe animation,
+ * but will be able to physically interact with its physic environnement
+ * @param ragdollEnabled
+ */
+ protected void setMode(Mode mode) {
+ this.mode = mode;
+ AnimControl animControl = targetModel.getControl(AnimControl.class);
+ animControl.setEnabled(mode == Mode.Kinetmatic);
+
+ baseRigidBody.setKinematic(mode == Mode.Kinetmatic);
+ TempVars vars = TempVars.get();
+
+ for (PhysicsBoneLink link : boneLinks.values()) {
+ link.rigidBody.setKinematic(mode == Mode.Kinetmatic);
+ if (mode == Mode.Ragdoll) {
+ Quaternion tmpRot1 = vars.quat1;
+ Vector3f position = vars.vect1;
+ //making sure that the ragdoll is at the correct place.
+ matchPhysicObjectToBone(link, position, tmpRot1);
+ }
+
+ }
+ vars.release();
+
+ for (Bone bone : skeleton.getRoots()) {
+ RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
+ }
+ }
+
+ /**
+ * Smoothly blend from Ragdoll mode to Kinematic mode
+ * This is useful to blend ragdoll actual position to a keyframe animation for example
+ * @param blendTime the blending time between ragdoll to anim.
+ */
+ public void blendToKinematicMode(float blendTime) {
+ if (mode == Mode.Kinetmatic) {
+ return;
+ }
+ blendedControl = true;
+ this.blendTime = blendTime;
+ mode = Mode.Kinetmatic;
+ AnimControl animControl = targetModel.getControl(AnimControl.class);
+ animControl.setEnabled(true);
+
+
+ TempVars vars = TempVars.get();
+ for (PhysicsBoneLink link : boneLinks.values()) {
+
+ Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
+ Vector3f position = vars.vect1;
+
+ targetModel.getWorldTransform().transformInverseVector(p, position);
+
+ Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
+ Quaternion q2 = vars.quat1;
+ Quaternion q3 = vars.quat2;
+
+ q2.set(q).multLocal(link.initalWorldRotation).normalizeLocal();
+ q3.set(targetModel.getWorldRotation()).inverseLocal().mult(q2, q2);
+ q2.normalizeLocal();
+ link.startBlendingPos.set(position);
+ link.startBlendingRot.set(q2);
+ link.rigidBody.setKinematic(true);
+ }
+ vars.release();
+
+ for (Bone bone : skeleton.getRoots()) {
+ RagdollUtils.setUserControl(bone, false);
+ }
+
+ blendStart = 0;
+ }
+
+ /**
+ * Set the control into Kinematic mode
+ * In theis mode, the collision shapes follow the movements of the skeleton,
+ * and can interact with physical environement
+ */
+ public void setKinematicMode() {
+ if (mode != Mode.Kinetmatic) {
+ setMode(Mode.Kinetmatic);
+ }
+ }
+
+ /**
+ * Sets the control into Ragdoll mode
+ * The skeleton is entirely controlled by physics.
+ */
+ public void setRagdollMode() {
+ if (mode != Mode.Ragdoll) {
+ setMode(Mode.Ragdoll);
+ }
+ }
+
+ /**
+ * retruns the mode of this control
+ * @return
+ */
+ public Mode getMode() {
+ return mode;
+ }
+
+ /**
+ * add a
+ * @param listener
+ */
+ public void addCollisionListener(RagdollCollisionListener listener) {
+ if (listeners == null) {
+ listeners = new ArrayList<RagdollCollisionListener>();
+ }
+ listeners.add(listener);
+ }
+
+ public void setRootMass(float rootMass) {
+ this.rootMass = rootMass;
+ }
+
+ public float getTotalMass() {
+ return totalMass;
+ }
+
+ public float getWeightThreshold() {
+ return weightThreshold;
+ }
+
+ public void setWeightThreshold(float weightThreshold) {
+ this.weightThreshold = weightThreshold;
+ }
+
+ public float getEventDispatchImpulseThreshold() {
+ return eventDispatchImpulseThreshold;
+ }
+
+ public void setEventDispatchImpulseThreshold(float eventDispatchImpulseThreshold) {
+ this.eventDispatchImpulseThreshold = eventDispatchImpulseThreshold;
+ }
+
+ /**
+ * Set the CcdMotionThreshold of all the bone's rigidBodies of the ragdoll
+ * @see PhysicsRigidBody#setCcdMotionThreshold(float)
+ * @param value
+ */
+ public void setCcdMotionThreshold(float value) {
+ for (PhysicsBoneLink link : boneLinks.values()) {
+ link.rigidBody.setCcdMotionThreshold(value);
+ }
+ }
+
+ /**
+ * Set the CcdSweptSphereRadius of all the bone's rigidBodies of the ragdoll
+ * @see PhysicsRigidBody#setCcdSweptSphereRadius(float)
+ * @param value
+ */
+ public void setCcdSweptSphereRadius(float value) {
+ for (PhysicsBoneLink link : boneLinks.values()) {
+ link.rigidBody.setCcdSweptSphereRadius(value);
+ }
+ }
+
+ /**
+ * Set the CcdMotionThreshold of the given bone's rigidBodies of the ragdoll
+ * @see PhysicsRigidBody#setCcdMotionThreshold(float)
+ * @param value
+ * @deprecated use getBoneRigidBody(String BoneName).setCcdMotionThreshold(float) instead
+ */
+ @Deprecated
+ public void setBoneCcdMotionThreshold(String boneName, float value) {
+ PhysicsBoneLink link = boneLinks.get(boneName);
+ if (link != null) {
+ link.rigidBody.setCcdMotionThreshold(value);
+ }
+ }
+
+ /**
+ * Set the CcdSweptSphereRadius of the given bone's rigidBodies of the ragdoll
+ * @see PhysicsRigidBody#setCcdSweptSphereRadius(float)
+ * @param value
+ * @deprecated use getBoneRigidBody(String BoneName).setCcdSweptSphereRadius(float) instead
+ */
+ @Deprecated
+ public void setBoneCcdSweptSphereRadius(String boneName, float value) {
+ PhysicsBoneLink link = boneLinks.get(boneName);
+ if (link != null) {
+ link.rigidBody.setCcdSweptSphereRadius(value);
+ }
+ }
+
+ /**
+ * return the rigidBody associated to the given bone
+ * @param boneName the name of the bone
+ * @return the associated rigidBody.
+ */
+ public PhysicsRigidBody getBoneRigidBody(String boneName) {
+ PhysicsBoneLink link = boneLinks.get(boneName);
+ if (link != null) {
+ return link.rigidBody;
+ }
+ return null;
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/PhysicsControl.java b/engine/src/bullet-common/com/jme3/bullet/control/PhysicsControl.java
new file mode 100644
index 0000000..ba65157
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/PhysicsControl.java
@@ -0,0 +1,28 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.scene.control.Control;
+
+/**
+ *
+ * @author normenhansen
+ */
+public interface PhysicsControl extends Control {
+
+ public void setPhysicsSpace(PhysicsSpace space);
+
+ public PhysicsSpace getPhysicsSpace();
+
+ /**
+ * The physics object is removed from the physics space when the control
+ * is disabled. When the control is enabled again the physics object is
+ * moved to the current location of the spatial and then added to the physics
+ * space. This allows disabling/enabling physics to move the spatial freely.
+ * @param state
+ */
+ public void setEnabled(boolean state);
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/RigidBodyControl.java b/engine/src/bullet-common/com/jme3/bullet/control/RigidBodyControl.java
new file mode 100644
index 0000000..4fbce1e
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/RigidBodyControl.java
@@ -0,0 +1,264 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import java.io.IOException;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl {
+
+ protected Spatial spatial;
+ protected boolean enabled = true;
+ protected boolean added = false;
+ protected PhysicsSpace space = null;
+ protected boolean kinematicSpatial = true;
+
+ public RigidBodyControl() {
+ }
+
+ /**
+ * When using this constructor, the CollisionShape for the RigidBody is generated
+ * automatically when the Control is added to a Spatial.
+ * @param mass When not 0, a HullCollisionShape is generated, otherwise a MeshCollisionShape is used. For geometries with box or sphere meshes the proper box or sphere collision shape is used.
+ */
+ public RigidBodyControl(float mass) {
+ this.mass = mass;
+ }
+
+ /**
+ * Creates a new PhysicsNode with the supplied collision shape and mass 1
+ * @param shape
+ */
+ public RigidBodyControl(CollisionShape shape) {
+ super(shape);
+ }
+
+ public RigidBodyControl(CollisionShape shape, float mass) {
+ super(shape, mass);
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ RigidBodyControl control = new RigidBodyControl(collisionShape, mass);
+ control.setAngularFactor(getAngularFactor());
+ control.setAngularSleepingThreshold(getAngularSleepingThreshold());
+ control.setCcdMotionThreshold(getCcdMotionThreshold());
+ control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
+ control.setCollideWithGroups(getCollideWithGroups());
+ control.setCollisionGroup(getCollisionGroup());
+ control.setDamping(getLinearDamping(), getAngularDamping());
+ control.setFriction(getFriction());
+ control.setGravity(getGravity());
+ control.setKinematic(isKinematic());
+ control.setKinematicSpatial(isKinematicSpatial());
+ control.setLinearSleepingThreshold(getLinearSleepingThreshold());
+ control.setPhysicsLocation(getPhysicsLocation(null));
+ control.setPhysicsRotation(getPhysicsRotationMatrix(null));
+ control.setRestitution(getRestitution());
+
+ if (mass > 0) {
+ control.setAngularVelocity(getAngularVelocity());
+ control.setLinearVelocity(getLinearVelocity());
+ }
+ control.setApplyPhysicsLocal(isApplyPhysicsLocal());
+
+ control.setSpatial(spatial);
+ return control;
+ }
+
+ public void setSpatial(Spatial spatial) {
+ if (getUserObject() == null || getUserObject() == this.spatial) {
+ setUserObject(spatial);
+ }
+ this.spatial = spatial;
+ if (spatial == null) {
+ if (getUserObject() == spatial) {
+ setUserObject(null);
+ }
+ spatial = null;
+ collisionShape = null;
+ return;
+ }
+ if (collisionShape == null) {
+ createCollisionShape();
+ rebuildRigidBody();
+ }
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+
+ protected void createCollisionShape() {
+ if (spatial == null) {
+ return;
+ }
+ if (spatial instanceof Geometry) {
+ Geometry geom = (Geometry) spatial;
+ Mesh mesh = geom.getMesh();
+ if (mesh instanceof Sphere) {
+ collisionShape = new SphereCollisionShape(((Sphere) mesh).getRadius());
+ return;
+ } else if (mesh instanceof Box) {
+ collisionShape = new BoxCollisionShape(new Vector3f(((Box) mesh).getXExtent(), ((Box) mesh).getYExtent(), ((Box) mesh).getZExtent()));
+ return;
+ }
+ }
+ if (mass > 0) {
+ collisionShape = CollisionShapeFactory.createDynamicMeshShape(spatial);
+ } else {
+ collisionShape = CollisionShapeFactory.createMeshShape(spatial);
+ }
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (space != null) {
+ if (enabled && !added) {
+ if (spatial != null) {
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+ space.addCollisionObject(this);
+ added = true;
+ } else if (!enabled && added) {
+ space.removeCollisionObject(this);
+ added = false;
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Checks if this control is in kinematic spatial mode.
+ * @return true if the spatial location is applied to this kinematic rigidbody
+ */
+ public boolean isKinematicSpatial() {
+ return kinematicSpatial;
+ }
+
+ /**
+ * Sets this control to kinematic spatial mode so that the spatials transform will
+ * be applied to the rigidbody in kinematic mode, defaults to true.
+ * @param kinematicSpatial
+ */
+ public void setKinematicSpatial(boolean kinematicSpatial) {
+ this.kinematicSpatial = kinematicSpatial;
+ }
+
+ public boolean isApplyPhysicsLocal() {
+ return motionState.isApplyPhysicsLocal();
+ }
+
+ /**
+ * When set to true, the physics coordinates will be applied to the local
+ * translation of the Spatial instead of the world traslation.
+ * @param applyPhysicsLocal
+ */
+ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
+ motionState.setApplyPhysicsLocal(applyPhysicsLocal);
+ }
+
+ private Vector3f getSpatialTranslation(){
+ if(motionState.isApplyPhysicsLocal()){
+ return spatial.getLocalTranslation();
+ }
+ return spatial.getWorldTranslation();
+ }
+
+ private Quaternion getSpatialRotation(){
+ if(motionState.isApplyPhysicsLocal()){
+ return spatial.getLocalRotation();
+ }
+ return spatial.getWorldRotation();
+ }
+
+ public void update(float tpf) {
+ if (enabled && spatial != null) {
+ if (isKinematic() && kinematicSpatial) {
+ super.setPhysicsLocation(getSpatialTranslation());
+ super.setPhysicsRotation(getSpatialRotation());
+ } else {
+ getMotionState().applyTransform(spatial);
+ }
+ }
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ if (enabled && space != null && space.getDebugManager() != null) {
+ if (debugShape == null) {
+ attachDebugShape(space.getDebugManager());
+ }
+ //TODO: using spatial traslation/rotation..
+ debugShape.setLocalTranslation(spatial.getWorldTranslation());
+ debugShape.setLocalRotation(spatial.getWorldRotation());
+ debugShape.updateLogicalState(0);
+ debugShape.updateGeometricState();
+ rm.renderScene(debugShape, vp);
+ }
+ }
+
+ public void setPhysicsSpace(PhysicsSpace space) {
+ if (space == null) {
+ if (this.space != null) {
+ this.space.removeCollisionObject(this);
+ added = false;
+ }
+ } else {
+ if(this.space==space) return;
+ space.addCollisionObject(this);
+ added = true;
+ }
+ this.space = space;
+ }
+
+ public PhysicsSpace getPhysicsSpace() {
+ return space;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(enabled, "enabled", true);
+ oc.write(motionState.isApplyPhysicsLocal(), "applyLocalPhysics", false);
+ oc.write(kinematicSpatial, "kinematicSpatial", true);
+ oc.write(spatial, "spatial", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ enabled = ic.readBoolean("enabled", true);
+ kinematicSpatial = ic.readBoolean("kinematicSpatial", true);
+ spatial = (Spatial) ic.readSavable("spatial", null);
+ motionState.setApplyPhysicsLocal(ic.readBoolean("applyLocalPhysics", false));
+ setUserObject(spatial);
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/VehicleControl.java b/engine/src/bullet-common/com/jme3/bullet/control/VehicleControl.java
new file mode 100644
index 0000000..0f04f41
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/VehicleControl.java
@@ -0,0 +1,268 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.bullet.objects.VehicleWheel;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class VehicleControl extends PhysicsVehicle implements PhysicsControl {
+
+ protected Spatial spatial;
+ protected boolean enabled = true;
+ protected PhysicsSpace space = null;
+ protected boolean added = false;
+
+ public VehicleControl() {
+ }
+
+ /**
+ * Creates a new PhysicsNode with the supplied collision shape
+ * @param shape
+ */
+ public VehicleControl(CollisionShape shape) {
+ super(shape);
+ }
+
+ public VehicleControl(CollisionShape shape, float mass) {
+ super(shape, mass);
+ }
+
+ public boolean isApplyPhysicsLocal() {
+ return motionState.isApplyPhysicsLocal();
+ }
+
+ /**
+ * When set to true, the physics coordinates will be applied to the local
+ * translation of the Spatial
+ * @param applyPhysicsLocal
+ */
+ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
+ motionState.setApplyPhysicsLocal(applyPhysicsLocal);
+ for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
+ VehicleWheel vehicleWheel = it.next();
+ vehicleWheel.setApplyLocal(applyPhysicsLocal);
+ }
+ }
+
+ private Vector3f getSpatialTranslation(){
+ if(motionState.isApplyPhysicsLocal()){
+ return spatial.getLocalTranslation();
+ }
+ return spatial.getWorldTranslation();
+ }
+
+ private Quaternion getSpatialRotation(){
+ if(motionState.isApplyPhysicsLocal()){
+ return spatial.getLocalRotation();
+ }
+ return spatial.getWorldRotation();
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ VehicleControl control = new VehicleControl(collisionShape, mass);
+ control.setAngularFactor(getAngularFactor());
+ control.setAngularSleepingThreshold(getAngularSleepingThreshold());
+ control.setAngularVelocity(getAngularVelocity());
+ control.setCcdMotionThreshold(getCcdMotionThreshold());
+ control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
+ control.setCollideWithGroups(getCollideWithGroups());
+ control.setCollisionGroup(getCollisionGroup());
+ control.setDamping(getLinearDamping(), getAngularDamping());
+ control.setFriction(getFriction());
+ control.setGravity(getGravity());
+ control.setKinematic(isKinematic());
+ control.setLinearSleepingThreshold(getLinearSleepingThreshold());
+ control.setLinearVelocity(getLinearVelocity());
+ control.setPhysicsLocation(getPhysicsLocation());
+ control.setPhysicsRotation(getPhysicsRotationMatrix());
+ control.setRestitution(getRestitution());
+
+ control.setFrictionSlip(getFrictionSlip());
+ control.setMaxSuspensionTravelCm(getMaxSuspensionTravelCm());
+ control.setSuspensionStiffness(getSuspensionStiffness());
+ control.setSuspensionCompression(tuning.suspensionCompression);
+ control.setSuspensionDamping(tuning.suspensionDamping);
+ control.setMaxSuspensionForce(getMaxSuspensionForce());
+
+ for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
+ VehicleWheel wheel = it.next();
+ VehicleWheel newWheel = control.addWheel(wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), wheel.isFrontWheel());
+ newWheel.setFrictionSlip(wheel.getFrictionSlip());
+ newWheel.setMaxSuspensionTravelCm(wheel.getMaxSuspensionTravelCm());
+ newWheel.setSuspensionStiffness(wheel.getSuspensionStiffness());
+ newWheel.setWheelsDampingCompression(wheel.getWheelsDampingCompression());
+ newWheel.setWheelsDampingRelaxation(wheel.getWheelsDampingRelaxation());
+ newWheel.setMaxSuspensionForce(wheel.getMaxSuspensionForce());
+
+ //TODO: bad way finding children!
+ if (spatial instanceof Node) {
+ Node node = (Node) spatial;
+ Spatial wheelSpat = node.getChild(wheel.getWheelSpatial().getName());
+ if (wheelSpat != null) {
+ newWheel.setWheelSpatial(wheelSpat);
+ }
+ }
+ }
+ control.setApplyPhysicsLocal(isApplyPhysicsLocal());
+
+ control.setSpatial(spatial);
+ return control;
+ }
+
+ public void setSpatial(Spatial spatial) {
+ if (getUserObject() == null || getUserObject() == this.spatial) {
+ setUserObject(spatial);
+ }
+ this.spatial = spatial;
+ if (spatial == null) {
+ if (getUserObject() == spatial) {
+ setUserObject(null);
+ }
+ this.spatial = null;
+ this.collisionShape = null;
+ return;
+ }
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (space != null) {
+ if (enabled && !added) {
+ if(spatial!=null){
+ setPhysicsLocation(getSpatialTranslation());
+ setPhysicsRotation(getSpatialRotation());
+ }
+ space.addCollisionObject(this);
+ added = true;
+ } else if (!enabled && added) {
+ space.removeCollisionObject(this);
+ added = false;
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void update(float tpf) {
+ if (enabled && spatial != null) {
+ if (getMotionState().applyTransform(spatial)) {
+ spatial.getWorldTransform();
+ applyWheelTransforms();
+ }
+ } else if (enabled) {
+ applyWheelTransforms();
+ }
+ }
+
+ @Override
+ protected Spatial getDebugShape() {
+ return super.getDebugShape();
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ if (enabled && space != null && space.getDebugManager() != null) {
+ if (debugShape == null) {
+ attachDebugShape(space.getDebugManager());
+ }
+ Node debugNode = (Node) debugShape;
+ debugShape.setLocalTranslation(spatial.getWorldTranslation());
+ debugShape.setLocalRotation(spatial.getWorldRotation());
+ int i = 0;
+ for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
+ VehicleWheel physicsVehicleWheel = it.next();
+ Vector3f location = physicsVehicleWheel.getLocation().clone();
+ Vector3f direction = physicsVehicleWheel.getDirection().clone();
+ Vector3f axle = physicsVehicleWheel.getAxle().clone();
+ float restLength = physicsVehicleWheel.getRestLength();
+ float radius = physicsVehicleWheel.getRadius();
+
+ Geometry locGeom = (Geometry) debugNode.getChild("WheelLocationDebugShape" + i);
+ Geometry dirGeom = (Geometry) debugNode.getChild("WheelDirectionDebugShape" + i);
+ Geometry axleGeom = (Geometry) debugNode.getChild("WheelAxleDebugShape" + i);
+ Geometry wheelGeom = (Geometry) debugNode.getChild("WheelRadiusDebugShape" + i);
+
+ Arrow locArrow = (Arrow) locGeom.getMesh();
+ locArrow.setArrowExtent(location);
+ Arrow axleArrow = (Arrow) axleGeom.getMesh();
+ axleArrow.setArrowExtent(axle.normalizeLocal().multLocal(0.3f));
+ Arrow wheelArrow = (Arrow) wheelGeom.getMesh();
+ wheelArrow.setArrowExtent(direction.normalizeLocal().multLocal(radius));
+ Arrow dirArrow = (Arrow) dirGeom.getMesh();
+ dirArrow.setArrowExtent(direction.normalizeLocal().multLocal(restLength));
+
+ dirGeom.setLocalTranslation(location);
+ axleGeom.setLocalTranslation(location.addLocal(direction));
+ wheelGeom.setLocalTranslation(location);
+ i++;
+ }
+ debugShape.updateLogicalState(0);
+ debugShape.updateGeometricState();
+ rm.renderScene(debugShape, vp);
+ }
+ }
+
+ public void setPhysicsSpace(PhysicsSpace space) {
+ createVehicle(space);
+ if (space == null) {
+ if (this.space != null) {
+ this.space.removeCollisionObject(this);
+ added = false;
+ }
+ } else {
+ if(this.space==space) return;
+ space.addCollisionObject(this);
+ added = true;
+ }
+ this.space = space;
+ }
+
+ public PhysicsSpace getPhysicsSpace() {
+ return space;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(enabled, "enabled", true);
+ oc.write(motionState.isApplyPhysicsLocal(), "applyLocalPhysics", false);
+ oc.write(spatial, "spatial", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ enabled = ic.readBoolean("enabled", true);
+ spatial = (Spatial) ic.readSavable("spatial", null);
+ motionState.setApplyPhysicsLocal(ic.readBoolean("applyLocalPhysics", false));
+ setUserObject(spatial);
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java
new file mode 100644
index 0000000..06eeab0
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java
@@ -0,0 +1,99 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control.ragdoll;
+
+import com.jme3.math.FastMath;
+
+/**
+ *
+ * @author Nehon
+ */
+public class HumanoidRagdollPreset extends RagdollPreset {
+
+ @Override
+ protected void initBoneMap() {
+ boneMap.put("head", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
+
+ boneMap.put("torso", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
+
+ boneMap.put("upperleg", new JointPreset(FastMath.PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI/2, -FastMath.QUARTER_PI/2, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
+
+ boneMap.put("lowerleg", new JointPreset(0, -FastMath.PI, 0, 0, 0, 0));
+
+ boneMap.put("foot", new JointPreset(0, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
+
+ boneMap.put("upperarm", new JointPreset(FastMath.HALF_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.HALF_PI, -FastMath.QUARTER_PI));
+
+ boneMap.put("lowerarm", new JointPreset(FastMath.HALF_PI, 0, 0, 0, 0, 0));
+
+ boneMap.put("hand", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
+
+ }
+
+ @Override
+ protected void initLexicon() {
+ LexiconEntry entry = new LexiconEntry();
+ entry.addSynonym("head", 100);
+ lexicon.put("head", entry);
+
+ entry = new LexiconEntry();
+ entry.addSynonym("torso", 100);
+ entry.addSynonym("chest", 100);
+ entry.addSynonym("spine", 45);
+ entry.addSynonym("high", 25);
+ lexicon.put("torso", entry);
+
+ entry = new LexiconEntry();
+ entry.addSynonym("upperleg", 100);
+ entry.addSynonym("thigh", 100);
+ entry.addSynonym("hip", 75);
+ entry.addSynonym("leg", 40);
+ entry.addSynonym("high", 10);
+ entry.addSynonym("up", 15);
+ entry.addSynonym("upper", 15);
+ lexicon.put("upperleg", entry);
+
+ entry = new LexiconEntry();
+ entry.addSynonym("lowerleg", 100);
+ entry.addSynonym("calf", 100);
+ entry.addSynonym("knee", 75);
+ entry.addSynonym("leg", 50);
+ entry.addSynonym("low", 10);
+ entry.addSynonym("lower", 10);
+ lexicon.put("lowerleg", entry);
+
+ entry = new LexiconEntry();
+ entry.addSynonym("foot", 100);
+ entry.addSynonym("ankle", 75);
+ lexicon.put("foot", entry);
+
+
+ entry = new LexiconEntry();
+ entry.addSynonym("upperarm", 100);
+ entry.addSynonym("humerus", 100);
+ entry.addSynonym("shoulder", 50);
+ entry.addSynonym("arm", 40);
+ entry.addSynonym("high", 10);
+ entry.addSynonym("up", 15);
+ entry.addSynonym("upper", 15);
+ lexicon.put("upperarm", entry);
+
+ entry = new LexiconEntry();
+ entry.addSynonym("lowerarm", 100);
+ entry.addSynonym("ulna", 100);
+ entry.addSynonym("elbow", 75);
+ entry.addSynonym("arm", 50);
+ entry.addSynonym("low", 10);
+ entry.addSynonym("lower", 10);
+ lexicon.put("lowerarm", entry);
+
+ entry = new LexiconEntry();
+ entry.addSynonym("hand", 100);
+ entry.addSynonym("fist", 100);
+ entry.addSynonym("wrist", 75);
+ lexicon.put("hand", entry);
+
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollPreset.java b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollPreset.java
new file mode 100644
index 0000000..51bf4ba
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollPreset.java
@@ -0,0 +1,106 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control.ragdoll;
+
+import com.jme3.bullet.joints.SixDofJoint;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public abstract class RagdollPreset {
+
+ protected static final Logger logger = Logger.getLogger(RagdollPreset.class.getName());
+ protected Map<String, JointPreset> boneMap = new HashMap<String, JointPreset>();
+ protected Map<String, LexiconEntry> lexicon = new HashMap<String, LexiconEntry>();
+
+ protected abstract void initBoneMap();
+
+ protected abstract void initLexicon();
+
+ public void setupJointForBone(String boneName, SixDofJoint joint) {
+
+ if (boneMap.isEmpty()) {
+ initBoneMap();
+ }
+ if (lexicon.isEmpty()) {
+ initLexicon();
+ }
+ String resultName = "";
+ int resultScore = 0;
+
+ for (String key : lexicon.keySet()) {
+
+ int score = lexicon.get(key).getScore(boneName);
+ if (score > resultScore) {
+ resultScore = score;
+ resultName = key;
+ }
+
+ }
+
+ JointPreset preset = boneMap.get(resultName);
+
+ if (preset != null && resultScore >= 50) {
+ logger.log(Level.INFO, "Found matching joint for bone {0} : {1} with score {2}", new Object[]{boneName, resultName, resultScore});
+ preset.setupJoint(joint);
+ } else {
+ logger.log(Level.INFO, "No joint match found for bone {0}", boneName);
+ if (resultScore > 0) {
+ logger.log(Level.INFO, "Best match found is {0} with score {1}", new Object[]{resultName, resultScore});
+ }
+ new JointPreset().setupJoint(joint);
+ }
+
+ }
+
+ protected class JointPreset {
+
+ private float maxX, minX, maxY, minY, maxZ, minZ;
+
+ public JointPreset() {
+ }
+
+ public JointPreset(float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
+ this.maxX = maxX;
+ this.minX = minX;
+ this.maxY = maxY;
+ this.minY = minY;
+ this.maxZ = maxZ;
+ this.minZ = minZ;
+ }
+
+ public void setupJoint(SixDofJoint joint) {
+ joint.getRotationalLimitMotor(0).setHiLimit(maxX);
+ joint.getRotationalLimitMotor(0).setLoLimit(minX);
+ joint.getRotationalLimitMotor(1).setHiLimit(maxY);
+ joint.getRotationalLimitMotor(1).setLoLimit(minY);
+ joint.getRotationalLimitMotor(2).setHiLimit(maxZ);
+ joint.getRotationalLimitMotor(2).setLoLimit(minZ);
+ }
+ }
+
+ protected class LexiconEntry extends HashMap<String, Integer> {
+
+ public void addSynonym(String word, int score) {
+ put(word.toLowerCase(), score);
+ }
+
+ public int getScore(String word) {
+ int score = 0;
+ String searchWord = word.toLowerCase();
+ for (String key : this.keySet()) {
+ if (searchWord.indexOf(key) >= 0) {
+ score += get(key);
+ }
+ }
+ return score;
+ }
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java
new file mode 100644
index 0000000..02bab98
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java
@@ -0,0 +1,268 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control.ragdoll;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.bullet.collision.shapes.HullCollisionShape;
+import com.jme3.bullet.joints.SixDofJoint;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.*;
+
+/**
+ *
+ * @author Nehon
+ */
+public class RagdollUtils {
+
+ public static void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
+
+ joint.getRotationalLimitMotor(0).setHiLimit(maxX);
+ joint.getRotationalLimitMotor(0).setLoLimit(minX);
+ joint.getRotationalLimitMotor(1).setHiLimit(maxY);
+ joint.getRotationalLimitMotor(1).setLoLimit(minY);
+ joint.getRotationalLimitMotor(2).setHiLimit(maxZ);
+ joint.getRotationalLimitMotor(2).setLoLimit(minZ);
+ }
+
+ public static Map<Integer, List<Float>> buildPointMap(Spatial model) {
+
+
+ Map<Integer, List<Float>> map = new HashMap<Integer, List<Float>>();
+ if (model instanceof Geometry) {
+ Geometry g = (Geometry) model;
+ buildPointMapForMesh(g.getMesh(), map);
+ } else if (model instanceof Node) {
+ Node node = (Node) model;
+ for (Spatial s : node.getChildren()) {
+ if (s instanceof Geometry) {
+ Geometry g = (Geometry) s;
+ buildPointMapForMesh(g.getMesh(), map);
+ }
+ }
+ }
+ return map;
+ }
+
+ private static Map<Integer, List<Float>> buildPointMapForMesh(Mesh mesh, Map<Integer, List<Float>> map) {
+
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+ FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+ vertices.rewind();
+ boneIndices.rewind();
+ boneWeight.rewind();
+
+ int vertexComponents = mesh.getVertexCount() * 3;
+ int k, start, index;
+ float maxWeight = 0;
+
+ for (int i = 0; i < vertexComponents; i += 3) {
+
+
+ start = i / 3 * 4;
+ index = 0;
+ maxWeight = -1;
+ for (k = start; k < start + 4; k++) {
+ float weight = boneWeight.get(k);
+ if (weight > maxWeight) {
+ maxWeight = weight;
+ index = boneIndices.get(k);
+ }
+ }
+ List<Float> points = map.get(index);
+ if (points == null) {
+ points = new ArrayList<Float>();
+ map.put(index, points);
+ }
+ points.add(vertices.get(i));
+ points.add(vertices.get(i + 1));
+ points.add(vertices.get(i + 2));
+ }
+ return map;
+ }
+
+ /**
+ * Create a hull collision shape from linked vertices to this bone.
+ * Vertices have to be previoulsly gathered in a map using buildPointMap method
+ * @param link
+ * @param model
+ * @return
+ */
+ public static HullCollisionShape makeShapeFromPointMap(Map<Integer, List<Float>> pointsMap, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition) {
+
+ ArrayList<Float> points = new ArrayList<Float>();
+ for (Integer index : boneIndices) {
+ List<Float> l = pointsMap.get(index);
+ if (l != null) {
+
+ for (int i = 0; i < l.size(); i += 3) {
+ Vector3f pos = new Vector3f();
+ pos.x = l.get(i);
+ pos.y = l.get(i + 1);
+ pos.z = l.get(i + 2);
+ pos.subtractLocal(initialPosition).multLocal(initialScale);
+ points.add(pos.x);
+ points.add(pos.y);
+ points.add(pos.z);
+ }
+ }
+ }
+
+ float[] p = new float[points.size()];
+ for (int i = 0; i < points.size(); i++) {
+ p[i] = points.get(i);
+ }
+
+
+ return new HullCollisionShape(p);
+ }
+
+ //retruns the list of bone indices of the given bone and its child(if they are not in the boneList)
+ public static List<Integer> getBoneIndices(Bone bone, Skeleton skeleton, Set<String> boneList) {
+ List<Integer> list = new LinkedList<Integer>();
+ if (boneList.isEmpty()) {
+ list.add(skeleton.getBoneIndex(bone));
+ } else {
+ list.add(skeleton.getBoneIndex(bone));
+ for (Bone chilBone : bone.getChildren()) {
+ if (!boneList.contains(chilBone.getName())) {
+ list.addAll(getBoneIndices(chilBone, skeleton, boneList));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Create a hull collision shape from linked vertices to this bone.
+ *
+ * @param link
+ * @param model
+ * @return
+ */
+ public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition, float weightThreshold) {
+
+ ArrayList<Float> points = new ArrayList<Float>();
+ if (model instanceof Geometry) {
+ Geometry g = (Geometry) model;
+ for (Integer index : boneIndices) {
+ points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold));
+ }
+ } else if (model instanceof Node) {
+ Node node = (Node) model;
+ for (Spatial s : node.getChildren()) {
+ if (s instanceof Geometry) {
+ Geometry g = (Geometry) s;
+ for (Integer index : boneIndices) {
+ points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold));
+ }
+
+ }
+ }
+ }
+ float[] p = new float[points.size()];
+ for (int i = 0; i < points.size(); i++) {
+ p[i] = points.get(i);
+ }
+
+
+ return new HullCollisionShape(p);
+ }
+
+ /**
+ * returns a list of points for the given bone
+ * @param mesh
+ * @param boneIndex
+ * @param offset
+ * @param link
+ * @return
+ */
+ private static List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold) {
+
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+ FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+ vertices.rewind();
+ boneIndices.rewind();
+ boneWeight.rewind();
+
+ ArrayList<Float> results = new ArrayList<Float>();
+
+ int vertexComponents = mesh.getVertexCount() * 3;
+
+ for (int i = 0; i < vertexComponents; i += 3) {
+ int k;
+ boolean add = false;
+ int start = i / 3 * 4;
+ for (k = start; k < start + 4; k++) {
+ if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) {
+ add = true;
+ break;
+ }
+ }
+ if (add) {
+
+ Vector3f pos = new Vector3f();
+ pos.x = vertices.get(i);
+ pos.y = vertices.get(i + 1);
+ pos.z = vertices.get(i + 2);
+ pos.subtractLocal(offset).multLocal(initialScale);
+ results.add(pos.x);
+ results.add(pos.y);
+ results.add(pos.z);
+
+ }
+ }
+
+ return results;
+ }
+
+ /**
+ * Updates a bone position and rotation.
+ * if the child bones are not in the bone list this means, they are not associated with a physic shape.
+ * So they have to be updated
+ * @param bone the bone
+ * @param pos the position
+ * @param rot the rotation
+ */
+ public static void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set<String> boneList) {
+ //we ensure that we have the control
+ if (restoreBoneControl) {
+ bone.setUserControl(true);
+ }
+ //we set te user transforms of the bone
+ bone.setUserTransformsWorld(pos, rot);
+ for (Bone childBone : bone.getChildren()) {
+ //each child bone that is not in the list is updated
+ if (!boneList.contains(childBone.getName())) {
+ Transform t = childBone.getCombinedTransform(pos, rot);
+ setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl, boneList);
+ }
+ }
+ //we give back the control to the keyframed animation
+ if (restoreBoneControl) {
+ bone.setUserControl(false);
+ }
+ }
+
+ public static void setUserControl(Bone bone, boolean bool) {
+ bone.setUserControl(bool);
+ for (Bone child : bone.getChildren()) {
+ setUserControl(child, bool);
+ }
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java b/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java
new file mode 100644
index 0000000..b0031f2
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.util;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bullet.collision.shapes.*;
+import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.*;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ *
+ * @author normenhansen, tim8dev
+ */
+public class CollisionShapeFactory {
+
+ /**
+ * returns the correct transform for a collisionshape in relation
+ * to the ancestor for which the collisionshape is generated
+ * @param spat
+ * @param parent
+ * @return
+ */
+ private static Transform getTransform(Spatial spat, Spatial parent) {
+ Transform shapeTransform = new Transform();
+ Spatial parentNode = spat.getParent() != null ? spat.getParent() : spat;
+ Spatial currentSpatial = spat;
+ //if we have parents combine their transforms
+ while (parentNode != null) {
+ if (parent == currentSpatial) {
+ //real parent -> only apply scale, not transform
+ Transform trans = new Transform();
+ trans.setScale(currentSpatial.getLocalScale());
+ shapeTransform.combineWithParent(trans);
+ parentNode = null;
+ } else {
+ shapeTransform.combineWithParent(currentSpatial.getLocalTransform());
+ parentNode = currentSpatial.getParent();
+ currentSpatial = parentNode;
+ }
+ }
+ return shapeTransform;
+ }
+
+ private static CompoundCollisionShape createCompoundShape(Node realRootNode,
+ Node rootNode, CompoundCollisionShape shape, boolean meshAccurate, boolean dynamic) {
+ for (Spatial spatial : rootNode.getChildren()) {
+ if (spatial instanceof TerrainQuad) {
+ Boolean bool = spatial.getUserData(UserData.JME_PHYSICSIGNORE);
+ if (bool != null && bool.booleanValue()) {
+ continue; // go to the next child in the loop
+ }
+ TerrainQuad terrain = (TerrainQuad) spatial;
+ Transform trans = getTransform(spatial, realRootNode);
+ shape.addChildShape(new HeightfieldCollisionShape(terrain.getHeightMap(), trans.getScale()),
+ trans.getTranslation(),
+ trans.getRotation().toRotationMatrix());
+ } else if (spatial instanceof Node) {
+ createCompoundShape(realRootNode, (Node) spatial, shape, meshAccurate, dynamic);
+ } else if (spatial instanceof TerrainPatch) {
+ Boolean bool = spatial.getUserData(UserData.JME_PHYSICSIGNORE);
+ if (bool != null && bool.booleanValue()) {
+ continue; // go to the next child in the loop
+ }
+ TerrainPatch terrain = (TerrainPatch) spatial;
+ Transform trans = getTransform(spatial, realRootNode);
+ shape.addChildShape(new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale()),
+ trans.getTranslation(),
+ trans.getRotation().toRotationMatrix());
+ } else if (spatial instanceof Geometry) {
+ Boolean bool = spatial.getUserData(UserData.JME_PHYSICSIGNORE);
+ if (bool != null && bool.booleanValue()) {
+ continue; // go to the next child in the loop
+ }
+
+ if (meshAccurate) {
+ CollisionShape childShape = dynamic
+ ? createSingleDynamicMeshShape((Geometry) spatial, realRootNode)
+ : createSingleMeshShape((Geometry) spatial, realRootNode);
+ if (childShape != null) {
+ Transform trans = getTransform(spatial, realRootNode);
+ shape.addChildShape(childShape,
+ trans.getTranslation(),
+ trans.getRotation().toRotationMatrix());
+ }
+ } else {
+ Transform trans = getTransform(spatial, realRootNode);
+ shape.addChildShape(createSingleBoxShape(spatial, realRootNode),
+ trans.getTranslation(),
+ trans.getRotation().toRotationMatrix());
+ }
+ }
+ }
+ return shape;
+ }
+
+ private static CompoundCollisionShape createCompoundShape(
+ Node rootNode, CompoundCollisionShape shape, boolean meshAccurate) {
+ return createCompoundShape(rootNode, rootNode, shape, meshAccurate, false);
+ }
+
+ /**
+ * This type of collision shape is mesh-accurate and meant for immovable "world objects".
+ * Examples include terrain, houses or whole shooter levels.<br>
+ * Objects with "mesh" type collision shape will not collide with each other.
+ */
+ private static CompoundCollisionShape createMeshCompoundShape(Node rootNode) {
+ return createCompoundShape(rootNode, new CompoundCollisionShape(), true);
+ }
+
+ /**
+ * This type of collision shape creates a CompoundShape made out of boxes that
+ * are based on the bounds of the Geometries in the tree.
+ * @param rootNode
+ * @return
+ */
+ private static CompoundCollisionShape createBoxCompoundShape(Node rootNode) {
+ return createCompoundShape(rootNode, new CompoundCollisionShape(), false);
+ }
+
+ /**
+ * This type of collision shape is mesh-accurate and meant for immovable "world objects".
+ * Examples include terrain, houses or whole shooter levels.<br/>
+ * Objects with "mesh" type collision shape will not collide with each other.<br/>
+ * Creates a HeightfieldCollisionShape if the supplied spatial is a TerrainQuad.
+ * @return A MeshCollisionShape or a CompoundCollisionShape with MeshCollisionShapes as children if the supplied spatial is a Node. A HeightieldCollisionShape if a TerrainQuad was supplied.
+ */
+ public static CollisionShape createMeshShape(Spatial spatial) {
+ if (spatial instanceof TerrainQuad) {
+ TerrainQuad terrain = (TerrainQuad) spatial;
+ return new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale());
+ } else if (spatial instanceof TerrainPatch) {
+ TerrainPatch terrain = (TerrainPatch) spatial;
+ return new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale());
+ } else if (spatial instanceof Geometry) {
+ return createSingleMeshShape((Geometry) spatial, spatial);
+ } else if (spatial instanceof Node) {
+ return createMeshCompoundShape((Node) spatial);
+ } else {
+ throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!");
+ }
+ }
+
+ /**
+ * This method creates a hull shape for the given Spatial.<br>
+ * If you want to have mesh-accurate dynamic shapes (CPU intense!!!) use GImpact shapes, its probably best to do so with a low-poly version of your model.
+ * @return A HullCollisionShape or a CompoundCollisionShape with HullCollisionShapes as children if the supplied spatial is a Node.
+ */
+ public static CollisionShape createDynamicMeshShape(Spatial spatial) {
+ if (spatial instanceof Geometry) {
+ return createSingleDynamicMeshShape((Geometry) spatial, spatial);
+ } else if (spatial instanceof Node) {
+ return createCompoundShape((Node) spatial, (Node) spatial, new CompoundCollisionShape(), true, true);
+ } else {
+ throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!");
+ }
+
+ }
+
+ public static CollisionShape createBoxShape(Spatial spatial) {
+ if (spatial instanceof Geometry) {
+ return createSingleBoxShape((Geometry) spatial, spatial);
+ } else if (spatial instanceof Node) {
+ return createBoxCompoundShape((Node) spatial);
+ } else {
+ throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!");
+ }
+ }
+
+ /**
+ * This type of collision shape is mesh-accurate and meant for immovable "world objects".
+ * Examples include terrain, houses or whole shooter levels.<br>
+ * Objects with "mesh" type collision shape will not collide with each other.
+ */
+ private static MeshCollisionShape createSingleMeshShape(Geometry geom, Spatial parent) {
+ Mesh mesh = geom.getMesh();
+ Transform trans = getTransform(geom, parent);
+ if (mesh != null) {
+ MeshCollisionShape mColl = new MeshCollisionShape(mesh);
+ mColl.setScale(trans.getScale());
+ return mColl;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Uses the bounding box of the supplied spatial to create a BoxCollisionShape
+ * @param spatial
+ * @return BoxCollisionShape with the size of the spatials BoundingBox
+ */
+ private static BoxCollisionShape createSingleBoxShape(Spatial spatial, Spatial parent) {
+ spatial.setModelBound(new BoundingBox());
+ //TODO: using world bound here instead of "local world" bound...
+ BoxCollisionShape shape = new BoxCollisionShape(
+ ((BoundingBox) spatial.getWorldBound()).getExtent(new Vector3f()));
+ return shape;
+ }
+
+ /**
+ * This method creates a hull collision shape for the given mesh.<br>
+ */
+ private static HullCollisionShape createSingleDynamicMeshShape(Geometry geom, Spatial parent) {
+ Mesh mesh = geom.getMesh();
+ Transform trans = getTransform(geom, parent);
+ if (mesh != null) {
+ HullCollisionShape dynamicShape = new HullCollisionShape(mesh);
+ dynamicShape.setScale(trans.getScale());
+ return dynamicShape;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * This method moves each child shape of a compound shape by the given vector
+ * @param vector
+ */
+ public static void shiftCompoundShapeContents(CompoundCollisionShape compoundShape, Vector3f vector) {
+ for (Iterator<ChildCollisionShape> it = new LinkedList(compoundShape.getChildren()).iterator(); it.hasNext();) {
+ ChildCollisionShape childCollisionShape = it.next();
+ CollisionShape child = childCollisionShape.shape;
+ Vector3f location = childCollisionShape.location;
+ Matrix3f rotation = childCollisionShape.rotation;
+ compoundShape.removeChildShape(child);
+ compoundShape.addChildShape(child, location.add(vector), rotation);
+ }
+ }
+}
diff --git a/engine/src/bullet-native/android/Android.mk b/engine/src/bullet-native/android/Android.mk
new file mode 100644
index 0000000..3db0351
--- /dev/null
+++ b/engine/src/bullet-native/android/Android.mk
@@ -0,0 +1,259 @@
+# /*
+# Bullet Continuous Collision Detection and Physics Library for Android NDK
+# Copyright (c) 2006-2009 Noritsuna Imamura <a href="http://www.siprop.org/" rel="nofollow">http://www.siprop.org/</a>
+#
+# This software is provided 'as-is', without any express or implied warranty.
+# In no event will the authors be held liable for any damages arising from the use of this software.
+# Permission is granted to anyone to use this software for any purpose,
+# including commercial applications, and to alter it and redistribute it freely,
+# subject to the following restrictions:
+#
+# 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+# 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+# 3. This notice may not be removed or altered from any source distribution.
+# */
+LOCAL_PATH:= $(call my-dir)
+JME3_PATH:=
+BULLET_PATH:=
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bulletjme
+LOCAL_C_INCLUDES := $(BULLET_PATH)/\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver\
+ $(BULLET_PATH)/BulletDynamics/Dynamics\
+ $(BULLET_PATH)/BulletDynamics/Vehicle\
+ $(BULLET_PATH)/LinearMath\
+ $(BULLET_PATH)/BulletCollision\
+ $(BULLET_PATH)/BulletDynamics\
+ $(BULLET_PATH)/BulletMultiThreaded\
+ $(BULLET_PATH)/BulletSoftBody\
+ $(BULLET_PATH)/ibmsdk\
+ $(BULLET_PATH)/LinearMath\
+ $(BULLET_PATH)/MiniCL\
+ $(BULLET_PATH)/vectormath\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes\
+ $(BULLET_PATH)/BulletCollision/Gimpact\
+ $(BULLET_PATH)/BulletCollision/ibmsdk\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision\
+ $(BULLET_PATH)/BulletDynamics/Character\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver\
+ $(BULLET_PATH)/BulletDynamics/Dynamics\
+ $(BULLET_PATH)/BulletDynamics/ibmsdk\
+ $(BULLET_PATH)/BulletDynamics/Vehicle\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers\
+ $(BULLET_PATH)/BulletMultiThreaded/out\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuSampleTask\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/CPU\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/DX11\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/DX11/HLSL\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/AMD\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Apple\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/NVidia\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/OpenCLC\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/OpenCLC10\
+ $(BULLET_PATH)/LinearMath/ibmsdk\
+ $(BULLET_PATH)/MiniCL/MiniCLTask\
+ $(BULLET_PATH)/vectormath/scalar\
+ $(BULLET_PATH)/vectormath/sse
+
+LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
+LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl -lm -llog
+
+LOCAL_SRC_FILES := $(JME3_PATH)/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_PhysicsCollisionObject.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_CollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_ConeJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_HingeJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_PhysicsJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_Point2PointJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_SixDofJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_SixDofSpringJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_joints_SliderJoint.cpp\
+ $(JME3_PATH)/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp\
+ $(JME3_PATH)/com_jme3_bullet_objects_PhysicsCharacter.cpp\
+ $(JME3_PATH)/com_jme3_bullet_objects_PhysicsGhostObject.cpp\
+ $(JME3_PATH)/com_jme3_bullet_objects_PhysicsRigidBody.cpp\
+ $(JME3_PATH)/com_jme3_bullet_objects_PhysicsVehicle.cpp\
+ $(JME3_PATH)/com_jme3_bullet_objects_VehicleWheel.cpp\
+ $(JME3_PATH)/com_jme3_bullet_PhysicsSpace.cpp\
+ $(JME3_PATH)/com_jme3_bullet_util_DebugShapeFactory.cpp\
+ $(JME3_PATH)/com_jme3_bullet_util_NativeMeshUtil.cpp\
+ $(JME3_PATH)/jmeBulletUtil.cpp\
+ $(JME3_PATH)/jmeClasses.cpp\
+ $(JME3_PATH)/jmeMotionState.cpp\
+ $(JME3_PATH)/jmePhysicsSpace.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btDbvt.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btDispatcher.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp\
+ $(BULLET_PATH)/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCollisionObject.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCollisionWorld.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btGhostObject.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btManifoldResult.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/btUnionFind.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btBox2dShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btBoxShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btCapsuleShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btCollisionShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btCompoundShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConcaveShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConeShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvex2dShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexHullShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexInternalShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btCylinderShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btEmptyShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btMultiSphereShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btOptimizedBvh.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btShapeHull.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btSphereShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTetrahedronShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleBuffer.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleCallback.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleMesh.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp\
+ $(BULLET_PATH)/BulletCollision/CollisionShapes/btUniformScalingShape.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btContactProcessing.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btGenericPoolAllocator.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactBvh.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btGImpactShape.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/btTriangleShapeEx.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/gim_box_set.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/gim_contact.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/gim_memory.cpp\
+ $(BULLET_PATH)/BulletCollision/Gimpact/gim_tri_collision.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp\
+ $(BULLET_PATH)/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp\
+ $(BULLET_PATH)/BulletDynamics/Character/btKinematicCharacterController.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btContactConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp\
+ $(BULLET_PATH)/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp\
+ $(BULLET_PATH)/BulletDynamics/Dynamics/btRigidBody.cpp\
+ $(BULLET_PATH)/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp\
+ $(BULLET_PATH)/BulletDynamics/Dynamics/Bullet-C-API.cpp\
+ $(BULLET_PATH)/BulletDynamics/Vehicle/btRaycastVehicle.cpp\
+ $(BULLET_PATH)/BulletDynamics/Vehicle/btWheelInfo.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/btGpu3DGridBroadphase.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/btParallelConstraintSolver.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/btThreadSupportInterface.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/PosixThreadSupport.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SequentialThreadSupport.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuCollisionObjectWrapper.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuCollisionTaskProcess.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuFakeDma.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuLibspe2Support.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuSampleTaskProcess.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/Win32ThreadSupport.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/CPU/btSoftBodySolver_CPU.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL/MiniCLTaskWrap.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp\
+ $(BULLET_PATH)/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btDefaultSoftBodySolver.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftBody.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftBodyHelpers.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftRigidDynamicsWorld.cpp\
+ $(BULLET_PATH)/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp\
+ $(BULLET_PATH)/LinearMath/btAlignedAllocator.cpp\
+ $(BULLET_PATH)/LinearMath/btConvexHull.cpp\
+ $(BULLET_PATH)/LinearMath/btConvexHullComputer.cpp\
+ $(BULLET_PATH)/LinearMath/btGeometryUtil.cpp\
+ $(BULLET_PATH)/LinearMath/btQuickprof.cpp\
+ $(BULLET_PATH)/LinearMath/btSerializer.cpp\
+ $(BULLET_PATH)/MiniCL/MiniCL.cpp\
+ $(BULLET_PATH)/MiniCL/MiniCLTaskScheduler.cpp\
+ $(BULLET_PATH)/MiniCL/MiniCLTask/MiniCLTask.cpp
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/engine/src/bullet-native/android/Application.mk b/engine/src/bullet-native/android/Application.mk
new file mode 100644
index 0000000..9e8be35
--- /dev/null
+++ b/engine/src/bullet-native/android/Application.mk
@@ -0,0 +1,2 @@
+APP_MODULES := bulletjme
+APP_ABI := all \ No newline at end of file
diff --git a/engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.cpp b/engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.cpp
new file mode 100644
index 0000000..8116f90
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "com_jme3_bullet_PhysicsSpace.h"
+#include "jmePhysicsSpace.h"
+#include "jmeBulletUtil.h"
+
+/**
+ * Author: Normen Hansen
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: createPhysicsSpace
+ * Signature: (FFFFFFI)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_PhysicsSpace_createPhysicsSpace
+ (JNIEnv * env, jobject object, jfloat minX, jfloat minY, jfloat minZ, jfloat maxX, jfloat maxY, jfloat maxZ, jint broadphase, jboolean threading) {
+ jmeClasses::initJavaClasses(env);
+ jmePhysicsSpace* space = new jmePhysicsSpace(env, object);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space has not been created.");
+ return 0;
+ }
+ space->createPhysicsSpace(minX, minY, minZ, maxX, maxY, maxZ, broadphase, threading);
+ return reinterpret_cast<jlong>(space);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: stepSimulation
+ * Signature: (JFIF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_stepSimulation
+ (JNIEnv * env, jobject object, jlong spaceId, jfloat tpf, jint maxSteps, jfloat accuracy) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ space->stepSimulation(tpf, maxSteps, accuracy);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addCollisionObject
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addCollisionObject
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ userPointer -> space = space;
+
+ space->getDynamicsWorld()->addCollisionObject(collisionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeCollisionObject
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeCollisionObject
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->removeCollisionObject(collisionObject);
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ userPointer -> space = NULL;
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addRigidBody
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addRigidBody
+ (JNIEnv * env, jobject object, jlong spaceId, jlong rigidBodyId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btRigidBody* collisionObject = reinterpret_cast<btRigidBody*>(rigidBodyId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ userPointer -> space = space;
+ space->getDynamicsWorld()->addRigidBody(collisionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeRigidBody
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeRigidBody
+ (JNIEnv * env, jobject object, jlong spaceId, jlong rigidBodyId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btRigidBody* collisionObject = reinterpret_cast<btRigidBody*>(rigidBodyId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ userPointer -> space = NULL;
+ space->getDynamicsWorld()->removeRigidBody(collisionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addCharacterObject
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addCharacterObject
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ userPointer -> space = space;
+ space->getDynamicsWorld()->addCollisionObject(collisionObject,
+ btBroadphaseProxy::CharacterFilter,
+ btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter
+ );
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeCharacterObject
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeCharacterObject
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ userPointer -> space = NULL;
+ space->getDynamicsWorld()->removeCollisionObject(collisionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addAction
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addAction
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (actionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The action object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->addAction(actionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeAction
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeAction
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (actionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The action object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->removeAction(actionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addVehicle
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addVehicle
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (actionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The vehicle object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->addVehicle(actionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeVehicle
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeVehicle
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (actionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The action object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->removeVehicle(actionObject);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addConstraint
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addConstraint
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btTypedConstraint* constraint = reinterpret_cast<btTypedConstraint*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (constraint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The constraint object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->addConstraint(constraint);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeConstraint
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeConstraint
+ (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ btTypedConstraint* constraint = reinterpret_cast<btTypedConstraint*>(objectId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ if (constraint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The constraint object does not exist.");
+ return;
+ }
+ space->getDynamicsWorld()->removeConstraint(constraint);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: setGravity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_setGravity
+ (JNIEnv * env, jobject object, jlong spaceId, jobject vector) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+ btVector3 gravity = btVector3();
+ jmeBulletUtil::convert(env, vector, &gravity);
+ space->getDynamicsWorld()->setGravity(gravity);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: initNativePhysics
+ * Signature: ()V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_initNativePhysics
+ (JNIEnv * env, jclass clazz) {
+ jmeClasses::initJavaClasses(env);
+ }
+
+ /*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative
+ (JNIEnv * env, jobject object, jlong spaceId) {
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ if (space == NULL) {
+ return;
+ }
+ delete(space);
+ }
+
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_rayTest_1native
+ (JNIEnv * env, jobject object, jobject to, jobject from, jlong spaceId, jobject resultlist) {
+
+ jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*> (spaceId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The physics space does not exist.");
+ return;
+ }
+
+ struct AllRayResultCallback : public btCollisionWorld::RayResultCallback {
+
+ AllRayResultCallback(const btVector3& rayFromWorld, const btVector3 & rayToWorld) : m_rayFromWorld(rayFromWorld), m_rayToWorld(rayToWorld) {
+ }
+ jobject resultlist;
+ JNIEnv* env;
+ btVector3 m_rayFromWorld; //used to calculate hitPointWorld from hitFraction
+ btVector3 m_rayToWorld;
+
+ btVector3 m_hitNormalWorld;
+ btVector3 m_hitPointWorld;
+
+ virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) {
+ if (normalInWorldSpace) {
+ m_hitNormalWorld = rayResult.m_hitNormalLocal;
+ } else {
+ m_hitNormalWorld = m_collisionObject->getWorldTransform().getBasis() * rayResult.m_hitNormalLocal;
+ }
+ m_hitPointWorld.setInterpolate3(m_rayFromWorld, m_rayToWorld, rayResult.m_hitFraction);
+
+ jmeBulletUtil::addResult(env, resultlist, m_hitNormalWorld, m_hitPointWorld, rayResult.m_hitFraction, rayResult.m_collisionObject);
+
+ return 1.f;
+ }
+ };
+
+ btVector3 native_to = btVector3();
+ jmeBulletUtil::convert(env, to, &native_to);
+
+ btVector3 native_from = btVector3();
+ jmeBulletUtil::convert(env, from, &native_from);
+
+ AllRayResultCallback resultCallback(native_from, native_to);
+ resultCallback.env = env;
+ resultCallback.resultlist = resultlist;
+ space->getDynamicsWorld()->rayTest(native_from, native_to, resultCallback);
+ return;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.h b/engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.h
new file mode 100644
index 0000000..3b14a75
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_PhysicsSpace.h
@@ -0,0 +1,165 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_PhysicsSpace */
+
+#ifndef _Included_com_jme3_bullet_PhysicsSpace
+#define _Included_com_jme3_bullet_PhysicsSpace
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_PhysicsSpace_AXIS_X
+#define com_jme3_bullet_PhysicsSpace_AXIS_X 0L
+#undef com_jme3_bullet_PhysicsSpace_AXIS_Y
+#define com_jme3_bullet_PhysicsSpace_AXIS_Y 1L
+#undef com_jme3_bullet_PhysicsSpace_AXIS_Z
+#define com_jme3_bullet_PhysicsSpace_AXIS_Z 2L
+/* Inaccessible static: pQueueTL */
+/* Inaccessible static: physicsSpaceTL */
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: createPhysicsSpace
+ * Signature: (FFFFFFIZ)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_PhysicsSpace_createPhysicsSpace
+ (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat, jint, jboolean);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: stepSimulation
+ * Signature: (JFIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_stepSimulation
+ (JNIEnv *, jobject, jlong, jfloat, jint, jfloat);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addCollisionObject
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addCollisionObject
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeCollisionObject
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeCollisionObject
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addRigidBody
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addRigidBody
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeRigidBody
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeRigidBody
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addCharacterObject
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addCharacterObject
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeCharacterObject
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeCharacterObject
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addAction
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addAction
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeAction
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeAction
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addVehicle
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addVehicle
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeVehicle
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeVehicle
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: addConstraint
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addConstraint
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: removeConstraint
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeConstraint
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: setGravity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_setGravity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: rayTest_native
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;JLjava/util/List;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_rayTest_1native
+ (JNIEnv *, jobject, jobject, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: initNativePhysics
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_initNativePhysics
+ (JNIEnv *, jclass);
+
+/*
+ * Class: com_jme3_bullet_PhysicsSpace
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp
new file mode 100644
index 0000000..e9199d4
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.cpp
@@ -0,0 +1,308 @@
+#include "jmeBulletUtil.h"
+#include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h"
+#include "com_jme3_bullet_collision_PhysicsCollisionEvent.h"
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getAppliedImpulse
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_appliedImpulse;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulseLateral1
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getAppliedImpulseLateral1
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_appliedImpulseLateral1;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulseLateral2
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getAppliedImpulseLateral2
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_appliedImpulseLateral2;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getCombinedFriction
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getCombinedFriction
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_combinedFriction;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getCombinedRestitution
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getCombinedRestitution
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_combinedRestitution;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getDistance1
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getDistance1
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_distance1;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getIndex0
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getIndex0
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_index0;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getIndex1
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getIndex1
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_index1;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLateralFrictionDir1
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLateralFrictionDir1
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject lateralFrictionDir1) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_lateralFrictionDir1, lateralFrictionDir1);
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLateralFrictionDir2
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLateralFrictionDir2
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject lateralFrictionDir2) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_lateralFrictionDir2, lateralFrictionDir2);
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: isLateralFrictionInitialized
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_isLateralFrictionInitialized
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_lateralFrictionInitialized;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLifeTime
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLifeTime
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_lifeTime;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLocalPointA
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLocalPointA
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject localPointA) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_localPointA, localPointA);
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLocalPointB
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLocalPointB
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject localPointB) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_localPointB, localPointB);
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getNormalWorldOnB
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getNormalWorldOnB
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject normalWorldOnB) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_normalWorldOnB, normalWorldOnB);
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPartId0
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPartId0
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_partId0;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPartId1
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPartId1
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return 0;
+ }
+ return mp -> m_partId1;
+}
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPositionWorldOnA
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPositionWorldOnA
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject positionWorldOnA) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_positionWorldOnA, positionWorldOnA);
+}
+
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPositionWorldOnB
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPositionWorldOnB
+ (JNIEnv * env, jobject object, jlong manifoldPointObjectId, jobject positionWorldOnB) {
+ btManifoldPoint* mp = reinterpret_cast<btManifoldPoint*>(manifoldPointObjectId);
+ if (mp == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The manifoldPoint does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &mp -> m_positionWorldOnB, positionWorldOnB);
+}
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.h b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.h
new file mode 100644
index 0000000..2dd9dcf
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionEvent.h
@@ -0,0 +1,173 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_PhysicsCollisionEvent */
+
+#ifndef _Included_com_jme3_bullet_collision_PhysicsCollisionEvent
+#define _Included_com_jme3_bullet_collision_PhysicsCollisionEvent
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_collision_PhysicsCollisionEvent_serialVersionUID
+#define com_jme3_bullet_collision_PhysicsCollisionEvent_serialVersionUID 5516075349620653480LL
+#undef com_jme3_bullet_collision_PhysicsCollisionEvent_TYPE_ADDED
+#define com_jme3_bullet_collision_PhysicsCollisionEvent_TYPE_ADDED 0L
+#undef com_jme3_bullet_collision_PhysicsCollisionEvent_TYPE_PROCESSED
+#define com_jme3_bullet_collision_PhysicsCollisionEvent_TYPE_PROCESSED 1L
+#undef com_jme3_bullet_collision_PhysicsCollisionEvent_TYPE_DESTROYED
+#define com_jme3_bullet_collision_PhysicsCollisionEvent_TYPE_DESTROYED 2L
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getAppliedImpulse
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulseLateral1
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getAppliedImpulseLateral1
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getAppliedImpulseLateral2
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getAppliedImpulseLateral2
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getCombinedFriction
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getCombinedFriction
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getCombinedRestitution
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getCombinedRestitution
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getDistance1
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getDistance1
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getIndex0
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getIndex0
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getIndex1
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getIndex1
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLateralFrictionDir1
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLateralFrictionDir1
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLateralFrictionDir2
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLateralFrictionDir2
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: isLateralFrictionInitialized
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_isLateralFrictionInitialized
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLifeTime
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLifeTime
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLocalPointA
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLocalPointA
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getLocalPointB
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getLocalPointB
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getNormalWorldOnB
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getNormalWorldOnB
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPartId0
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPartId0
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPartId1
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPartId1
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPositionWorldOnA
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPositionWorldOnA
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionEvent
+ * Method: getPositionWorldOnB
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionEvent_getPositionWorldOnB
+ (JNIEnv *, jobject, jlong, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.cpp
new file mode 100644
index 0000000..5dc75b3
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_PhysicsCollisionObject.h"
+#include "jmeBulletUtil.h"
+#include "jmePhysicsSpace.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: attachCollisionShape
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_attachCollisionShape
+ (JNIEnv * env, jobject object, jlong objectId, jlong shapeId) {
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/IllegalStateException");
+ env->ThrowNew(newExc, "The collision object does not exist.");
+ return;
+ }
+ btCollisionShape* collisionShape = reinterpret_cast<btCollisionShape*>(shapeId);
+ if (collisionShape == NULL) {
+ jclass newExc = env->FindClass("java/lang/IllegalStateException");
+ env->ThrowNew(newExc, "The collision shape does not exist.");
+ return;
+ }
+ collisionObject->setCollisionShape(collisionShape);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_finalizeNative
+ (JNIEnv * env, jobject object, jlong objectId) {
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ if (collisionObject -> getUserPointer() != NULL){
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ delete(userPointer);
+ }
+ delete(collisionObject);
+ }
+ /*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: initUserPointer
+ * Signature: (JII)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_initUserPointer
+ (JNIEnv *env, jobject object, jlong objectId, jint group, jint groups) {
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ if (userPointer != NULL) {
+// delete(userPointer);
+ }
+ userPointer = new jmeUserPointer();
+ userPointer -> javaCollisionObject = env->NewWeakGlobalRef(object);
+ userPointer -> group = group;
+ userPointer -> groups = groups;
+ userPointer -> space = NULL;
+ collisionObject -> setUserPointer(userPointer);
+ }
+ /*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: setCollisionGroup
+ * Signature: (JI)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_setCollisionGroup
+ (JNIEnv *env, jobject object, jlong objectId, jint group) {
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ if (userPointer != NULL){
+ userPointer -> group = group;
+ }
+ }
+ /*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: setCollideWithGroups
+ * Signature: (JI)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_setCollideWithGroups
+ (JNIEnv *env, jobject object, jlong objectId, jint groups) {
+ btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
+ if (collisionObject == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
+ if (userPointer != NULL){
+ userPointer -> groups = groups;
+ }
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.h b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.h
new file mode 100644
index 0000000..db5bf7a
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_PhysicsCollisionObject.h
@@ -0,0 +1,87 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_PhysicsCollisionObject */
+
+#ifndef _Included_com_jme3_bullet_collision_PhysicsCollisionObject
+#define _Included_com_jme3_bullet_collision_PhysicsCollisionObject
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_NONE
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_NONE 0L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_01
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_01 1L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_02
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_02 2L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_03
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_03 4L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_04
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_04 8L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_05
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_05 16L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_06
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_06 32L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_07
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_07 64L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_08
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_08 128L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_09
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_09 256L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_10
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_10 512L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_11
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_11 1024L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_12
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_12 2048L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_13
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_13 4096L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_14
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_14 8192L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_15
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_15 16384L
+#undef com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_16
+#define com_jme3_bullet_collision_PhysicsCollisionObject_COLLISION_GROUP_16 32768L
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: initUserPointer
+ * Signature: (JII)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_initUserPointer
+ (JNIEnv *, jobject, jlong, jint, jint);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: attachCollisionShape
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_attachCollisionShape
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: setCollisionGroup
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_setCollisionGroup
+ (JNIEnv *, jobject, jlong, jint);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: setCollideWithGroups
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_setCollideWithGroups
+ (JNIEnv *, jobject, jlong, jint);
+
+/*
+ * Class: com_jme3_bullet_collision_PhysicsCollisionObject
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp
new file mode 100644
index 0000000..3d2f0e3
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_BoxCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_BoxCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_BoxCollisionShape_createShape
+ (JNIEnv *env, jobject object, jobject halfExtents) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 extents = btVector3();
+ jmeBulletUtil::convert(env, halfExtents, &extents);
+ btBoxShape* shape = new btBoxShape(extents);
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.h
new file mode 100644
index 0000000..5602a0d
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_BoxCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_BoxCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_BoxCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_BoxCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_BoxCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_BoxCollisionShape_createShape
+ (JNIEnv *, jobject, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp
new file mode 100644
index 0000000..b3d4a8e
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_CapsuleCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CapsuleCollisionShape
+ * Method: createShape
+ * Signature: (IFF)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CapsuleCollisionShape_createShape
+ (JNIEnv * env, jobject object, jint axis, jfloat radius, jfloat height) {
+ jmeClasses::initJavaClasses(env);
+ btCollisionShape* shape;
+ switch(axis){
+ case 0:
+ shape = new btCapsuleShapeX(radius, height);
+ break;
+ case 1:
+ shape = new btCapsuleShape(radius, height);
+ break;
+ case 2:
+ shape = new btCapsuleShapeZ(radius, height);
+ break;
+ }
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.h
new file mode 100644
index 0000000..4d70674
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CapsuleCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_CapsuleCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_CapsuleCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_CapsuleCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_CapsuleCollisionShape
+ * Method: createShape
+ * Signature: (IFF)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CapsuleCollisionShape_createShape
+ (JNIEnv *, jobject, jint, jfloat, jfloat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.cpp
new file mode 100644
index 0000000..efd7990
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_CollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: getMargin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_getMargin
+ (JNIEnv * env, jobject object, jlong shapeId) {
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return shape->getMargin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: setLocalScaling
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_setLocalScaling
+ (JNIEnv * env, jobject object, jlong shapeId, jobject scale) {
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 scl = btVector3();
+ jmeBulletUtil::convert(env, scale, &scl);
+ shape->setLocalScaling(scl);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: setMargin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_setMargin
+ (JNIEnv * env, jobject object, jlong shapeId, jfloat newMargin) {
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ shape->setMargin(newMargin);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_finalizeNative
+ (JNIEnv * env, jobject object, jlong shapeId) {
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ delete(shape);
+ }
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.h
new file mode 100644
index 0000000..cd5d70f
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CollisionShape.h
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_CollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_CollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_CollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: getMargin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_getMargin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: setLocalScaling
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_setLocalScaling
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: setMargin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_setMargin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_CollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp
new file mode 100644
index 0000000..5282175
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_CompoundCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: createShape
+ * Signature: ()J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CompoundCollisionShape_createShape
+ (JNIEnv *env, jobject object) {
+ jmeClasses::initJavaClasses(env);
+ btCompoundShape* shape = new btCompoundShape();
+ return reinterpret_cast<jlong>(shape);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: addChildShape
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CompoundCollisionShape_addChildShape
+ (JNIEnv *env, jobject object, jlong compoundId, jlong childId, jobject childLocation, jobject childRotation) {
+ btCompoundShape* shape = reinterpret_cast<btCompoundShape*>(compoundId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btCollisionShape* child = reinterpret_cast<btCollisionShape*>(childId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btMatrix3x3 mtx = btMatrix3x3();
+ btTransform trans = btTransform(mtx);
+ jmeBulletUtil::convert(env, childLocation, &trans.getOrigin());
+ jmeBulletUtil::convert(env, childRotation, &trans.getBasis());
+ shape->addChildShape(trans, child);
+ return 0;
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: removeChildShape
+ * Signature: (JJ)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CompoundCollisionShape_removeChildShape
+ (JNIEnv * env, jobject object, jlong compoundId, jlong childId) {
+ btCompoundShape* shape = reinterpret_cast<btCompoundShape*>(compoundId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btCollisionShape* child = reinterpret_cast<btCollisionShape*>(childId);
+ if (shape == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ shape->removeChildShape(child);
+ return 0;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.h
new file mode 100644
index 0000000..18783ce
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CompoundCollisionShape.h
@@ -0,0 +1,37 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_CompoundCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_CompoundCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_CompoundCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: createShape
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CompoundCollisionShape_createShape
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: addChildShape
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CompoundCollisionShape_addChildShape
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_CompoundCollisionShape
+ * Method: removeChildShape
+ * Signature: (JJ)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CompoundCollisionShape_removeChildShape
+ (JNIEnv *, jobject, jlong, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp
new file mode 100644
index 0000000..06de8dd
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_ConeCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_ConeCollisionShape
+ * Method: createShape
+ * Signature: (IFF)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_ConeCollisionShape_createShape
+ (JNIEnv * env, jobject object, jint axis, jfloat radius, jfloat height) {
+ jmeClasses::initJavaClasses(env);
+ btCollisionShape* shape;
+ switch (axis) {
+ case 0:
+ shape = new btConeShapeX(radius, height);
+ break;
+ case 1:
+ shape = new btConeShape(radius, height);
+ break;
+ case 2:
+ shape = new btConeShapeZ(radius, height);
+ break;
+ }
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.h
new file mode 100644
index 0000000..711276e
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_ConeCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_ConeCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_ConeCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_ConeCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_ConeCollisionShape
+ * Method: createShape
+ * Signature: (IFF)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_ConeCollisionShape_createShape
+ (JNIEnv *, jobject, jint, jfloat, jfloat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp
new file mode 100644
index 0000000..fd82f13
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_CylinderCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_CylinderCollisionShape
+ * Method: createShape
+ * Signature: (ILcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CylinderCollisionShape_createShape
+ (JNIEnv * env, jobject object, jint axis, jobject halfExtents) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 extents = btVector3();
+ jmeBulletUtil::convert(env, halfExtents, &extents);
+ btCollisionShape* shape;
+ switch (axis) {
+ case 0:
+ shape = new btCylinderShapeX(extents);
+ break;
+ case 1:
+ shape = new btCylinderShape(extents);
+ break;
+ case 2:
+ shape = new btCylinderShapeZ(extents);
+ break;
+ }
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.h
new file mode 100644
index 0000000..48a665a
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_CylinderCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_CylinderCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_CylinderCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_CylinderCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_CylinderCollisionShape
+ * Method: createShape
+ * Signature: (ILcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_CylinderCollisionShape_createShape
+ (JNIEnv *, jobject, jint, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp
new file mode 100644
index 0000000..43ceb3d
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_GImpactCollisionShape.h"
+#include "jmeBulletUtil.h"
+#include <BulletCollision/Gimpact/btGImpactShape.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape
+ * Method: createShape
+ * Signature: (J)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_GImpactCollisionShape_createShape
+ (JNIEnv * env, jobject object, jlong meshId) {
+ jmeClasses::initJavaClasses(env);
+ btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(meshId);
+ btGImpactMeshShape* shape = new btGImpactMeshShape(array);
+ return reinterpret_cast<jlong>(shape);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_GImpactCollisionShape_finalizeNative
+ (JNIEnv * env, jobject object, jlong meshId) {
+ btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*> (meshId);
+ delete(array);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.h
new file mode 100644
index 0000000..f08d3eb
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_GImpactCollisionShape.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_GImpactCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_GImpactCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_GImpactCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape
+ * Method: createShape
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_GImpactCollisionShape_createShape
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_GImpactCollisionShape_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp
new file mode 100644
index 0000000..06698a1
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.h"
+#include "jmeBulletUtil.h"
+#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_HeightfieldCollisionShape
+ * Method: createShape
+ * Signature: (II[FFFFIZ)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_HeightfieldCollisionShape_createShape
+ (JNIEnv * env, jobject object, jint heightStickWidth, jint heightStickLength, jobject heightfieldData, jfloat heightScale, jfloat minHeight, jfloat maxHeight, jint upAxis, jboolean flipQuadEdges) {
+ jmeClasses::initJavaClasses(env);
+ void* data = env->GetDirectBufferAddress(heightfieldData);
+ btHeightfieldTerrainShape* shape=new btHeightfieldTerrainShape(heightStickWidth, heightStickLength, data, heightScale, minHeight, maxHeight, upAxis, PHY_FLOAT, flipQuadEdges);
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.h
new file mode 100644
index 0000000..a3d1621
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HeightfieldCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_HeightfieldCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_HeightfieldCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_HeightfieldCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_HeightfieldCollisionShape
+ * Method: createShape
+ * Signature: (IILjava/nio/ByteBuffer;FFFIZ)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_HeightfieldCollisionShape_createShape
+ (JNIEnv *, jobject, jint, jint, jobject, jfloat, jfloat, jfloat, jint, jboolean);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp
new file mode 100644
index 0000000..5789bd9
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_HullCollisionShape.h"
+#include "jmeBulletUtil.h"
+#include "BulletCollision/CollisionShapes/btConvexHullShape.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_HullCollisionShape
+ * Method: createShape
+ * Signature: ([F)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_HullCollisionShape_createShape
+ (JNIEnv *env, jobject object, jobject array) {
+ jmeClasses::initJavaClasses(env);
+ float* data = (float*) env->GetDirectBufferAddress(array);
+ //TODO: capacity will not always be length!
+ int length = env->GetDirectBufferCapacity(array)/4;
+ btConvexHullShape* shape = new btConvexHullShape();
+ for (int i = 0; i < length; i+=3) {
+ btVector3 vect = btVector3(data[i],
+ data[i + 1],
+ data[i + 2]);
+
+ shape->addPoint(vect);
+ }
+
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.h
new file mode 100644
index 0000000..42a2672
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_HullCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_HullCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_HullCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_HullCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_HullCollisionShape
+ * Method: createShape
+ * Signature: (Ljava/nio/ByteBuffer;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_HullCollisionShape_createShape
+ (JNIEnv *, jobject, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp
new file mode 100644
index 0000000..346d47e
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_MeshCollisionShape.h"
+#include "jmeBulletUtil.h"
+#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape
+ * Method: createShape
+ * Signature: (J)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_createShape
+ (JNIEnv * env, jobject object, jlong arrayId) {
+ jmeClasses::initJavaClasses(env);
+ btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(arrayId);
+ btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(array, true, true);
+ return reinterpret_cast<jlong>(shape);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_finalizeNative
+ (JNIEnv * env, jobject object, jlong arrayId){
+ btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(arrayId);
+ delete(array);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.h
new file mode 100644
index 0000000..4dd6aa2
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_MeshCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_MeshCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_MeshCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape
+ * Method: createShape
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_createShape
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp
new file mode 100644
index 0000000..b8c9adc
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_PlaneCollisionShape.h"
+#include "jmeBulletUtil.h"
+#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_PlaneCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;F)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_PlaneCollisionShape_createShape
+ (JNIEnv * env, jobject object, jobject normal, jfloat constant) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 norm = btVector3();
+ jmeBulletUtil::convert(env, normal, &norm);
+ btStaticPlaneShape* shape = new btStaticPlaneShape(norm, constant);
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.h
new file mode 100644
index 0000000..7e7a22b
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_PlaneCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_PlaneCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_PlaneCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_PlaneCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_PlaneCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;F)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_PlaneCollisionShape_createShape
+ (JNIEnv *, jobject, jobject, jfloat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp
new file mode 100644
index 0000000..7337945
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_SimplexCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2
+ (JNIEnv *env, jobject object, jobject vector1) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, vector1, &vec1);
+ btBU_Simplex1to4* simplexShape = new btBU_Simplex1to4(vec1);
+ return reinterpret_cast<jlong>(simplexShape);
+ }
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2
+ (JNIEnv *env, jobject object, jobject vector1, jobject vector2) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, vector1, &vec1);
+ btVector3 vec2 = btVector3();
+ jmeBulletUtil::convert(env, vector2, &vec2);
+ btBU_Simplex1to4* simplexShape = new btBU_Simplex1to4(vec1, vec2);
+ return reinterpret_cast<jlong>(simplexShape);
+ }
+ /*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2
+ (JNIEnv * env, jobject object, jobject vector1, jobject vector2, jobject vector3) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, vector1, &vec1);
+ btVector3 vec2 = btVector3();
+ jmeBulletUtil::convert(env, vector2, &vec2);
+ btVector3 vec3 = btVector3();
+ jmeBulletUtil::convert(env, vector3, &vec3);
+ btBU_Simplex1to4* simplexShape = new btBU_Simplex1to4(vec1, vec2, vec3);
+ return reinterpret_cast<jlong>(simplexShape);
+ }
+ /*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2
+ (JNIEnv * env, jobject object, jobject vector1, jobject vector2, jobject vector3, jobject vector4) {
+ jmeClasses::initJavaClasses(env);
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, vector1, &vec1);
+ btVector3 vec2 = btVector3();
+ jmeBulletUtil::convert(env, vector2, &vec2);
+ btVector3 vec3 = btVector3();
+ jmeBulletUtil::convert(env, vector3, &vec3);
+ btVector3 vec4 = btVector3();
+ jmeBulletUtil::convert(env, vector4, &vec4);
+ btBU_Simplex1to4* simplexShape = new btBU_Simplex1to4(vec1, vec2, vec3, vec4);
+ return reinterpret_cast<jlong>(simplexShape);
+ }
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.h
new file mode 100644
index 0000000..e50d062
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SimplexCollisionShape.h
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_SimplexCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_SimplexCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_SimplexCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2
+ (JNIEnv *, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2
+ (JNIEnv *, jobject, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2
+ (JNIEnv *, jobject, jobject, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_collision_shapes_SimplexCollisionShape
+ * Method: createShape
+ * Signature: (Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SimplexCollisionShape_createShape__Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2Lcom_jme3_math_Vector3f_2
+ (JNIEnv *, jobject, jobject, jobject, jobject, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp
new file mode 100644
index 0000000..8f4b984
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_collision_shapes_SphereCollisionShape.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_collision_shapes_SphereCollisionShape
+ * Method: createShape
+ * Signature: (F)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SphereCollisionShape_createShape
+ (JNIEnv *env, jobject object, jfloat radius) {
+ jmeClasses::initJavaClasses(env);
+ btSphereShape* shape=new btSphereShape(radius);
+ return reinterpret_cast<jlong>(shape);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.h b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.h
new file mode 100644
index 0000000..ef1b2cb
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_SphereCollisionShape.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_collision_shapes_SphereCollisionShape */
+
+#ifndef _Included_com_jme3_bullet_collision_shapes_SphereCollisionShape
+#define _Included_com_jme3_bullet_collision_shapes_SphereCollisionShape
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_collision_shapes_SphereCollisionShape
+ * Method: createShape
+ * Signature: (F)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_SphereCollisionShape_createShape
+ (JNIEnv *, jobject, jfloat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.cpp
new file mode 100644
index 0000000..cade120
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_ConeJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: setLimit
+ * Signature: (JFFF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_ConeJoint_setLimit
+ (JNIEnv * env, jobject object, jlong jointId, jfloat swingSpan1, jfloat swingSpan2, jfloat twistSpan) {
+ btConeTwistConstraint* joint = reinterpret_cast<btConeTwistConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ //TODO: extended setLimit!
+ joint->setLimit(swingSpan1, swingSpan2, twistSpan);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: setAngularOnly
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_ConeJoint_setAngularOnly
+ (JNIEnv * env, jobject object, jlong jointId, jboolean angularOnly) {
+ btConeTwistConstraint* joint = reinterpret_cast<btConeTwistConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setAngularOnly(angularOnly);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_ConeJoint_createJoint
+ (JNIEnv * env, jobject object, jlong bodyIdA, jlong bodyIdB, jobject pivotA, jobject rotA, jobject pivotB, jobject rotB) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* bodyA = reinterpret_cast<btRigidBody*>(bodyIdA);
+ btRigidBody* bodyB = reinterpret_cast<btRigidBody*>(bodyIdB);
+ btMatrix3x3 mtx1 = btMatrix3x3();
+ btMatrix3x3 mtx2 = btMatrix3x3();
+ btTransform transA = btTransform(mtx1);
+ jmeBulletUtil::convert(env, pivotA, &transA.getOrigin());
+ jmeBulletUtil::convert(env, rotA, &transA.getBasis());
+ btTransform transB = btTransform(mtx2);
+ jmeBulletUtil::convert(env, pivotB, &transB.getOrigin());
+ jmeBulletUtil::convert(env, rotB, &transB.getBasis());
+ btConeTwistConstraint* joint = new btConeTwistConstraint(*bodyA, *bodyB, transA, transB);
+ return reinterpret_cast<jlong>(joint);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.h
new file mode 100644
index 0000000..327b47f
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_ConeJoint.h
@@ -0,0 +1,37 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_ConeJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_ConeJoint
+#define _Included_com_jme3_bullet_joints_ConeJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: setLimit
+ * Signature: (JFFF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_ConeJoint_setLimit
+ (JNIEnv *, jobject, jlong, jfloat, jfloat, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: setAngularOnly
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_ConeJoint_setAngularOnly
+ (JNIEnv *, jobject, jlong, jboolean);
+
+/*
+ * Class: com_jme3_bullet_joints_ConeJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_ConeJoint_createJoint
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jobject, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.cpp
new file mode 100644
index 0000000..56a6fec
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_HingeJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: enableMotor
+ * Signature: (JZFF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_enableMotor
+ (JNIEnv * env, jobject object, jlong jointId, jboolean enable, jfloat targetVelocity, jfloat maxMotorImpulse) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->enableAngularMotor(enable, targetVelocity, maxMotorImpulse);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getEnableAngularMotor
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_HingeJoint_getEnableAngularMotor
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return joint->getEnableAngularMotor();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getMotorTargetVelocity
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getMotorTargetVelocity
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getMotorTargetVelosity();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getMaxMotorImpulse
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getMaxMotorImpulse
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getMaxMotorImpulse();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: setLimit
+ * Signature: (JFF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_setLimit__JFF
+ (JNIEnv * env, jobject object, jlong jointId, jfloat low, jfloat high) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ return joint->setLimit(low, high);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: setLimit
+ * Signature: (JFFFFF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_setLimit__JFFFFF
+ (JNIEnv * env, jobject object, jlong jointId, jfloat low, jfloat high, jfloat softness, jfloat biasFactor, jfloat relaxationFactor) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ return joint->setLimit(low, high, softness, biasFactor, relaxationFactor);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getUpperLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getUpperLimit
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getUpperLimit();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getLowerLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getLowerLimit
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getLowerLimit();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: setAngularOnly
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_setAngularOnly
+ (JNIEnv * env, jobject object, jlong jointId, jboolean angular) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setAngularOnly(angular);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getHingeAngle
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getHingeAngle
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btHingeConstraint* joint = reinterpret_cast<btHingeConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getHingeAngle();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_HingeJoint_createJoint
+ (JNIEnv * env, jobject object, jlong bodyIdA, jlong bodyIdB, jobject pivotA, jobject axisA, jobject pivotB, jobject axisB) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* bodyA = reinterpret_cast<btRigidBody*>(bodyIdA);
+ btRigidBody* bodyB = reinterpret_cast<btRigidBody*>(bodyIdB);
+ btVector3 vec1 = btVector3();
+ btVector3 vec2 = btVector3();
+ btVector3 vec3 = btVector3();
+ btVector3 vec4 = btVector3();
+ jmeBulletUtil::convert(env, pivotA, &vec1);
+ jmeBulletUtil::convert(env, pivotB, &vec2);
+ jmeBulletUtil::convert(env, axisA, &vec3);
+ jmeBulletUtil::convert(env, axisB, &vec4);
+ btHingeConstraint* joint = new btHingeConstraint(*bodyA, *bodyB, vec1, vec2, vec3, vec4);
+ return reinterpret_cast<jlong>(joint);
+ }
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.h
new file mode 100644
index 0000000..ab6e003
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_HingeJoint.h
@@ -0,0 +1,101 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_HingeJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_HingeJoint
+#define _Included_com_jme3_bullet_joints_HingeJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: enableMotor
+ * Signature: (JZFF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_enableMotor
+ (JNIEnv *, jobject, jlong, jboolean, jfloat, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getEnableAngularMotor
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_HingeJoint_getEnableAngularMotor
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getMotorTargetVelocity
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getMotorTargetVelocity
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getMaxMotorImpulse
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getMaxMotorImpulse
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: setLimit
+ * Signature: (JFF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_setLimit__JFF
+ (JNIEnv *, jobject, jlong, jfloat, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: setLimit
+ * Signature: (JFFFFF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_setLimit__JFFFFF
+ (JNIEnv *, jobject, jlong, jfloat, jfloat, jfloat, jfloat, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getUpperLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getUpperLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getLowerLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getLowerLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: setAngularOnly
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_HingeJoint_setAngularOnly
+ (JNIEnv *, jobject, jlong, jboolean);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: getHingeAngle
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_HingeJoint_getHingeAngle
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_HingeJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_HingeJoint_createJoint
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jobject, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.cpp
new file mode 100644
index 0000000..c5ae9c1
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_PhysicsJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_PhysicsJoint
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_PhysicsJoint_getAppliedImpulse
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btTypedConstraint* joint = reinterpret_cast<btTypedConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getAppliedImpulse();
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.h
new file mode 100644
index 0000000..63e21a7
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_PhysicsJoint.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_PhysicsJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_PhysicsJoint
+#define _Included_com_jme3_bullet_joints_PhysicsJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_PhysicsJoint
+ * Method: getAppliedImpulse
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_PhysicsJoint_getAppliedImpulse
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_PhysicsJoint
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_PhysicsJoint_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.cpp
new file mode 100644
index 0000000..e019b9b
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_Point2PointJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_setDamping
+ (JNIEnv * env, jobject object, jlong jointId, jfloat damping) {
+ btPoint2PointConstraint* joint = reinterpret_cast<btPoint2PointConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->m_setting.m_damping = damping;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setImpulseClamp
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_setImpulseClamp
+ (JNIEnv * env, jobject object, jlong jointId, jfloat clamp) {
+ btPoint2PointConstraint* joint = reinterpret_cast<btPoint2PointConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->m_setting.m_impulseClamp = clamp;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setTau
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_setTau
+ (JNIEnv * env, jobject object, jlong jointId, jfloat tau) {
+ btPoint2PointConstraint* joint = reinterpret_cast<btPoint2PointConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->m_setting.m_tau = tau;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: getDamping
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_getDamping
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btPoint2PointConstraint* joint = reinterpret_cast<btPoint2PointConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->m_setting.m_damping;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: getImpulseClamp
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_getImpulseClamp
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btPoint2PointConstraint* joint = reinterpret_cast<btPoint2PointConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->m_setting.m_damping;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: getTau
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_getTau
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btPoint2PointConstraint* joint = reinterpret_cast<btPoint2PointConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->m_setting.m_damping;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_createJoint
+ (JNIEnv * env, jobject object, jlong bodyIdA, jlong bodyIdB, jobject pivotA, jobject pivotB) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* bodyA = reinterpret_cast<btRigidBody*>(bodyIdA);
+ btRigidBody* bodyB = reinterpret_cast<btRigidBody*>(bodyIdB);
+ //TODO: matrix not needed?
+ btMatrix3x3 mtx1 = btMatrix3x3();
+ btMatrix3x3 mtx2 = btMatrix3x3();
+ btTransform transA = btTransform(mtx1);
+ jmeBulletUtil::convert(env, pivotA, &transA.getOrigin());
+ btTransform transB = btTransform(mtx2);
+ jmeBulletUtil::convert(env, pivotB, &transB.getOrigin());
+ btHingeConstraint* joint = new btHingeConstraint(*bodyA, *bodyB, transA, transB);
+ return reinterpret_cast<jlong>(joint);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.h
new file mode 100644
index 0000000..5cb8188
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_Point2PointJoint.h
@@ -0,0 +1,69 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_Point2PointJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_Point2PointJoint
+#define _Included_com_jme3_bullet_joints_Point2PointJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_setDamping
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setImpulseClamp
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_setImpulseClamp
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: setTau
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_setTau
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: getDamping
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_getDamping
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: getImpulseClamp
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_getImpulseClamp
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: getTau
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_getTau
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_Point2PointJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_Point2PointJoint_createJoint
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.cpp
new file mode 100644
index 0000000..9611a14
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_SixDofJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: getRotationalLimitMotor
+ * Signature: (JI)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofJoint_getRotationalLimitMotor
+ (JNIEnv * env, jobject object, jlong jointId, jint index) {
+ btGeneric6DofConstraint* joint = reinterpret_cast<btGeneric6DofConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(joint->getRotationalLimitMotor(index));
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: getTranslationalLimitMotor
+ * Signature: (J)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofJoint_getTranslationalLimitMotor
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btGeneric6DofConstraint* joint = reinterpret_cast<btGeneric6DofConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(joint->getTranslationalLimitMotor());
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setLinearUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setLinearUpperLimit
+ (JNIEnv * env, jobject object, jlong jointId, jobject vector) {
+ btGeneric6DofConstraint* joint = reinterpret_cast<btGeneric6DofConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, vector, &vec);
+ joint->setLinearUpperLimit(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setLinearLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setLinearLowerLimit
+ (JNIEnv * env, jobject object, jlong jointId, jobject vector) {
+ btGeneric6DofConstraint* joint = reinterpret_cast<btGeneric6DofConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, vector, &vec);
+ joint->setLinearLowerLimit(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setAngularUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setAngularUpperLimit
+ (JNIEnv * env, jobject object, jlong jointId, jobject vector) {
+ btGeneric6DofConstraint* joint = reinterpret_cast<btGeneric6DofConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, vector, &vec);
+ joint->setAngularUpperLimit(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setAngularLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setAngularLowerLimit
+ (JNIEnv * env, jobject object, jlong jointId, jobject vector) {
+ btGeneric6DofConstraint* joint = reinterpret_cast<btGeneric6DofConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, vector, &vec);
+ joint->setAngularLowerLimit(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Z)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofJoint_createJoint
+ (JNIEnv * env, jobject object, jlong bodyIdA, jlong bodyIdB, jobject pivotA, jobject rotA, jobject pivotB, jobject rotB, jboolean useLinearReferenceFrameA) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* bodyA = reinterpret_cast<btRigidBody*>(bodyIdA);
+ btRigidBody* bodyB = reinterpret_cast<btRigidBody*>(bodyIdB);
+ btMatrix3x3 mtx1 = btMatrix3x3();
+ btMatrix3x3 mtx2 = btMatrix3x3();
+ btTransform transA = btTransform(mtx1);
+ jmeBulletUtil::convert(env, pivotA, &transA.getOrigin());
+ jmeBulletUtil::convert(env, rotA, &transA.getBasis());
+ btTransform transB = btTransform(mtx2);
+ jmeBulletUtil::convert(env, pivotB, &transB.getOrigin());
+ jmeBulletUtil::convert(env, rotB, &transB.getBasis());
+ btGeneric6DofConstraint* joint = new btGeneric6DofConstraint(*bodyA, *bodyB, transA, transB, useLinearReferenceFrameA);
+ return reinterpret_cast<jlong>(joint);
+ }
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.h
new file mode 100644
index 0000000..86e6102
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofJoint.h
@@ -0,0 +1,69 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_SixDofJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_SixDofJoint
+#define _Included_com_jme3_bullet_joints_SixDofJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: getRotationalLimitMotor
+ * Signature: (JI)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofJoint_getRotationalLimitMotor
+ (JNIEnv *, jobject, jlong, jint);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: getTranslationalLimitMotor
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofJoint_getTranslationalLimitMotor
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setLinearUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setLinearUpperLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setLinearLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setLinearLowerLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setAngularUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setAngularUpperLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: setAngularLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_setAngularLowerLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Z)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofJoint_createJoint
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jobject, jobject, jboolean);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.cpp
new file mode 100644
index 0000000..97724c6
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.cpp
@@ -0,0 +1,94 @@
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_SixDofSpringJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: enableString
+ * Signature: (JIZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_enableSpring
+ (JNIEnv *env, jobject object, jlong jointId, jint index, jboolean onOff) {
+ btGeneric6DofSpringConstraint* joint = reinterpret_cast<btGeneric6DofSpringConstraint*>(jointId);
+ joint -> enableSpring(index, onOff);
+}
+
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setStiffness
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setStiffness
+ (JNIEnv *env, jobject object, jlong jointId, jint index, jfloat stiffness) {
+ btGeneric6DofSpringConstraint* joint = reinterpret_cast<btGeneric6DofSpringConstraint*>(jointId);
+ joint -> setStiffness(index, stiffness);
+}
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setDamping
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setDamping
+ (JNIEnv *env, jobject object, jlong jointId, jint index, jfloat damping) {
+ btGeneric6DofSpringConstraint* joint = reinterpret_cast<btGeneric6DofSpringConstraint*>(jointId);
+ joint -> setDamping(index, damping);
+}
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setEquilibriumPoint
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setEquilibriumPoint__J
+ (JNIEnv *env, jobject object, jlong jointId) {
+ btGeneric6DofSpringConstraint* joint = reinterpret_cast<btGeneric6DofSpringConstraint*>(jointId);
+ joint -> setEquilibriumPoint();
+}
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setEquilibriumPoint
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setEquilibriumPoint__JI
+ (JNIEnv *env, jobject object, jlong jointId, jint index) {
+ btGeneric6DofSpringConstraint* joint = reinterpret_cast<btGeneric6DofSpringConstraint*>(jointId);
+ joint -> setEquilibriumPoint(index);
+}
+
+
+
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Z)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_createJoint
+ (JNIEnv * env, jobject object, jlong bodyIdA, jlong bodyIdB, jobject pivotA, jobject rotA, jobject pivotB, jobject rotB, jboolean useLinearReferenceFrameA) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* bodyA = reinterpret_cast<btRigidBody*>(bodyIdA);
+ btRigidBody* bodyB = reinterpret_cast<btRigidBody*>(bodyIdB);
+ btTransform transA;
+ jmeBulletUtil::convert(env, pivotA, &transA.getOrigin());
+ jmeBulletUtil::convert(env, rotA, &transA.getBasis());
+ btTransform transB;
+ jmeBulletUtil::convert(env, pivotB, &transB.getOrigin());
+ jmeBulletUtil::convert(env, rotB, &transB.getBasis());
+
+ btGeneric6DofSpringConstraint* joint = new btGeneric6DofSpringConstraint(*bodyA, *bodyB, transA, transB, useLinearReferenceFrameA);
+ return reinterpret_cast<jlong>(joint);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.h
new file mode 100644
index 0000000..b4fced0
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_SixDofSpringJoint.h
@@ -0,0 +1,61 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_SixDofSpringJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_SixDofSpringJoint
+#define _Included_com_jme3_bullet_joints_SixDofSpringJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: enableSpring
+ * Signature: (JIZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_enableSpring
+ (JNIEnv *, jobject, jlong, jint, jboolean);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setStiffness
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setStiffness
+ (JNIEnv *, jobject, jlong, jint, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setDamping
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setDamping
+ (JNIEnv *, jobject, jlong, jint, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setEquilibriumPoint
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setEquilibriumPoint__J
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: setEquilibriumPoint
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_setEquilibriumPoint__JI
+ (JNIEnv *, jobject, jlong, jint);
+
+/*
+ * Class: com_jme3_bullet_joints_SixDofSpringJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Z)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SixDofSpringJoint_createJoint
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jobject, jobject, jboolean);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.cpp
new file mode 100644
index 0000000..c6e704d
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.cpp
@@ -0,0 +1,963 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_SliderJoint.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getLowerLinLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getLowerLinLimit
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getLowerLinLimit();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setLowerLinLimit
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setLowerLinLimit
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setLowerLinLimit(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getUpperLinLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getUpperLinLimit
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getUpperLinLimit();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setUpperLinLimit
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setUpperLinLimit
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setUpperLinLimit(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getLowerAngLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getLowerAngLimit
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getLowerAngLimit();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setLowerAngLimit
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setLowerAngLimit
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setLowerAngLimit(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getUpperAngLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getUpperAngLimit
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getUpperAngLimit();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setUpperAngLimit
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setUpperAngLimit
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setUpperAngLimit(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessDirLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessDirLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getSoftnessDirLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessDirLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessDirLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setSoftnessDirLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionDirLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionDirLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getRestitutionDirLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionDirLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionDirLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setRestitutionDirLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingDirLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingDirLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getDampingDirLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingDirLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingDirLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setDampingDirLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessDirAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessDirAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getSoftnessDirAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessDirAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessDirAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setSoftnessDirAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionDirAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionDirAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getRestitutionDirAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionDirAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionDirAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setRestitutionDirAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingDirAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingDirAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getDampingDirAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingDirAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingDirAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setDampingDirAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessLimLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessLimLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getSoftnessLimLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessLimLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessLimLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setSoftnessLimLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionLimLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionLimLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getRestitutionLimLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionLimLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionLimLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setRestitutionLimLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingLimLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingLimLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getDampingLimLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingLimLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingLimLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setDampingLimLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessLimAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessLimAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getSoftnessLimAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessLimAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessLimAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setSoftnessLimAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionLimAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionLimAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getRestitutionLimAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionLimAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionLimAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setRestitutionLimAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingLimAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingLimAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getDampingLimAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingLimAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingLimAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setDampingLimAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessOrthoLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessOrthoLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getSoftnessOrthoLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessOrthoLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessOrthoLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setSoftnessOrthoLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionOrthoLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionOrthoLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getRestitutionOrthoLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionOrthoLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionOrthoLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setRestitutionOrthoLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingOrthoLin
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingOrthoLin
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getDampingOrthoLin();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingOrthoLin
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingOrthoLin
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setDampingOrthoLin(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessOrthoAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessOrthoAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getSoftnessOrthoAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessOrthoAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessOrthoAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setSoftnessOrthoAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionOrthoAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionOrthoAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getRestitutionOrthoAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionOrthoAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionOrthoAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setRestitutionOrthoAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingOrthoAng
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingOrthoAng
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getDampingOrthoAng();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingOrthoAng
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingOrthoAng
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setDampingOrthoAng(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: isPoweredLinMotor
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_SliderJoint_isPoweredLinMotor
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return joint->getPoweredLinMotor();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setPoweredLinMotor
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setPoweredLinMotor
+ (JNIEnv * env, jobject object, jlong jointId, jboolean value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setPoweredLinMotor(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getTargetLinMotorVelocity
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getTargetLinMotorVelocity
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getTargetLinMotorVelocity();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setTargetLinMotorVelocity
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setTargetLinMotorVelocity
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setTargetLinMotorVelocity(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getMaxLinMotorForce
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getMaxLinMotorForce
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getMaxLinMotorForce();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setMaxLinMotorForce
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setMaxLinMotorForce
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setMaxLinMotorForce(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: isPoweredAngMotor
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_SliderJoint_isPoweredAngMotor
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return joint->getPoweredAngMotor();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setPoweredAngMotor
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setPoweredAngMotor
+ (JNIEnv * env, jobject object, jlong jointId, jboolean value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setPoweredAngMotor(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getTargetAngMotorVelocity
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getTargetAngMotorVelocity
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getTargetAngMotorVelocity();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setTargetAngMotorVelocity
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setTargetAngMotorVelocity
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setTargetAngMotorVelocity(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getMaxAngMotorForce
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getMaxAngMotorForce
+ (JNIEnv * env, jobject object, jlong jointId) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return joint->getMaxAngMotorForce();
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setMaxAngMotorForce
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setMaxAngMotorForce
+ (JNIEnv * env, jobject object, jlong jointId, jfloat value) {
+ btSliderConstraint* joint = reinterpret_cast<btSliderConstraint*>(jointId);
+ if (joint == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ joint->setMaxAngMotorForce(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Z)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SliderJoint_createJoint
+ (JNIEnv * env, jobject object, jlong bodyIdA, jlong bodyIdB, jobject pivotA, jobject rotA, jobject pivotB, jobject rotB, jboolean useLinearReferenceFrameA) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* bodyA = reinterpret_cast<btRigidBody*>(bodyIdA);
+ btRigidBody* bodyB = reinterpret_cast<btRigidBody*>(bodyIdB);
+ btMatrix3x3 mtx1 = btMatrix3x3();
+ btMatrix3x3 mtx2 = btMatrix3x3();
+ btTransform transA = btTransform(mtx1);
+ jmeBulletUtil::convert(env, pivotA, &transA.getOrigin());
+ jmeBulletUtil::convert(env, rotA, &transA.getBasis());
+ btTransform transB = btTransform(mtx2);
+ jmeBulletUtil::convert(env, pivotB, &transB.getOrigin());
+ jmeBulletUtil::convert(env, rotB, &transB.getBasis());
+ btSliderConstraint* joint = new btSliderConstraint(*bodyA, *bodyB, transA, transB, useLinearReferenceFrameA);
+ return reinterpret_cast<jlong>(joint);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.h b/engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.h
new file mode 100644
index 0000000..7afd66a
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_SliderJoint.h
@@ -0,0 +1,469 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_SliderJoint */
+
+#ifndef _Included_com_jme3_bullet_joints_SliderJoint
+#define _Included_com_jme3_bullet_joints_SliderJoint
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getLowerLinLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getLowerLinLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setLowerLinLimit
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setLowerLinLimit
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getUpperLinLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getUpperLinLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setUpperLinLimit
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setUpperLinLimit
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getLowerAngLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getLowerAngLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setLowerAngLimit
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setLowerAngLimit
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getUpperAngLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getUpperAngLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setUpperAngLimit
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setUpperAngLimit
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessDirLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessDirLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessDirLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessDirLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionDirLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionDirLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionDirLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionDirLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingDirLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingDirLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingDirLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingDirLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessDirAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessDirAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessDirAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessDirAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionDirAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionDirAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionDirAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionDirAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingDirAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingDirAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingDirAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingDirAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessLimLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessLimLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessLimLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessLimLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionLimLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionLimLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionLimLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionLimLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingLimLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingLimLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingLimLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingLimLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessLimAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessLimAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessLimAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessLimAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionLimAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionLimAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionLimAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionLimAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingLimAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingLimAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingLimAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingLimAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessOrthoLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessOrthoLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessOrthoLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessOrthoLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionOrthoLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionOrthoLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionOrthoLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionOrthoLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingOrthoLin
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingOrthoLin
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingOrthoLin
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingOrthoLin
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getSoftnessOrthoAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getSoftnessOrthoAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setSoftnessOrthoAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setSoftnessOrthoAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getRestitutionOrthoAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getRestitutionOrthoAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setRestitutionOrthoAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setRestitutionOrthoAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getDampingOrthoAng
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getDampingOrthoAng
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setDampingOrthoAng
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setDampingOrthoAng
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: isPoweredLinMotor
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_SliderJoint_isPoweredLinMotor
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setPoweredLinMotor
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setPoweredLinMotor
+ (JNIEnv *, jobject, jlong, jboolean);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getTargetLinMotorVelocity
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getTargetLinMotorVelocity
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setTargetLinMotorVelocity
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setTargetLinMotorVelocity
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getMaxLinMotorForce
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getMaxLinMotorForce
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setMaxLinMotorForce
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setMaxLinMotorForce
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: isPoweredAngMotor
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_SliderJoint_isPoweredAngMotor
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setPoweredAngMotor
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setPoweredAngMotor
+ (JNIEnv *, jobject, jlong, jboolean);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getTargetAngMotorVelocity
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getTargetAngMotorVelocity
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setTargetAngMotorVelocity
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setTargetAngMotorVelocity
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: getMaxAngMotorForce
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_SliderJoint_getMaxAngMotorForce
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: setMaxAngMotorForce
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SliderJoint_setMaxAngMotorForce
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_SliderJoint
+ * Method: createJoint
+ * Signature: (JJLcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Matrix3f;Z)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_joints_SliderJoint_createJoint
+ (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jobject, jobject, jboolean);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp
new file mode 100644
index 0000000..e9da298
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_motors_RotationalLimitMotor.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getLoLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getLoLimit
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_loLimit;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setLoLimit
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setLoLimit
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_loLimit = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getHiLimit
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getHiLimit
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_hiLimit;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setHiLimit
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setHiLimit
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_hiLimit = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getTargetVelocity
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getTargetVelocity
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_targetVelocity;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setTargetVelocity
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setTargetVelocity
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_targetVelocity = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getMaxMotorForce
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getMaxMotorForce
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_maxMotorForce;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setMaxMotorForce
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setMaxMotorForce
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_maxMotorForce = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getMaxLimitForce
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getMaxLimitForce
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_maxLimitForce;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setMaxLimitForce
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setMaxLimitForce
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_maxLimitForce = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getDamping
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getDamping
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_damping;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setDamping
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_damping = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getLimitSoftness
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getLimitSoftness
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_limitSoftness;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setLimitSoftness
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setLimitSoftness
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_limitSoftness = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getERP
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getERP
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_stopERP;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setERP
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setERP
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_stopERP = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getBounce
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getBounce
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_bounce;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setBounce
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setBounce
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_bounce = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: isEnableMotor
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_isEnableMotor
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return motor->m_enableMotor;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setEnableMotor
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setEnableMotor
+ (JNIEnv *env, jobject object, jlong motorId, jboolean value) {
+ btRotationalLimitMotor* motor = reinterpret_cast<btRotationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_enableMotor = value;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.h b/engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.h
new file mode 100644
index 0000000..b14bf1d
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_motors_RotationalLimitMotor.h
@@ -0,0 +1,173 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_motors_RotationalLimitMotor */
+
+#ifndef _Included_com_jme3_bullet_joints_motors_RotationalLimitMotor
+#define _Included_com_jme3_bullet_joints_motors_RotationalLimitMotor
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getLoLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getLoLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setLoLimit
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setLoLimit
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getHiLimit
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getHiLimit
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setHiLimit
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setHiLimit
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getTargetVelocity
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getTargetVelocity
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setTargetVelocity
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setTargetVelocity
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getMaxMotorForce
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getMaxMotorForce
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setMaxMotorForce
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setMaxMotorForce
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getMaxLimitForce
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getMaxLimitForce
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setMaxLimitForce
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setMaxLimitForce
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getDamping
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getDamping
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setDamping
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getLimitSoftness
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getLimitSoftness
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setLimitSoftness
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setLimitSoftness
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getERP
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getERP
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setERP
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setERP
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: getBounce
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_getBounce
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setBounce
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setBounce
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: isEnableMotor
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_isEnableMotor
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_RotationalLimitMotor
+ * Method: setEnableMotor
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_RotationalLimitMotor_setEnableMotor
+ (JNIEnv *, jobject, jlong, jboolean);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp b/engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp
new file mode 100644
index 0000000..64f0de0
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_joints_motors_TranslationalLimitMotor.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getLowerLimit
+ (JNIEnv *env, jobject object, jlong motorId, jobject vector) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &motor->m_lowerLimit, vector);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setLowerLimit
+ (JNIEnv *env, jobject object, jlong motorId, jobject vector) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, vector, &motor->m_lowerLimit);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getUpperLimit
+ (JNIEnv *env, jobject object, jlong motorId, jobject vector) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &motor->m_upperLimit, vector);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setUpperLimit
+ (JNIEnv *env, jobject object, jlong motorId, jobject vector) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, vector, &motor->m_upperLimit);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getAccumulatedImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getAccumulatedImpulse
+ (JNIEnv *env, jobject object, jlong motorId, jobject vector) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &motor->m_accumulatedImpulse, vector);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setAccumulatedImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setAccumulatedImpulse
+ (JNIEnv *env, jobject object, jlong motorId, jobject vector) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, vector, &motor->m_accumulatedImpulse);
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getLimitSoftness
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getLetLimitSoftness
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_limitSoftness;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setLimitSoftness
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setLimitSoftness
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_limitSoftness = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getDamping
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getDamping
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_damping;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setDamping
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_damping = value;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getRestitution
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getRestitution
+ (JNIEnv *env, jobject object, jlong motorId) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return motor->m_restitution;
+ }
+
+ /*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setRestitution
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setRestitution
+ (JNIEnv *env, jobject object, jlong motorId, jfloat value) {
+ btTranslationalLimitMotor* motor = reinterpret_cast<btTranslationalLimitMotor*>(motorId);
+ if (motor == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ motor->m_restitution = value;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.h b/engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.h
new file mode 100644
index 0000000..0ea93e2
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_joints_motors_TranslationalLimitMotor.h
@@ -0,0 +1,109 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_joints_motors_TranslationalLimitMotor */
+
+#ifndef _Included_com_jme3_bullet_joints_motors_TranslationalLimitMotor
+#define _Included_com_jme3_bullet_joints_motors_TranslationalLimitMotor
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getLowerLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setLowerLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setLowerLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getUpperLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setUpperLimit
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setUpperLimit
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getAccumulatedImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getAccumulatedImpulse
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setAccumulatedImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setAccumulatedImpulse
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getLimitSoftness
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getLimitSoftness
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setLimitSoftness
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setLimitSoftness
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getDamping
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getDamping
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setDamping
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setDamping
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: getRestitution
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_getRestitution
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_joints_motors_TranslationalLimitMotor
+ * Method: setRestitution
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setRestitution
+ (JNIEnv *, jobject, jlong, jfloat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.cpp b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.cpp
new file mode 100644
index 0000000..f64cc3a
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+
+#include "com_jme3_bullet_objects_PhysicsCharacter.h"
+#include "jmeBulletUtil.h"
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include "BulletDynamics/Character/btKinematicCharacterController.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: createGhostObject
+ * Signature: ()J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_createGhostObject
+ (JNIEnv * env, jobject object) {
+ jmeClasses::initJavaClasses(env);
+ btPairCachingGhostObject* ghost = new btPairCachingGhostObject();
+ return reinterpret_cast<jlong>(ghost);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setCharacterFlags
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setCharacterFlags
+ (JNIEnv *env, jobject object, jlong ghostId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(ghostId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ ghost->setCollisionFlags(/*ghost->getCollisionFlags() |*/ btCollisionObject::CF_CHARACTER_OBJECT);
+ ghost->setCollisionFlags(ghost->getCollisionFlags() & ~btCollisionObject::CF_NO_CONTACT_RESPONSE);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: createCharacterObject
+ * Signature: (JJF)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_createCharacterObject
+ (JNIEnv *env, jobject object, jlong objectId, jlong shapeId, jfloat stepHeight) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ //TODO: check convexshape!
+ btConvexShape* shape = reinterpret_cast<btConvexShape*>(shapeId);
+ btKinematicCharacterController* character = new btKinematicCharacterController(ghost, shape, stepHeight);
+ return reinterpret_cast<jlong>(character);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: warp
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_warp
+ (JNIEnv *env, jobject object, jlong objectId, jobject vector) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, vector, &vec);
+ character->warp(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setWalkDirection
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setWalkDirection
+ (JNIEnv *env, jobject object, jlong objectId, jobject vector) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, vector, &vec);
+ character->setWalkDirection(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setUpAxis
+ * Signature: (JI)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setUpAxis
+ (JNIEnv *env, jobject object, jlong objectId, jint value) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ character->setUpAxis(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setFallSpeed
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setFallSpeed
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ character->setFallSpeed(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setJumpSpeed
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setJumpSpeed
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ character->setJumpSpeed(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setGravity
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setGravity
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ character->setGravity(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getGravity
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getGravity
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return character->getGravity();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setMaxSlope
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setMaxSlope
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ character->setMaxSlope(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getMaxSlope
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getMaxSlope
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return character->getMaxSlope();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: onGround
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_onGround
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return character->onGround();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: jump
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_jump
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ character->jump();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getPhysicsLocation
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btGhostObject* ghost = reinterpret_cast<btGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &ghost->getWorldTransform().getOrigin(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setCcdSweptSphereRadius
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setCcdSweptSphereRadius
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btGhostObject* ghost = reinterpret_cast<btGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ ghost->setCcdSweptSphereRadius(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setCcdMotionThreshold
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setCcdMotionThreshold
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btGhostObject* ghost = reinterpret_cast<btGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ ghost->setCcdMotionThreshold(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getCcdSweptSphereRadius
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getCcdSweptSphereRadius
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btGhostObject* ghost = reinterpret_cast<btGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getCcdSweptSphereRadius();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getCcdMotionThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getCcdMotionThreshold
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btGhostObject* ghost = reinterpret_cast<btGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getCcdMotionThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getCcdSquareMotionThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getCcdSquareMotionThreshold
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btGhostObject* ghost = reinterpret_cast<btGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getCcdSquareMotionThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: finalizeNativeCharacter
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_finalizeNativeCharacter
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btKinematicCharacterController* character = reinterpret_cast<btKinematicCharacterController*>(objectId);
+ if (character == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ delete(character);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.h b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.h
new file mode 100644
index 0000000..210198c
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsCharacter.h
@@ -0,0 +1,215 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_objects_PhysicsCharacter */
+
+#ifndef _Included_com_jme3_bullet_objects_PhysicsCharacter
+#define _Included_com_jme3_bullet_objects_PhysicsCharacter
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_NONE
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_NONE 0L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_01
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_01 1L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_02
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_02 2L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_03
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_03 4L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_04
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_04 8L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_05
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_05 16L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_06
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_06 32L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_07
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_07 64L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_08
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_08 128L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_09
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_09 256L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_10
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_10 512L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_11
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_11 1024L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_12
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_12 2048L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_13
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_13 4096L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_14
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_14 8192L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_15
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_15 16384L
+#undef com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_16
+#define com_jme3_bullet_objects_PhysicsCharacter_COLLISION_GROUP_16 32768L
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: createGhostObject
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_createGhostObject
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setCharacterFlags
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setCharacterFlags
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: createCharacterObject
+ * Signature: (JJF)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_createCharacterObject
+ (JNIEnv *, jobject, jlong, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: warp
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_warp
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setWalkDirection
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setWalkDirection
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setUpAxis
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setUpAxis
+ (JNIEnv *, jobject, jlong, jint);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setFallSpeed
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setFallSpeed
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setJumpSpeed
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setJumpSpeed
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setGravity
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setGravity
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getGravity
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getGravity
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setMaxSlope
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setMaxSlope
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getMaxSlope
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getMaxSlope
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: onGround
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_onGround
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: jump
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_jump
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getPhysicsLocation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setCcdSweptSphereRadius
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setCcdSweptSphereRadius
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: setCcdMotionThreshold
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_setCcdMotionThreshold
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getCcdSweptSphereRadius
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getCcdSweptSphereRadius
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getCcdMotionThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getCcdMotionThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: getCcdSquareMotionThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_getCcdSquareMotionThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsCharacter
+ * Method: finalizeNativeCharacter
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsCharacter_finalizeNativeCharacter
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.cpp b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.cpp
new file mode 100644
index 0000000..2fb48f5
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+
+#include <BulletCollision/CollisionDispatch/btGhostObject.h>
+
+#include "com_jme3_bullet_objects_PhysicsGhostObject.h"
+#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
+#include "jmeBulletUtil.h"
+#include "jmePhysicsSpace.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: createGhostObject
+ * Signature: ()J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_createGhostObject
+ (JNIEnv * env, jobject object) {
+ jmeClasses::initJavaClasses(env);
+ btPairCachingGhostObject* ghost = new btPairCachingGhostObject();
+ return reinterpret_cast<jlong>(ghost);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setGhostFlags
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setGhostFlags
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ ghost->setCollisionFlags(ghost->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setPhysicsLocation
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, value, &ghost->getWorldTransform().getOrigin());
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setPhysicsRotation__JLcom_jme3_math_Matrix3f_2
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, value, &ghost->getWorldTransform().getBasis());
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setPhysicsRotation__JLcom_jme3_math_Quaternion_2
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convertQuat(env, value, &ghost->getWorldTransform().getBasis());
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getPhysicsLocation
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &ghost->getWorldTransform().getOrigin(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getPhysicsRotation
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convertQuat(env, &ghost->getWorldTransform().getBasis(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getPhysicsRotationMatrix
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getPhysicsRotationMatrix
+ (JNIEnv *env, jobject object, jlong objectId, jobject value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &ghost->getWorldTransform().getBasis(), value);
+ }
+
+ class jmeGhostOverlapCallback : public btOverlapCallback {
+ JNIEnv* m_env;
+ jobject m_object;
+ public:
+ jmeGhostOverlapCallback(JNIEnv *env, jobject object)
+ :m_env(env),
+ m_object(object)
+ {
+ }
+ virtual ~jmeGhostOverlapCallback() {}
+ virtual bool processOverlap(btBroadphasePair& pair)
+ {
+ btCollisionObject *co1 = (btCollisionObject *)pair.m_pProxy1->m_clientObject;
+ jmeUserPointer *up1 = (jmeUserPointer*)co1 -> getUserPointer();
+ jobject javaCollisionObject1 = m_env->NewLocalRef(up1->javaCollisionObject);
+ m_env->CallVoidMethod(m_object, jmeClasses::PhysicsGhostObject_addOverlappingObject, javaCollisionObject1);
+ m_env->DeleteLocalRef(javaCollisionObject1);
+ if (m_env->ExceptionCheck()) {
+ m_env->Throw(m_env->ExceptionOccurred());
+ return false;
+ }
+
+ return false;
+ }
+ };
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getOverlappingObjects
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getOverlappingObjects
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btHashedOverlappingPairCache * pc = ghost->getOverlappingPairCache();
+ jmeGhostOverlapCallback cb(env, object);
+ pc -> processAllOverlappingPairs(&cb, NULL);
+ }
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getOverlappingCount
+ * Signature: (J)I
+ */
+ JNIEXPORT jint JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getOverlappingCount
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getNumOverlappingObjects();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setCcdSweptSphereRadius
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setCcdSweptSphereRadius
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ ghost->setCcdSweptSphereRadius(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setCcdMotionThreshold
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setCcdMotionThreshold
+ (JNIEnv *env, jobject object, jlong objectId, jfloat value) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ ghost->setCcdMotionThreshold(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getCcdSweptSphereRadius
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getCcdSweptSphereRadius
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getCcdSweptSphereRadius();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getCcdMotionThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getCcdMotionThreshold
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getCcdMotionThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getCcdSquareMotionThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getCcdSquareMotionThreshold
+ (JNIEnv *env, jobject object, jlong objectId) {
+ btPairCachingGhostObject* ghost = reinterpret_cast<btPairCachingGhostObject*>(objectId);
+ if (ghost == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return ghost->getCcdSquareMotionThreshold();
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.h b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.h
new file mode 100644
index 0000000..cf98e59
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsGhostObject.h
@@ -0,0 +1,167 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_objects_PhysicsGhostObject */
+
+#ifndef _Included_com_jme3_bullet_objects_PhysicsGhostObject
+#define _Included_com_jme3_bullet_objects_PhysicsGhostObject
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_NONE
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_NONE 0L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_01
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_01 1L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_02
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_02 2L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_03
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_03 4L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_04
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_04 8L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_05
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_05 16L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_06
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_06 32L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_07
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_07 64L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_08
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_08 128L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_09
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_09 256L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_10
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_10 512L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_11
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_11 1024L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_12
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_12 2048L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_13
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_13 4096L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_14
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_14 8192L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_15
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_15 16384L
+#undef com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_16
+#define com_jme3_bullet_objects_PhysicsGhostObject_COLLISION_GROUP_16 32768L
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: createGhostObject
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_createGhostObject
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setGhostFlags
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setGhostFlags
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setPhysicsLocation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setPhysicsRotation__JLcom_jme3_math_Matrix3f_2
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setPhysicsRotation__JLcom_jme3_math_Quaternion_2
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getPhysicsLocation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getPhysicsRotation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getPhysicsRotationMatrix
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getPhysicsRotationMatrix
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getOverlappingObjects
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getOverlappingObjects
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getOverlappingCount
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getOverlappingCount
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setCcdSweptSphereRadius
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setCcdSweptSphereRadius
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: setCcdMotionThreshold
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_setCcdMotionThreshold
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getCcdSweptSphereRadius
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getCcdSweptSphereRadius
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getCcdMotionThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getCcdMotionThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsGhostObject
+ * Method: getCcdSquareMotionThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsGhostObject_getCcdSquareMotionThreshold
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.cpp b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.cpp
new file mode 100644
index 0000000..4794cde
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.cpp
@@ -0,0 +1,849 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_objects_PhysicsRigidBody.h"
+#include "jmeBulletUtil.h"
+#include "jmeMotionState.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: createRigidBody
+ * Signature: (FJJ)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_createRigidBody
+ (JNIEnv *env, jobject object, jfloat mass, jlong motionstatId, jlong shapeId) {
+ jmeClasses::initJavaClasses(env);
+ btMotionState* motionState = reinterpret_cast<btMotionState*>(motionstatId);
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ btVector3 localInertia = btVector3();
+ shape->calculateLocalInertia(mass, localInertia);
+ btRigidBody* body = new btRigidBody(mass, motionState, shape, localInertia);
+ body->setUserPointer(NULL);
+ return reinterpret_cast<jlong>(body);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: isInWorld
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_isInWorld
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return body->isInWorld();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setPhysicsLocation
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ // if (body->isStaticOrKinematicObject() || !body->isInWorld())
+ ((jmeMotionState*) body->getMotionState())->setKinematicLocation(env, value);
+ body->setCenterOfMassTransform(((jmeMotionState*) body->getMotionState())->worldTransform);
+ // else{
+ // btMatrix3x3* mtx = &btMatrix3x3();
+ // btTransform* trans = &btTransform(*mtx);
+ // trans->setBasis(body->getCenterOfMassTransform().getBasis());
+ // jmeBulletUtil::convert(env, value, &trans->getOrigin());
+ // body->setCenterOfMassTransform(*trans);
+ // }
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setPhysicsRotation__JLcom_jme3_math_Matrix3f_2
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ // if (body->isStaticOrKinematicObject() || !body->isInWorld())
+ ((jmeMotionState*) body->getMotionState())->setKinematicRotation(env, value);
+ body->setCenterOfMassTransform(((jmeMotionState*) body->getMotionState())->worldTransform);
+ // else{
+ // btMatrix3x3* mtx = &btMatrix3x3();
+ // btTransform* trans = &btTransform(*mtx);
+ // trans->setOrigin(body->getCenterOfMassTransform().getOrigin());
+ // jmeBulletUtil::convert(env, value, &trans->getBasis());
+ // body->setCenterOfMassTransform(*trans);
+ // }
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setPhysicsRotation__JLcom_jme3_math_Quaternion_2
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ // if (body->isStaticOrKinematicObject() || !body->isInWorld())
+ ((jmeMotionState*) body->getMotionState())->setKinematicRotationQuat(env, value);
+ body->setCenterOfMassTransform(((jmeMotionState*) body->getMotionState())->worldTransform);
+ // else{
+ // btMatrix3x3* mtx = &btMatrix3x3();
+ // btTransform* trans = &btTransform(*mtx);
+ // trans->setOrigin(body->getCenterOfMassTransform().getOrigin());
+ // jmeBulletUtil::convertQuat(env, value, &trans->getBasis());
+ // body->setCenterOfMassTransform(*trans);
+ // }
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getPhysicsLocation
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &body->getWorldTransform().getOrigin(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getPhysicsRotation
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convertQuat(env, &body->getWorldTransform().getBasis(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getPhysicsRotationMatrix
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getPhysicsRotationMatrix
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &body->getWorldTransform().getBasis(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setKinematic
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setKinematic
+ (JNIEnv *env, jobject object, jlong bodyId, jboolean value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ if (value) {
+ body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
+ body->setActivationState(DISABLE_DEACTIVATION);
+ } else {
+ body->setCollisionFlags(body->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
+ body->setActivationState(ACTIVE_TAG);
+ }
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setCcdSweptSphereRadius
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setCcdSweptSphereRadius
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setCcdSweptSphereRadius(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setCcdMotionThreshold
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setCcdMotionThreshold
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setCcdMotionThreshold(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getCcdSweptSphereRadius
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getCcdSweptSphereRadius
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getCcdSweptSphereRadius();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getCcdMotionThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getCcdMotionThreshold
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getCcdMotionThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getCcdSquareMotionThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getCcdSquareMotionThreshold
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getCcdSquareMotionThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setStatic
+ * Signature: (JZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setStatic
+ (JNIEnv *env, jobject object, jlong bodyId, jboolean value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ if (value) {
+ body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT);
+ } else {
+ body->setCollisionFlags(body->getCollisionFlags() & ~btCollisionObject::CF_STATIC_OBJECT);
+ }
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: updateMassProps
+ * Signature: (JJF)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_updateMassProps
+ (JNIEnv *env, jobject object, jlong bodyId, jlong shapeId, jfloat mass) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ btVector3 localInertia = btVector3();
+ shape->calculateLocalInertia(mass, localInertia);
+ body->setMassProps(mass, localInertia);
+ return reinterpret_cast<jlong>(body);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getGravity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getGravity
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &body->getGravity(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setGravity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setGravity
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, value, &vec);
+ body->setGravity(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getFriction
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getFriction
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getFriction();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setFriction
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setFriction
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setFriction(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setDamping
+ * Signature: (JFF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setDamping
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value1, jfloat value2) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setDamping(value1, value2);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularDamping
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularDamping
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setDamping(body->getAngularDamping(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getLinearDamping
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearDamping
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getLinearDamping();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularDamping
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularDamping
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getAngularDamping();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getRestitution
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getRestitution
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getRestitution();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setRestitution
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setRestitution
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setRestitution(value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularVelocity
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &body->getAngularVelocity(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularVelocity
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, value, &vec);
+ body->setAngularVelocity(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getLinearVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearVelocity
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &body->getLinearVelocity(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setLinearVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setLinearVelocity
+ (JNIEnv *env, jobject object, jlong bodyId, jobject value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec = btVector3();
+ jmeBulletUtil::convert(env, value, &vec);
+ body->setLinearVelocity(vec);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyForce
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyForce
+ (JNIEnv *env, jobject object, jlong bodyId, jobject force, jobject location) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec1 = btVector3();
+ btVector3 vec2 = btVector3();
+ jmeBulletUtil::convert(env, force, &vec1);
+ jmeBulletUtil::convert(env, location, &vec2);
+ body->applyForce(vec1, vec2);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyCentralForce
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyCentralForce
+ (JNIEnv *env, jobject object, jlong bodyId, jobject force) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, force, &vec1);
+ body->applyCentralForce(vec1);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyTorque
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyTorque
+ (JNIEnv *env, jobject object, jlong bodyId, jobject force) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, force, &vec1);
+ body->applyTorque(vec1);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyImpulse
+ (JNIEnv *env, jobject object, jlong bodyId, jobject force, jobject location) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec1 = btVector3();
+ btVector3 vec2 = btVector3();
+ jmeBulletUtil::convert(env, force, &vec1);
+ jmeBulletUtil::convert(env, location, &vec2);
+ body->applyImpulse(vec1, vec2);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyTorqueImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyTorqueImpulse
+ (JNIEnv *env, jobject object, jlong bodyId, jobject force) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec1 = btVector3();
+ jmeBulletUtil::convert(env, force, &vec1);
+ body->applyTorqueImpulse(vec1);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: clearForces
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_clearForces
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->clearForces();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setCollisionShape
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setCollisionShape
+ (JNIEnv *env, jobject object, jlong bodyId, jlong shapeId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ body->setCollisionShape(shape);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: activate
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_activate
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->activate(false);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: isActive
+ * Signature: (J)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_isActive
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return body->isActive();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setSleepingThresholds
+ * Signature: (JFF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setSleepingThresholds
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat linear, jfloat angular) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setSleepingThresholds(linear, angular);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setLinearSleepingThreshold
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setLinearSleepingThreshold
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setSleepingThresholds(value, body->getLinearSleepingThreshold());
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularSleepingThreshold
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularSleepingThreshold
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ body->setSleepingThresholds(body->getAngularSleepingThreshold(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getLinearSleepingThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearSleepingThreshold
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getLinearSleepingThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularSleepingThreshold
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularSleepingThreshold
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getAngularSleepingThreshold();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularFactor
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
+ (JNIEnv *env, jobject object, jlong bodyId) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return body->getAngularFactor().getX();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularFactor
+ * Signature: (JF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularFactor
+ (JNIEnv *env, jobject object, jlong bodyId, jfloat value) {
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 vec1 = btVector3();
+ vec1.setX(value);
+ vec1.setY(value);
+ vec1.setZ(value);
+ body->setAngularFactor(vec1);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.h b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.h
new file mode 100644
index 0000000..aa09a62
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsRigidBody.h
@@ -0,0 +1,415 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_objects_PhysicsRigidBody */
+
+#ifndef _Included_com_jme3_bullet_objects_PhysicsRigidBody
+#define _Included_com_jme3_bullet_objects_PhysicsRigidBody
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_NONE
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_NONE 0L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_01
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_01 1L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_02
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_02 2L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_03
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_03 4L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_04
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_04 8L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_05
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_05 16L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_06
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_06 32L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_07
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_07 64L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_08
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_08 128L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_09
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_09 256L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_10
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_10 512L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_11
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_11 1024L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_12
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_12 2048L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_13
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_13 4096L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_14
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_14 8192L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_15
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_15 16384L
+#undef com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_16
+#define com_jme3_bullet_objects_PhysicsRigidBody_COLLISION_GROUP_16 32768L
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: createRigidBody
+ * Signature: (FJJ)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_createRigidBody
+ (JNIEnv *, jobject, jfloat, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: isInWorld
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_isInWorld
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setPhysicsLocation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setPhysicsRotation__JLcom_jme3_math_Matrix3f_2
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setPhysicsRotation__JLcom_jme3_math_Quaternion_2
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getPhysicsLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getPhysicsLocation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getPhysicsRotation
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getPhysicsRotation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getPhysicsRotationMatrix
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getPhysicsRotationMatrix
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setKinematic
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setKinematic
+ (JNIEnv *, jobject, jlong, jboolean);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setCcdSweptSphereRadius
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setCcdSweptSphereRadius
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setCcdMotionThreshold
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setCcdMotionThreshold
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getCcdSweptSphereRadius
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getCcdSweptSphereRadius
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getCcdMotionThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getCcdMotionThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getCcdSquareMotionThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getCcdSquareMotionThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setStatic
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setStatic
+ (JNIEnv *, jobject, jlong, jboolean);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: updateMassProps
+ * Signature: (JJF)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_updateMassProps
+ (JNIEnv *, jobject, jlong, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getGravity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getGravity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setGravity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setGravity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getFriction
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getFriction
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setFriction
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setFriction
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setDamping
+ * Signature: (JFF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setDamping
+ (JNIEnv *, jobject, jlong, jfloat, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularDamping
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularDamping
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getLinearDamping
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearDamping
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularDamping
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularDamping
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getRestitution
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getRestitution
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setRestitution
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setRestitution
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularVelocity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularVelocity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getLinearVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearVelocity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setLinearVelocity
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setLinearVelocity
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyForce
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyForce
+ (JNIEnv *, jobject, jlong, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyCentralForce
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyCentralForce
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyTorque
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyTorque
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyImpulse
+ (JNIEnv *, jobject, jlong, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: applyTorqueImpulse
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_applyTorqueImpulse
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: clearForces
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_clearForces
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setCollisionShape
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setCollisionShape
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: activate
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_activate
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: isActive
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_isActive
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setSleepingThresholds
+ * Signature: (JFF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setSleepingThresholds
+ (JNIEnv *, jobject, jlong, jfloat, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setLinearSleepingThreshold
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setLinearSleepingThreshold
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularSleepingThreshold
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularSleepingThreshold
+ (JNIEnv *, jobject, jlong, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getLinearSleepingThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearSleepingThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularSleepingThreshold
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularSleepingThreshold
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: getAngularFactor
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsRigidBody
+ * Method: setAngularFactor
+ * Signature: (JF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularFactor
+ (JNIEnv *, jobject, jlong, jfloat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.cpp b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.cpp
new file mode 100644
index 0000000..b96f08b
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+
+#include "com_jme3_bullet_objects_PhysicsVehicle.h"
+#include "jmeBulletUtil.h"
+#include "jmePhysicsSpace.h"
+#include "BulletDynamics/Vehicle/btRaycastVehicle.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: updateWheelTransform
+ * Signature: (JIZ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_updateWheelTransform
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheel, jboolean interpolated) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ vehicle->updateWheelTransform(wheel, interpolated);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: createVehicleRaycaster
+ * Signature: (JJ)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_createVehicleRaycaster
+ (JNIEnv *env, jobject object, jlong bodyId, jlong spaceId) {
+ //btRigidBody* body = reinterpret_cast<btRigidBody*> bodyId;
+ jmeClasses::initJavaClasses(env);
+ jmePhysicsSpace *space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
+ if (space == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btDefaultVehicleRaycaster* caster = new btDefaultVehicleRaycaster(space->getDynamicsWorld());
+ return reinterpret_cast<jlong>(caster);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: createRaycastVehicle
+ * Signature: (JJ)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_createRaycastVehicle
+ (JNIEnv *env, jobject object, jlong objectId, jlong casterId) {
+ jmeClasses::initJavaClasses(env);
+ btRigidBody* body = reinterpret_cast<btRigidBody*>(objectId);
+ if (body == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ body->setActivationState(DISABLE_DEACTIVATION);
+ btVehicleRaycaster* caster = reinterpret_cast<btDefaultVehicleRaycaster*>(casterId);
+ if (caster == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btRaycastVehicle::btVehicleTuning tuning;
+ btRaycastVehicle* vehicle = new btRaycastVehicle(tuning, body, caster);
+ return reinterpret_cast<jlong>(vehicle);
+
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: setCoordinateSystem
+ * Signature: (JIII)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_setCoordinateSystem
+ (JNIEnv *env, jobject object, jlong vehicleId, jint right, jint up, jint forward) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ vehicle->setCoordinateSystem(right, up, forward);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: addWheel
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;FFLcom/jme3/bullet/objects/infos/VehicleTuning;Z)J
+ */
+ JNIEXPORT jint JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_addWheel
+ (JNIEnv *env, jobject object, jlong vehicleId, jobject location, jobject direction, jobject axle, jfloat restLength, jfloat radius, jobject tuning, jboolean frontWheel) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ btVector3 vec1 = btVector3();
+ btVector3 vec2 = btVector3();
+ btVector3 vec3 = btVector3();
+ jmeBulletUtil::convert(env, location, &vec1);
+ jmeBulletUtil::convert(env, direction, &vec2);
+ jmeBulletUtil::convert(env, axle, &vec3);
+ btRaycastVehicle::btVehicleTuning tune;
+ btWheelInfo* info = &vehicle->addWheel(vec1, vec2, vec3, restLength, radius, tune, frontWheel);
+ int idx = vehicle->getNumWheels();
+ return idx-1;
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: resetSuspension
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_resetSuspension
+ (JNIEnv *env, jobject object, jlong vehicleId) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ vehicle->resetSuspension();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: applyEngineForce
+ * Signature: (JIF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_applyEngineForce
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheel, jfloat force) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ vehicle->applyEngineForce(force, wheel);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: steer
+ * Signature: (JIF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_steer
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheel, jfloat value) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ vehicle->setSteeringValue(value, wheel);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: brake
+ * Signature: (JIF)F
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_brake
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheel, jfloat value) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ vehicle->setBrake(value, wheel);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: getCurrentVehicleSpeedKmHour
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_getCurrentVehicleSpeedKmHour
+ (JNIEnv *env, jobject object, jlong vehicleId) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return vehicle->getCurrentSpeedKmHour();
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: getForwardVector
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_getForwardVector
+ (JNIEnv *env, jobject object, jlong vehicleId, jobject out) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ btVector3 forwardVector = vehicle->getForwardVector();
+ jmeBulletUtil::convert(env, &forwardVector, out);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: finalizeNative
+ * Signature: (JJ)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_finalizeNative
+ (JNIEnv *env, jobject object, jlong casterId, jlong vehicleId) {
+ btVehicleRaycaster* rayCaster = reinterpret_cast<btVehicleRaycaster*>(casterId);
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ delete(vehicle);
+ if (rayCaster == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ delete(rayCaster);
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.h b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.h
new file mode 100644
index 0000000..821e384
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_PhysicsVehicle.h
@@ -0,0 +1,143 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_objects_PhysicsVehicle */
+
+#ifndef _Included_com_jme3_bullet_objects_PhysicsVehicle
+#define _Included_com_jme3_bullet_objects_PhysicsVehicle
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_NONE
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_NONE 0L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_01
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_01 1L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_02
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_02 2L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_03
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_03 4L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_04
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_04 8L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_05
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_05 16L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_06
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_06 32L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_07
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_07 64L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_08
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_08 128L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_09
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_09 256L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_10
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_10 512L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_11
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_11 1024L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_12
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_12 2048L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_13
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_13 4096L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_14
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_14 8192L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_15
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_15 16384L
+#undef com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_16
+#define com_jme3_bullet_objects_PhysicsVehicle_COLLISION_GROUP_16 32768L
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: updateWheelTransform
+ * Signature: (JIZ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_updateWheelTransform
+ (JNIEnv *, jobject, jlong, jint, jboolean);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: createVehicleRaycaster
+ * Signature: (JJ)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_createVehicleRaycaster
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: createRaycastVehicle
+ * Signature: (JJ)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_createRaycastVehicle
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: setCoordinateSystem
+ * Signature: (JIII)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_setCoordinateSystem
+ (JNIEnv *, jobject, jlong, jint, jint, jint);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: addWheel
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;Lcom/jme3/math/Vector3f;FFLcom/jme3/bullet/objects/infos/VehicleTuning;Z)I
+ */
+JNIEXPORT jint JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_addWheel
+ (JNIEnv *, jobject, jlong, jobject, jobject, jobject, jfloat, jfloat, jobject, jboolean);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: resetSuspension
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_resetSuspension
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: applyEngineForce
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_applyEngineForce
+ (JNIEnv *, jobject, jlong, jint, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: steer
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_steer
+ (JNIEnv *, jobject, jlong, jint, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: brake
+ * Signature: (JIF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_brake
+ (JNIEnv *, jobject, jlong, jint, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: getCurrentVehicleSpeedKmHour
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_getCurrentVehicleSpeedKmHour
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: getForwardVector
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_getForwardVector
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_PhysicsVehicle
+ * Method: finalizeNative
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_finalizeNative
+ (JNIEnv *, jobject, jlong, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.cpp b/engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.cpp
new file mode 100644
index 0000000..f05e57f
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+
+#include "com_jme3_bullet_objects_VehicleWheel.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getWheelLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getWheelLocation
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex, jobject out) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &vehicle->getWheelInfo(wheelIndex).m_worldTransform.getOrigin(), out);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getWheelRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getWheelRotation
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex, jobject out) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &vehicle->getWheelInfo(wheelIndex).m_worldTransform.getBasis(), out);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: applyInfo
+ * Signature: (JFFFFFFFFZF)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_applyInfo
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex, jfloat suspensionStiffness, jfloat wheelsDampingRelaxation, jfloat wheelsDampingCompression, jfloat frictionSlip, jfloat rollInfluence, jfloat maxSuspensionTravelCm, jfloat maxSuspensionForce, jfloat radius, jboolean frontWheel, jfloat restLength) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ vehicle->getWheelInfo(wheelIndex).m_suspensionStiffness = suspensionStiffness;
+ vehicle->getWheelInfo(wheelIndex).m_wheelsDampingRelaxation = wheelsDampingRelaxation;
+ vehicle->getWheelInfo(wheelIndex).m_wheelsDampingCompression = wheelsDampingCompression;
+ vehicle->getWheelInfo(wheelIndex).m_frictionSlip = frictionSlip;
+ vehicle->getWheelInfo(wheelIndex).m_rollInfluence = rollInfluence;
+ vehicle->getWheelInfo(wheelIndex).m_maxSuspensionTravelCm = maxSuspensionTravelCm;
+ vehicle->getWheelInfo(wheelIndex).m_maxSuspensionForce = maxSuspensionForce;
+ vehicle->getWheelInfo(wheelIndex).m_wheelsRadius = radius;
+ vehicle->getWheelInfo(wheelIndex).m_bIsFrontWheel = frontWheel;
+ vehicle->getWheelInfo(wheelIndex).m_suspensionRestLength1 = restLength;
+
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getCollisionLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getCollisionLocation
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex, jobject out) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &vehicle->getWheelInfo(wheelIndex).m_raycastInfo.m_contactPointWS, out);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getCollisionNormal
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getCollisionNormal
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex, jobject out) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &vehicle->getWheelInfo(wheelIndex).m_raycastInfo.m_contactNormalWS, out);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getSkidInfo
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getSkidInfo
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return vehicle->getWheelInfo(wheelIndex).m_skidInfo;
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getDeltaRotation
+ * Signature: (J)F
+ */
+ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getDeltaRotation
+ (JNIEnv *env, jobject object, jlong vehicleId, jint wheelIndex) {
+ btRaycastVehicle* vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);
+ if (vehicle == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return 0;
+ }
+ return vehicle->getWheelInfo(wheelIndex).m_deltaRotation;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.h b/engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.h
new file mode 100644
index 0000000..f4ab208
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_VehicleWheel.h
@@ -0,0 +1,69 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_objects_VehicleWheel */
+
+#ifndef _Included_com_jme3_bullet_objects_VehicleWheel
+#define _Included_com_jme3_bullet_objects_VehicleWheel
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getWheelLocation
+ * Signature: (JILcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getWheelLocation
+ (JNIEnv *, jobject, jlong, jint, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getWheelRotation
+ * Signature: (JILcom/jme3/math/Matrix3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getWheelRotation
+ (JNIEnv *, jobject, jlong, jint, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: applyInfo
+ * Signature: (JIFFFFFFFFZF)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_applyInfo
+ (JNIEnv *, jobject, jlong, jint, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat, jboolean, jfloat);
+
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getCollisionLocation
+ * Signature: (JILcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getCollisionLocation
+ (JNIEnv *, jobject, jlong, jint, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getCollisionNormal
+ * Signature: (JILcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getCollisionNormal
+ (JNIEnv *, jobject, jlong, jint, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getSkidInfo
+ * Signature: (JI)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getSkidInfo
+ (JNIEnv *, jobject, jlong, jint);
+
+/*
+ * Class: com_jme3_bullet_objects_VehicleWheel
+ * Method: getDeltaRotation
+ * Signature: (JI)F
+ */
+JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_VehicleWheel_getDeltaRotation
+ (JNIEnv *, jobject, jlong, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp b/engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp
new file mode 100644
index 0000000..f61a376
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_objects_infos_RigidBodyMotionState.h"
+#include "jmeBulletUtil.h"
+#include "jmeMotionState.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: createMotionState
+ * Signature: ()J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_createMotionState
+ (JNIEnv *env, jobject object) {
+ jmeClasses::initJavaClasses(env);
+ jmeMotionState* motionState = new jmeMotionState();
+ return reinterpret_cast<jlong>(motionState);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: applyTransform
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Quaternion;)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_applyTransform
+ (JNIEnv *env, jobject object, jlong stateId, jobject location, jobject rotation) {
+ jmeMotionState* motionState = reinterpret_cast<jmeMotionState*>(stateId);
+ if (motionState == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return false;
+ }
+ return motionState->applyTransform(env, location, rotation);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_getWorldLocation
+ (JNIEnv *env, jobject object, jlong stateId, jobject value) {
+ jmeMotionState* motionState = reinterpret_cast<jmeMotionState*>(stateId);
+ if (motionState == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &motionState->worldTransform.getOrigin(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_getWorldRotation
+ (JNIEnv *env, jobject object, jlong stateId, jobject value) {
+ jmeMotionState* motionState = reinterpret_cast<jmeMotionState*>(stateId);
+ if (motionState == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convert(env, &motionState->worldTransform.getBasis(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldRotationQuat
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_getWorldRotationQuat
+ (JNIEnv *env, jobject object, jlong stateId, jobject value) {
+ jmeMotionState* motionState = reinterpret_cast<jmeMotionState*>(stateId);
+ if (motionState == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ jmeBulletUtil::convertQuat(env, &motionState->worldTransform.getBasis(), value);
+ }
+
+ /*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_finalizeNative
+ (JNIEnv *env, jobject object, jlong stateId) {
+ jmeMotionState* motionState = reinterpret_cast<jmeMotionState*>(stateId);
+ if (motionState == NULL) {
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "The native object does not exist.");
+ return;
+ }
+ delete(motionState);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.h b/engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.h
new file mode 100644
index 0000000..7939038
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_objects_infos_RigidBodyMotionState.h
@@ -0,0 +1,61 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_objects_infos_RigidBodyMotionState */
+
+#ifndef _Included_com_jme3_bullet_objects_infos_RigidBodyMotionState
+#define _Included_com_jme3_bullet_objects_infos_RigidBodyMotionState
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: createMotionState
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_createMotionState
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: applyTransform
+ * Signature: (JLcom/jme3/math/Vector3f;Lcom/jme3/math/Quaternion;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_applyTransform
+ (JNIEnv *, jobject, jlong, jobject, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldLocation
+ * Signature: (JLcom/jme3/math/Vector3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_getWorldLocation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldRotation
+ * Signature: (JLcom/jme3/math/Matrix3f;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_getWorldRotation
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: getWorldRotationQuat
+ * Signature: (JLcom/jme3/math/Quaternion;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_getWorldRotationQuat
+ (JNIEnv *, jobject, jlong, jobject);
+
+/*
+ * Class: com_jme3_bullet_objects_infos_RigidBodyMotionState
+ * Method: finalizeNative
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_infos_RigidBodyMotionState_finalizeNative
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.cpp b/engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.cpp
new file mode 100644
index 0000000..7d6c0b0
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen, CJ Hare
+ */
+#include "com_jme3_bullet_util_DebugShapeFactory.h"
+#include "jmeBulletUtil.h"
+#include "BulletCollision/CollisionShapes/btShapeHull.h"
+
+class DebugCallback : public btTriangleCallback, public btInternalTriangleIndexCallback {
+public:
+ JNIEnv* env;
+ jobject callback;
+
+ DebugCallback(JNIEnv* env, jobject object) {
+ this->env = env;
+ this->callback = object;
+ }
+
+ virtual void internalProcessTriangleIndex(btVector3* triangle, int partId, int triangleIndex) {
+ processTriangle(triangle, partId, triangleIndex);
+ }
+
+ virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) {
+ btVector3 vertexA, vertexB, vertexC;
+ vertexA = triangle[0];
+ vertexB = triangle[1];
+ vertexC = triangle[2];
+ env->CallVoidMethod(callback, jmeClasses::DebugMeshCallback_addVector, vertexA.getX(), vertexA.getY(), vertexA.getZ(), partId, triangleIndex);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+// triangle =
+ env->CallVoidMethod(callback, jmeClasses::DebugMeshCallback_addVector, vertexB.getX(), vertexB.getY(), vertexB.getZ(), partId, triangleIndex);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->CallVoidMethod(callback, jmeClasses::DebugMeshCallback_addVector, vertexC.getX(), vertexC.getY(), vertexC.getZ(), partId, triangleIndex);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ }
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Inaccessible static: _00024assertionsDisabled */
+
+ /*
+ * Class: com_jme3_bullet_util_DebugShapeFactory
+ * Method: getVertices
+ * Signature: (JLcom/jme3/bullet/util/DebugMeshCallback;)V
+ */
+ JNIEXPORT void JNICALL Java_com_jme3_bullet_util_DebugShapeFactory_getVertices
+ (JNIEnv *env, jclass clazz, jlong shapeId, jobject callback) {
+ btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
+ if (shape->isConcave()) {
+ btConcaveShape* concave = reinterpret_cast<btConcaveShape*>(shape);
+ DebugCallback* clb = new DebugCallback(env, callback);
+ btVector3 min = btVector3(-1e30, -1e30, -1e30);
+ btVector3 max = btVector3(1e30, 1e30, 1e30);
+ concave->processAllTriangles(clb, min, max);
+ delete(clb);
+ } else if (shape->isConvex()) {
+ btConvexShape* convexShape = reinterpret_cast<btConvexShape*>(shape);
+ // Check there is a hull shape to render
+ if (convexShape->getUserPointer() == NULL) {
+ // create a hull approximation
+ btShapeHull* hull = new btShapeHull(convexShape);
+ float margin = convexShape->getMargin();
+ hull->buildHull(margin);
+ convexShape->setUserPointer(hull);
+ }
+
+ btShapeHull* hull = (btShapeHull*) convexShape->getUserPointer();
+
+ int numberOfTriangles = hull->numTriangles();
+ int numberOfFloats = 3 * 3 * numberOfTriangles;
+ int byteBufferSize = numberOfFloats * 4;
+
+ // Loop variables
+ const unsigned int* hullIndices = hull->getIndexPointer();
+ const btVector3* hullVertices = hull->getVertexPointer();
+ btVector3 vertexA, vertexB, vertexC;
+ int index = 0;
+
+ for (int i = 0; i < numberOfTriangles; i++) {
+ // Grab the data for this triangle from the hull
+ vertexA = hullVertices[hullIndices[index++]];
+ vertexB = hullVertices[hullIndices[index++]];
+ vertexC = hullVertices[hullIndices[index++]];
+
+ // Put the verticies into the vertex buffer
+ env->CallVoidMethod(callback, jmeClasses::DebugMeshCallback_addVector, vertexA.getX(), vertexA.getY(), vertexA.getZ());
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->CallVoidMethod(callback, jmeClasses::DebugMeshCallback_addVector, vertexB.getX(), vertexB.getY(), vertexB.getZ());
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->CallVoidMethod(callback, jmeClasses::DebugMeshCallback_addVector, vertexC.getX(), vertexC.getY(), vertexC.getZ());
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ }
+ delete hull;
+ convexShape->setUserPointer(NULL);
+ }
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.h b/engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.h
new file mode 100644
index 0000000..757696f
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_util_DebugShapeFactory.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_util_DebugShapeFactory */
+
+#ifndef _Included_com_jme3_bullet_util_DebugShapeFactory
+#define _Included_com_jme3_bullet_util_DebugShapeFactory
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_util_DebugShapeFactory
+ * Method: getVertices
+ * Signature: (JLcom/jme3/bullet/util/DebugMeshCallback;)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_bullet_util_DebugShapeFactory_getVertices
+ (JNIEnv *, jclass, jlong, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.cpp b/engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.cpp
new file mode 100644
index 0000000..bf0b478
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Author: Normen Hansen
+ */
+#include "com_jme3_bullet_util_NativeMeshUtil.h"
+#include "jmeBulletUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: com_jme3_bullet_util_NativeMeshUtil
+ * Method: createTriangleIndexVertexArray
+ * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;IIII)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_util_NativeMeshUtil_createTriangleIndexVertexArray
+ (JNIEnv * env, jclass cls, jobject triangleIndexBase, jobject vertexIndexBase, jint numTriangles, jint numVertices, jint vertexStride, jint triangleIndexStride) {
+ jmeClasses::initJavaClasses(env);
+ int* triangles = (int*) env->GetDirectBufferAddress(triangleIndexBase);
+ float* vertices = (float*) env->GetDirectBufferAddress(vertexIndexBase);
+ btTriangleIndexVertexArray* array = new btTriangleIndexVertexArray(numTriangles, triangles, triangleIndexStride, numVertices, vertices, vertexStride);
+ return reinterpret_cast<jlong>(array);
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.h b/engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.h
new file mode 100644
index 0000000..0be9f4d
--- /dev/null
+++ b/engine/src/bullet-native/com_jme3_bullet_util_NativeMeshUtil.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_jme3_bullet_util_NativeMeshUtil */
+
+#ifndef _Included_com_jme3_bullet_util_NativeMeshUtil
+#define _Included_com_jme3_bullet_util_NativeMeshUtil
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_jme3_bullet_util_NativeMeshUtil
+ * Method: createTriangleIndexVertexArray
+ * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;IIII)J
+ */
+JNIEXPORT jlong JNICALL Java_com_jme3_bullet_util_NativeMeshUtil_createTriangleIndexVertexArray
+ (JNIEnv *, jclass, jobject, jobject, jint, jint, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/engine/src/bullet-native/jmeBulletUtil.cpp b/engine/src/bullet-native/jmeBulletUtil.cpp
new file mode 100644
index 0000000..4bc899d
--- /dev/null
+++ b/engine/src/bullet-native/jmeBulletUtil.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <math.h>
+#include "jmeBulletUtil.h"
+
+/**
+ * Author: Normen Hansen,Empire Phoenix, Lutherion
+ */
+void jmeBulletUtil::convert(JNIEnv* env, jobject in, btVector3* out) {
+ if (in == NULL || out == NULL) {
+ jmeClasses::throwNPE(env);
+ }
+ float x = env->GetFloatField(in, jmeClasses::Vector3f_x); //env->CallFloatMethod(in, jmeClasses::Vector3f_getX);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float y = env->GetFloatField(in, jmeClasses::Vector3f_y); //env->CallFloatMethod(in, jmeClasses::Vector3f_getY);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float z = env->GetFloatField(in, jmeClasses::Vector3f_z); //env->CallFloatMethod(in, jmeClasses::Vector3f_getZ);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ out->setX(x);
+ out->setY(y);
+ out->setZ(z);
+}
+
+void jmeBulletUtil::convert(JNIEnv* env, const btVector3* in, jobject out) {
+ if (in == NULL || out == NULL) {
+ jmeClasses::throwNPE(env);
+ }
+ float x = in->getX();
+ float y = in->getY();
+ float z = in->getZ();
+ env->SetFloatField(out, jmeClasses::Vector3f_x, x);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Vector3f_y, y);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Vector3f_z, z);
+ // env->CallObjectMethod(out, jmeClasses::Vector3f_set, x, y, z);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+}
+
+void jmeBulletUtil::convert(JNIEnv* env, jobject in, btMatrix3x3* out) {
+ if (in == NULL || out == NULL) {
+ jmeClasses::throwNPE(env);
+ }
+ float m00 = env->GetFloatField(in, jmeClasses::Matrix3f_m00);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m01 = env->GetFloatField(in, jmeClasses::Matrix3f_m01);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m02 = env->GetFloatField(in, jmeClasses::Matrix3f_m02);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m10 = env->GetFloatField(in, jmeClasses::Matrix3f_m10);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m11 = env->GetFloatField(in, jmeClasses::Matrix3f_m11);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m12 = env->GetFloatField(in, jmeClasses::Matrix3f_m12);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m20 = env->GetFloatField(in, jmeClasses::Matrix3f_m20);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m21 = env->GetFloatField(in, jmeClasses::Matrix3f_m21);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float m22 = env->GetFloatField(in, jmeClasses::Matrix3f_m22);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ out->setValue(m00, m01, m02, m10, m11, m12, m20, m21, m22);
+}
+
+void jmeBulletUtil::convert(JNIEnv* env, const btMatrix3x3* in, jobject out) {
+ if (in == NULL || out == NULL) {
+ jmeClasses::throwNPE(env);
+ }
+ float m00 = in->getRow(0).m_floats[0];
+ float m01 = in->getRow(0).m_floats[1];
+ float m02 = in->getRow(0).m_floats[2];
+ float m10 = in->getRow(1).m_floats[0];
+ float m11 = in->getRow(1).m_floats[1];
+ float m12 = in->getRow(1).m_floats[2];
+ float m20 = in->getRow(2).m_floats[0];
+ float m21 = in->getRow(2).m_floats[1];
+ float m22 = in->getRow(2).m_floats[2];
+ env->SetFloatField(out, jmeClasses::Matrix3f_m00, m00);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m01, m01);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m02, m02);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m10, m10);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m11, m11);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m12, m12);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m20, m20);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m21, m21);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Matrix3f_m22, m22);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+}
+
+void jmeBulletUtil::convertQuat(JNIEnv* env, jobject in, btMatrix3x3* out) {
+ if (in == NULL || out == NULL) {
+ jmeClasses::throwNPE(env);
+ }
+ float x = env->GetFloatField(in, jmeClasses::Quaternion_x);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float y = env->GetFloatField(in, jmeClasses::Quaternion_y);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float z = env->GetFloatField(in, jmeClasses::Quaternion_z);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ float w = env->GetFloatField(in, jmeClasses::Quaternion_w);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ float norm = w * w + x * x + y * y + z * z;
+ float s = (norm == 1.0) ? 2.0 : (norm > 0.1) ? 2.0 / norm : 0.0;
+
+ // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
+ // will be used 2-4 times each.
+ float xs = x * s;
+ float ys = y * s;
+ float zs = z * s;
+ float xx = x * xs;
+ float xy = x * ys;
+ float xz = x * zs;
+ float xw = w * xs;
+ float yy = y * ys;
+ float yz = y * zs;
+ float yw = w * ys;
+ float zz = z * zs;
+ float zw = w * zs;
+
+ // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
+ out->setValue(1.0 - (yy + zz), (xy - zw), (xz + yw),
+ (xy + zw), 1 - (xx + zz), (yz - xw),
+ (xz - yw), (yz + xw), 1.0 - (xx + yy));
+}
+
+void jmeBulletUtil::convertQuat(JNIEnv* env, const btMatrix3x3* in, jobject out) {
+ if (in == NULL || out == NULL) {
+ jmeClasses::throwNPE(env);
+ }
+ // the trace is the sum of the diagonal elements; see
+ // http://mathworld.wolfram.com/MatrixTrace.html
+ float t = in->getRow(0).m_floats[0] + in->getRow(1).m_floats[1] + in->getRow(2).m_floats[2];
+ float w, x, y, z;
+ // we protect the division by s by ensuring that s>=1
+ if (t >= 0) { // |w| >= .5
+ float s = sqrt(t + 1); // |s|>=1 ...
+ w = 0.5f * s;
+ s = 0.5f / s; // so this division isn't bad
+ x = (in->getRow(2).m_floats[1] - in->getRow(1).m_floats[2]) * s;
+ y = (in->getRow(0).m_floats[2] - in->getRow(2).m_floats[0]) * s;
+ z = (in->getRow(1).m_floats[0] - in->getRow(0).m_floats[1]) * s;
+ } else if ((in->getRow(0).m_floats[0] > in->getRow(1).m_floats[1]) && (in->getRow(0).m_floats[0] > in->getRow(2).m_floats[2])) {
+ float s = sqrt(1.0f + in->getRow(0).m_floats[0] - in->getRow(1).m_floats[1] - in->getRow(2).m_floats[2]); // |s|>=1
+ x = s * 0.5f; // |x| >= .5
+ s = 0.5f / s;
+ y = (in->getRow(1).m_floats[0] + in->getRow(0).m_floats[1]) * s;
+ z = (in->getRow(0).m_floats[2] + in->getRow(2).m_floats[0]) * s;
+ w = (in->getRow(2).m_floats[1] - in->getRow(1).m_floats[2]) * s;
+ } else if (in->getRow(1).m_floats[1] > in->getRow(2).m_floats[2]) {
+ float s = sqrt(1.0f + in->getRow(1).m_floats[1] - in->getRow(0).m_floats[0] - in->getRow(2).m_floats[2]); // |s|>=1
+ y = s * 0.5f; // |y| >= .5
+ s = 0.5f / s;
+ x = (in->getRow(1).m_floats[0] + in->getRow(0).m_floats[1]) * s;
+ z = (in->getRow(2).m_floats[1] + in->getRow(1).m_floats[2]) * s;
+ w = (in->getRow(0).m_floats[2] - in->getRow(2).m_floats[0]) * s;
+ } else {
+ float s = sqrt(1.0f + in->getRow(2).m_floats[2] - in->getRow(0).m_floats[0] - in->getRow(1).m_floats[1]); // |s|>=1
+ z = s * 0.5f; // |z| >= .5
+ s = 0.5f / s;
+ x = (in->getRow(0).m_floats[2] + in->getRow(2).m_floats[0]) * s;
+ y = (in->getRow(2).m_floats[1] + in->getRow(1).m_floats[2]) * s;
+ w = (in->getRow(1).m_floats[0] - in->getRow(0).m_floats[1]) * s;
+ }
+
+ env->SetFloatField(out, jmeClasses::Quaternion_x, x);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Quaternion_y, y);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Quaternion_z, z);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ env->SetFloatField(out, jmeClasses::Quaternion_w, w);
+ // env->CallObjectMethod(out, jmeClasses::Quaternion_set, x, y, z, w);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+}
+
+void jmeBulletUtil::addResult(JNIEnv* env, jobject resultlist, btVector3 hitnormal, btVector3 m_hitPointWorld, btScalar m_hitFraction, btCollisionObject* hitobject) {
+
+ jobject singleresult = env->AllocObject(jmeClasses::PhysicsRay_Class);
+ jobject hitnormalvec = env->AllocObject(jmeClasses::Vector3f);
+
+ convert(env, const_cast<btVector3*> (&hitnormal), hitnormalvec);
+ jmeUserPointer *up1 = (jmeUserPointer*) hitobject -> getUserPointer();
+
+ env->SetObjectField(singleresult, jmeClasses::PhysicsRay_normalInWorldSpace, hitnormalvec);
+ env->SetFloatField(singleresult, jmeClasses::PhysicsRay_hitfraction, m_hitFraction);
+
+ env->SetObjectField(singleresult, jmeClasses::PhysicsRay_collisionObject, up1->javaCollisionObject);
+ env->CallVoidMethod(resultlist, jmeClasses::PhysicsRay_addmethod, singleresult);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+}
diff --git a/engine/src/bullet-native/jmeBulletUtil.h b/engine/src/bullet-native/jmeBulletUtil.h
new file mode 100644
index 0000000..bd211fd
--- /dev/null
+++ b/engine/src/bullet-native/jmeBulletUtil.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "jmeClasses.h"
+#include "btBulletDynamicsCommon.h"
+#include "btBulletCollisionCommon.h"
+#include "LinearMath/btVector3.h"
+
+/**
+ * Author: Normen Hansen
+ */
+class jmeBulletUtil{
+public:
+ static void convert(JNIEnv* env, jobject in, btVector3* out);
+ static void convert(JNIEnv* env, const btVector3* in, jobject out);
+ static void convert(JNIEnv* env, jobject in, btMatrix3x3* out);
+ static void convert(JNIEnv* env, const btMatrix3x3* in, jobject out);
+ static void convertQuat(JNIEnv* env, jobject in, btMatrix3x3* out);
+ static void convertQuat(JNIEnv* env, const btMatrix3x3* in, jobject out);
+ static void addResult(JNIEnv* env, jobject resultlist, const btVector3 hitnormal,const btVector3 m_hitPointWorld,const btScalar m_hitFraction,btCollisionObject* hitobject);
+private:
+ jmeBulletUtil(){};
+ ~jmeBulletUtil(){};
+
+};
+
+class jmeUserPointer {
+public:
+ jobject javaCollisionObject;
+ jint group;
+ jint groups;
+ void *space;
+}; \ No newline at end of file
diff --git a/engine/src/bullet-native/jmeClasses.cpp b/engine/src/bullet-native/jmeClasses.cpp
new file mode 100644
index 0000000..ce79ffa
--- /dev/null
+++ b/engine/src/bullet-native/jmeClasses.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "jmeClasses.h"
+#include <stdio.h>
+
+/**
+ * Author: Normen Hansen,Empire Phoenix, Lutherion
+ */
+//public fields
+jclass jmeClasses::PhysicsSpace;
+jmethodID jmeClasses::PhysicsSpace_preTick;
+jmethodID jmeClasses::PhysicsSpace_postTick;
+jmethodID jmeClasses::PhysicsSpace_addCollisionEvent;
+
+jclass jmeClasses::PhysicsGhostObject;
+jmethodID jmeClasses::PhysicsGhostObject_addOverlappingObject;
+
+jclass jmeClasses::Vector3f;
+jmethodID jmeClasses::Vector3f_set;
+jmethodID jmeClasses::Vector3f_toArray;
+jmethodID jmeClasses::Vector3f_getX;
+jmethodID jmeClasses::Vector3f_getY;
+jmethodID jmeClasses::Vector3f_getZ;
+jfieldID jmeClasses::Vector3f_x;
+jfieldID jmeClasses::Vector3f_y;
+jfieldID jmeClasses::Vector3f_z;
+
+jclass jmeClasses::Quaternion;
+jmethodID jmeClasses::Quaternion_set;
+jmethodID jmeClasses::Quaternion_getX;
+jmethodID jmeClasses::Quaternion_getY;
+jmethodID jmeClasses::Quaternion_getZ;
+jmethodID jmeClasses::Quaternion_getW;
+jfieldID jmeClasses::Quaternion_x;
+jfieldID jmeClasses::Quaternion_y;
+jfieldID jmeClasses::Quaternion_z;
+jfieldID jmeClasses::Quaternion_w;
+
+jclass jmeClasses::Matrix3f;
+jmethodID jmeClasses::Matrix3f_set;
+jmethodID jmeClasses::Matrix3f_get;
+jfieldID jmeClasses::Matrix3f_m00;
+jfieldID jmeClasses::Matrix3f_m01;
+jfieldID jmeClasses::Matrix3f_m02;
+jfieldID jmeClasses::Matrix3f_m10;
+jfieldID jmeClasses::Matrix3f_m11;
+jfieldID jmeClasses::Matrix3f_m12;
+jfieldID jmeClasses::Matrix3f_m20;
+jfieldID jmeClasses::Matrix3f_m21;
+jfieldID jmeClasses::Matrix3f_m22;
+
+jclass jmeClasses::DebugMeshCallback;
+jmethodID jmeClasses::DebugMeshCallback_addVector;
+
+jclass jmeClasses::PhysicsRay_Class;
+jmethodID jmeClasses::PhysicsRay_newSingleResult;
+
+jfieldID jmeClasses::PhysicsRay_normalInWorldSpace;
+jfieldID jmeClasses::PhysicsRay_hitfraction;
+jfieldID jmeClasses::PhysicsRay_collisionObject;
+
+jclass jmeClasses::PhysicsRay_listresult;
+jmethodID jmeClasses::PhysicsRay_addmethod;
+
+//private fields
+//JNIEnv* jmeClasses::env;
+JavaVM* jmeClasses::vm;
+
+void jmeClasses::initJavaClasses(JNIEnv* env) {
+// if (env != NULL) {
+// fprintf(stdout, "Check Java VM state\n");
+// fflush(stdout);
+// int res = vm->AttachCurrentThread((void**) &jmeClasses::env, NULL);
+// if (res < 0) {
+// fprintf(stdout, "** ERROR: getting Java env!\n");
+// if (res == JNI_EVERSION) fprintf(stdout, "GetEnv Error because of different JNI Version!\n");
+// fflush(stdout);
+// }
+// return;
+// }
+ if(PhysicsSpace!=NULL) return;
+ fprintf(stdout, "Bullet-Native: Initializing java classes\n");
+ fflush(stdout);
+// jmeClasses::env = env;
+ env->GetJavaVM(&vm);
+
+ PhysicsSpace = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/bullet/PhysicsSpace"));
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsSpace_preTick = env->GetMethodID(PhysicsSpace, "preTick_native", "(F)V");
+ PhysicsSpace_postTick = env->GetMethodID(PhysicsSpace, "postTick_native", "(F)V");
+ PhysicsSpace_addCollisionEvent = env->GetMethodID(PhysicsSpace, "addCollisionEvent_native","(Lcom/jme3/bullet/collision/PhysicsCollisionObject;Lcom/jme3/bullet/collision/PhysicsCollisionObject;J)V");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsGhostObject = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/bullet/objects/PhysicsGhostObject"));
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ PhysicsGhostObject_addOverlappingObject = env->GetMethodID(PhysicsGhostObject, "addOverlappingObject_native","(Lcom/jme3/bullet/collision/PhysicsCollisionObject;)V");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ Vector3f = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/math/Vector3f"));
+ Vector3f_set = env->GetMethodID(Vector3f, "set", "(FFF)Lcom/jme3/math/Vector3f;");
+ Vector3f_toArray = env->GetMethodID(Vector3f, "toArray", "([F)[F");
+ Vector3f_getX = env->GetMethodID(Vector3f, "getX", "()F");
+ Vector3f_getY = env->GetMethodID(Vector3f, "getY", "()F");
+ Vector3f_getZ = env->GetMethodID(Vector3f, "getZ", "()F");
+ Vector3f_x = env->GetFieldID(Vector3f, "x", "F");
+ Vector3f_y = env->GetFieldID(Vector3f, "y", "F");
+ Vector3f_z = env->GetFieldID(Vector3f, "z", "F");
+
+ Quaternion = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/math/Quaternion"));
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ Quaternion_set = env->GetMethodID(Quaternion, "set", "(FFFF)Lcom/jme3/math/Quaternion;");
+ Quaternion_getW = env->GetMethodID(Quaternion, "getW", "()F");
+ Quaternion_getX = env->GetMethodID(Quaternion, "getX", "()F");
+ Quaternion_getY = env->GetMethodID(Quaternion, "getY", "()F");
+ Quaternion_getZ = env->GetMethodID(Quaternion, "getZ", "()F");
+ Quaternion_x = env->GetFieldID(Quaternion, "x", "F");
+ Quaternion_y = env->GetFieldID(Quaternion, "y", "F");
+ Quaternion_z = env->GetFieldID(Quaternion, "z", "F");
+ Quaternion_w = env->GetFieldID(Quaternion, "w", "F");
+
+ Matrix3f = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/math/Matrix3f"));
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ Matrix3f_set = env->GetMethodID(Matrix3f, "set", "(IIF)Lcom/jme3/math/Matrix3f;");
+ Matrix3f_get = env->GetMethodID(Matrix3f, "get", "(II)F");
+ Matrix3f_m00 = env->GetFieldID(Matrix3f, "m00", "F");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ Matrix3f_m01 = env->GetFieldID(Matrix3f, "m01", "F");
+ Matrix3f_m02 = env->GetFieldID(Matrix3f, "m02", "F");
+ Matrix3f_m10 = env->GetFieldID(Matrix3f, "m10", "F");
+ Matrix3f_m11 = env->GetFieldID(Matrix3f, "m11", "F");
+ Matrix3f_m12 = env->GetFieldID(Matrix3f, "m12", "F");
+ Matrix3f_m20 = env->GetFieldID(Matrix3f, "m20", "F");
+ Matrix3f_m21 = env->GetFieldID(Matrix3f, "m21", "F");
+ Matrix3f_m22 = env->GetFieldID(Matrix3f, "m22", "F");
+
+ DebugMeshCallback = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/bullet/util/DebugMeshCallback"));
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ DebugMeshCallback_addVector = env->GetMethodID(DebugMeshCallback, "addVector", "(FFFII)V");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsRay_Class = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/bullet/collision/PhysicsRayTestResult"));
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsRay_newSingleResult = env->GetMethodID(PhysicsRay_Class,"<init>","()V");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsRay_normalInWorldSpace = env->GetFieldID(PhysicsRay_Class,"hitNormalLocal","Lcom/jme3/math/Vector3f;");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+
+ PhysicsRay_hitfraction = env->GetFieldID(PhysicsRay_Class,"hitFraction","F");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+
+ PhysicsRay_collisionObject = env->GetFieldID(PhysicsRay_Class,"collisionObject","Lcom/jme3/bullet/collision/PhysicsCollisionObject;");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsRay_listresult = env->FindClass("java/util/List");
+ PhysicsRay_listresult = (jclass)env->NewGlobalRef(PhysicsRay_listresult);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+
+ PhysicsRay_addmethod = env->GetMethodID(PhysicsRay_listresult,"add","(Ljava/lang/Object;)Z");
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+}
+
+void jmeClasses::throwNPE(JNIEnv* env) {
+ if (env == NULL) return;
+ jclass newExc = env->FindClass("java/lang/NullPointerException");
+ env->ThrowNew(newExc, "");
+ return;
+}
diff --git a/engine/src/bullet-native/jmeClasses.h b/engine/src/bullet-native/jmeClasses.h
new file mode 100644
index 0000000..731b86f
--- /dev/null
+++ b/engine/src/bullet-native/jmeClasses.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <jni.h>
+
+/**
+ * Author: Normen Hansen
+ */
+
+class jmeClasses {
+public:
+ static void initJavaClasses(JNIEnv* env);
+// static JNIEnv* env;
+ static JavaVM* vm;
+ static jclass PhysicsSpace;
+ static jmethodID PhysicsSpace_preTick;
+ static jmethodID PhysicsSpace_postTick;
+ static jmethodID PhysicsSpace_addCollisionEvent;
+ static jclass PhysicsGhostObject;
+ static jmethodID PhysicsGhostObject_addOverlappingObject;
+
+ static jclass Vector3f;
+ static jmethodID Vector3f_set;
+ static jmethodID Vector3f_getX;
+ static jmethodID Vector3f_getY;
+ static jmethodID Vector3f_getZ;
+ static jmethodID Vector3f_toArray;
+ static jfieldID Vector3f_x;
+ static jfieldID Vector3f_y;
+ static jfieldID Vector3f_z;
+
+ static jclass Quaternion;
+ static jmethodID Quaternion_set;
+ static jmethodID Quaternion_getX;
+ static jmethodID Quaternion_getY;
+ static jmethodID Quaternion_getZ;
+ static jmethodID Quaternion_getW;
+ static jfieldID Quaternion_x;
+ static jfieldID Quaternion_y;
+ static jfieldID Quaternion_z;
+ static jfieldID Quaternion_w;
+
+ static jclass Matrix3f;
+ static jmethodID Matrix3f_get;
+ static jmethodID Matrix3f_set;
+ static jfieldID Matrix3f_m00;
+ static jfieldID Matrix3f_m01;
+ static jfieldID Matrix3f_m02;
+ static jfieldID Matrix3f_m10;
+ static jfieldID Matrix3f_m11;
+ static jfieldID Matrix3f_m12;
+ static jfieldID Matrix3f_m20;
+ static jfieldID Matrix3f_m21;
+ static jfieldID Matrix3f_m22;
+
+ static jclass PhysicsRay_Class;
+ static jmethodID PhysicsRay_newSingleResult;
+ static jfieldID PhysicsRay_normalInWorldSpace;
+ static jfieldID PhysicsRay_hitfraction;
+ static jfieldID PhysicsRay_collisionObject;
+ static jclass PhysicsRay_listresult;
+ static jmethodID PhysicsRay_addmethod;
+
+ static jclass DebugMeshCallback;
+ static jmethodID DebugMeshCallback_addVector;
+
+ static void throwNPE(JNIEnv* env);
+private:
+ jmeClasses(){};
+ ~jmeClasses(){};
+}; \ No newline at end of file
diff --git a/engine/src/bullet-native/jmeMotionState.cpp b/engine/src/bullet-native/jmeMotionState.cpp
new file mode 100644
index 0000000..0c61f9b
--- /dev/null
+++ b/engine/src/bullet-native/jmeMotionState.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "jmeMotionState.h"
+#include "jmeBulletUtil.h"
+
+/**
+ * Author: Normen Hansen
+ */
+
+jmeMotionState::jmeMotionState() {
+ trans = new btTransform();
+ trans -> setIdentity();
+ worldTransform = *trans;
+ dirty = true;
+}
+
+void jmeMotionState::getWorldTransform(btTransform& worldTrans) const {
+ worldTrans = worldTransform;
+}
+
+void jmeMotionState::setWorldTransform(const btTransform& worldTrans) {
+ worldTransform = worldTrans;
+ dirty = true;
+}
+
+void jmeMotionState::setKinematicTransform(const btTransform& worldTrans) {
+ worldTransform = worldTrans;
+ dirty = true;
+}
+
+void jmeMotionState::setKinematicLocation(JNIEnv* env, jobject location) {
+ jmeBulletUtil::convert(env, location, &worldTransform.getOrigin());
+ dirty = true;
+}
+
+void jmeMotionState::setKinematicRotation(JNIEnv* env, jobject rotation) {
+ jmeBulletUtil::convert(env, rotation, &worldTransform.getBasis());
+ dirty = true;
+}
+
+void jmeMotionState::setKinematicRotationQuat(JNIEnv* env, jobject rotation) {
+ jmeBulletUtil::convertQuat(env, rotation, &worldTransform.getBasis());
+ dirty = true;
+}
+
+bool jmeMotionState::applyTransform(JNIEnv* env, jobject location, jobject rotation) {
+ if (dirty) {
+ // fprintf(stdout, "Apply world translation\n");
+ // fflush(stdout);
+ jmeBulletUtil::convert(env, &worldTransform.getOrigin(), location);
+ jmeBulletUtil::convertQuat(env, &worldTransform.getBasis(), rotation);
+ dirty = false;
+ return true;
+ }
+ return false;
+}
+
+jmeMotionState::~jmeMotionState() {
+ free(trans);
+}
diff --git a/engine/src/bullet-native/jmeMotionState.h b/engine/src/bullet-native/jmeMotionState.h
new file mode 100644
index 0000000..b9e6ebb
--- /dev/null
+++ b/engine/src/bullet-native/jmeMotionState.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <jni.h>
+
+/**
+ * Author: Normen Hansen
+ */
+
+#include "btBulletDynamicsCommon.h"
+//#include "btBulletCollisionCommon.h"
+
+class jmeMotionState : public btMotionState {
+private:
+ bool dirty;
+ btTransform* trans;
+public:
+ jmeMotionState();
+ virtual ~jmeMotionState();
+
+ btTransform worldTransform;
+ virtual void getWorldTransform(btTransform& worldTrans) const;
+ virtual void setWorldTransform(const btTransform& worldTrans);
+ void setKinematicTransform(const btTransform& worldTrans);
+ void setKinematicLocation(JNIEnv*, jobject);
+ void setKinematicRotation(JNIEnv*, jobject);
+ void setKinematicRotationQuat(JNIEnv*, jobject);
+ bool applyTransform(JNIEnv* env, jobject location, jobject rotation);
+};
diff --git a/engine/src/bullet-native/jmePhysicsSpace.cpp b/engine/src/bullet-native/jmePhysicsSpace.cpp
new file mode 100644
index 0000000..f5e97ea
--- /dev/null
+++ b/engine/src/bullet-native/jmePhysicsSpace.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "jmePhysicsSpace.h"
+#include "jmeBulletUtil.h"
+#include <stdio.h>
+
+/**
+ * Author: Normen Hansen
+ */
+jmePhysicsSpace::jmePhysicsSpace(JNIEnv* env, jobject javaSpace) {
+ //TODO: global ref? maybe not -> cleaning, rather callback class?
+ this->javaPhysicsSpace = env->NewWeakGlobalRef(javaSpace);
+ this->env = env;
+ env->GetJavaVM(&vm);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+}
+
+void jmePhysicsSpace::attachThread() {
+#ifdef ANDROID
+ vm->AttachCurrentThread((JNIEnv**) &env, NULL);
+#elif defined (JNI_VERSION_1_2)
+ vm->AttachCurrentThread((void**) &env, NULL);
+#else
+ vm->AttachCurrentThread(&env, NULL);
+#endif
+}
+
+JNIEnv* jmePhysicsSpace::getEnv() {
+ attachThread();
+ return this->env;
+}
+
+void jmePhysicsSpace::stepSimulation(jfloat tpf, jint maxSteps, jfloat accuracy) {
+ dynamicsWorld->stepSimulation(tpf, maxSteps, accuracy);
+}
+
+btThreadSupportInterface* jmePhysicsSpace::createSolverThreadSupport(int maxNumThreads) {
+#ifdef _WIN32
+ Win32ThreadSupport::Win32ThreadConstructionInfo threadConstructionInfo("solverThreads", SolverThreadFunc, SolverlsMemoryFunc, maxNumThreads);
+ Win32ThreadSupport* threadSupport = new Win32ThreadSupport(threadConstructionInfo);
+ threadSupport->startSPU();
+#elif defined (USE_PTHREADS)
+ PosixThreadSupport::ThreadConstructionInfo constructionInfo("collision", SolverThreadFunc,
+ SolverlsMemoryFunc, maxNumThreads);
+ PosixThreadSupport* threadSupport = new PosixThreadSupport(constructionInfo);
+ threadSupport->startSPU();
+#else
+ SequentialThreadSupport::SequentialThreadConstructionInfo tci("solverThreads", SolverThreadFunc, SolverlsMemoryFunc);
+ SequentialThreadSupport* threadSupport = new SequentialThreadSupport(tci);
+ threadSupport->startSPU();
+#endif
+ return threadSupport;
+}
+
+btThreadSupportInterface* jmePhysicsSpace::createDispatchThreadSupport(int maxNumThreads) {
+#ifdef _WIN32
+ Win32ThreadSupport::Win32ThreadConstructionInfo threadConstructionInfo("solverThreads", processCollisionTask, createCollisionLocalStoreMemory, maxNumThreads);
+ Win32ThreadSupport* threadSupport = new Win32ThreadSupport(threadConstructionInfo);
+ threadSupport->startSPU();
+#elif defined (USE_PTHREADS)
+ PosixThreadSupport::ThreadConstructionInfo solverConstructionInfo("solver", processCollisionTask,
+ createCollisionLocalStoreMemory, maxNumThreads);
+ PosixThreadSupport* threadSupport = new PosixThreadSupport(solverConstructionInfo);
+ threadSupport->startSPU();
+#else
+ SequentialThreadSupport::SequentialThreadConstructionInfo tci("solverThreads", processCollisionTask, createCollisionLocalStoreMemory);
+ SequentialThreadSupport* threadSupport = new SequentialThreadSupport(tci);
+ threadSupport->startSPU();
+#endif
+ return threadSupport;
+}
+
+void jmePhysicsSpace::createPhysicsSpace(jfloat minX, jfloat minY, jfloat minZ, jfloat maxX, jfloat maxY, jfloat maxZ, jint broadphaseId, jboolean threading) {
+ // collision configuration contains default setup for memory, collision setup
+ btDefaultCollisionConstructionInfo cci;
+ // if(threading){
+ // cci.m_defaultMaxPersistentManifoldPoolSize = 32768;
+ // }
+ btCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration(cci);
+
+ btVector3 min = btVector3(minX, minY, minZ);
+ btVector3 max = btVector3(maxX, maxY, maxZ);
+
+ btBroadphaseInterface* broadphase;
+
+ switch (broadphaseId) {
+ case 0:
+ broadphase = new btSimpleBroadphase();
+ break;
+ case 1:
+ broadphase = new btAxisSweep3(min, max);
+ break;
+ case 2:
+ //TODO: 32bit!
+ broadphase = new btAxisSweep3(min, max);
+ break;
+ case 3:
+ broadphase = new btDbvtBroadphase();
+ break;
+ case 4:
+ // broadphase = new btGpu3DGridBroadphase(
+ // min, max,
+ // 20, 20, 20,
+ // 10000, 1000, 25);
+ break;
+ }
+
+ btCollisionDispatcher* dispatcher;
+ btConstraintSolver* solver;
+ // use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
+ if (threading) {
+ btThreadSupportInterface* dispatchThreads = createDispatchThreadSupport(4);
+ dispatcher = new SpuGatheringCollisionDispatcher(dispatchThreads, 4, collisionConfiguration);
+ dispatcher->setDispatcherFlags(btCollisionDispatcher::CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION);
+ } else {
+ dispatcher = new btCollisionDispatcher(collisionConfiguration);
+ }
+
+ // the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
+ if (threading) {
+ btThreadSupportInterface* solverThreads = createSolverThreadSupport(4);
+ solver = new btParallelConstraintSolver(solverThreads);
+ } else {
+ solver = new btSequentialImpulseConstraintSolver;
+ }
+
+ //create dynamics world
+ btDiscreteDynamicsWorld* world = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
+ dynamicsWorld = world;
+ dynamicsWorld->setWorldUserInfo(this);
+
+ //parallel solver requires the contacts to be in a contiguous pool, so avoid dynamic allocation
+ if (threading) {
+ world->getSimulationIslandManager()->setSplitIslands(false);
+ world->getSolverInfo().m_numIterations = 4;
+ world->getSolverInfo().m_solverMode = SOLVER_SIMD + SOLVER_USE_WARMSTARTING; //+SOLVER_RANDMIZE_ORDER;
+ world->getDispatchInfo().m_enableSPU = true;
+ }
+
+ broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
+
+ dynamicsWorld->setGravity(btVector3(0, -9.81f, 0));
+
+ struct jmeFilterCallback : public btOverlapFilterCallback {
+ // return true when pairs need collision
+
+ virtual bool needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy * proxy1) const {
+ // bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;
+ // collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);
+ bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;
+ collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);
+ if (collides) {
+ btCollisionObject* co0 = (btCollisionObject*) proxy0->m_clientObject;
+ btCollisionObject* co1 = (btCollisionObject*) proxy1->m_clientObject;
+ jmeUserPointer *up0 = (jmeUserPointer*) co0 -> getUserPointer();
+ jmeUserPointer *up1 = (jmeUserPointer*) co1 -> getUserPointer();
+ if (up0 != NULL && up1 != NULL) {
+ collides = (up0->group & up1->groups) != 0;
+ collides = collides && (up1->group & up0->groups);
+
+ //add some additional logic here that modified 'collides'
+ return collides;
+ }
+ return false;
+ }
+ return collides;
+ }
+ };
+ dynamicsWorld->getPairCache()->setOverlapFilterCallback(new jmeFilterCallback());
+ dynamicsWorld->setInternalTickCallback(&jmePhysicsSpace::preTickCallback, static_cast<void *> (this), true);
+ dynamicsWorld->setInternalTickCallback(&jmePhysicsSpace::postTickCallback, static_cast<void *> (this));
+ if (gContactProcessedCallback == NULL) {
+ gContactProcessedCallback = &jmePhysicsSpace::contactProcessedCallback;
+ }
+}
+
+void jmePhysicsSpace::preTickCallback(btDynamicsWorld *world, btScalar timeStep) {
+ jmePhysicsSpace* dynamicsWorld = (jmePhysicsSpace*) world->getWorldUserInfo();
+ JNIEnv* env = dynamicsWorld->getEnv();
+ jobject javaPhysicsSpace = env->NewLocalRef(dynamicsWorld->getJavaPhysicsSpace());
+ if (javaPhysicsSpace != NULL) {
+ env->CallVoidMethod(javaPhysicsSpace, jmeClasses::PhysicsSpace_preTick, timeStep);
+ env->DeleteLocalRef(javaPhysicsSpace);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ }
+}
+
+void jmePhysicsSpace::postTickCallback(btDynamicsWorld *world, btScalar timeStep) {
+ jmePhysicsSpace* dynamicsWorld = (jmePhysicsSpace*) world->getWorldUserInfo();
+ JNIEnv* env = dynamicsWorld->getEnv();
+ jobject javaPhysicsSpace = env->NewLocalRef(dynamicsWorld->getJavaPhysicsSpace());
+ if (javaPhysicsSpace != NULL) {
+ env->CallVoidMethod(javaPhysicsSpace, jmeClasses::PhysicsSpace_postTick, timeStep);
+ env->DeleteLocalRef(javaPhysicsSpace);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return;
+ }
+ }
+}
+
+bool jmePhysicsSpace::contactProcessedCallback(btManifoldPoint &cp, void *body0, void *body1) {
+ // printf("contactProcessedCallback %d %dn", body0, body1);
+ btCollisionObject* co0 = (btCollisionObject*) body0;
+ jmeUserPointer *up0 = (jmeUserPointer*) co0 -> getUserPointer();
+ btCollisionObject* co1 = (btCollisionObject*) body1;
+ jmeUserPointer *up1 = (jmeUserPointer*) co1 -> getUserPointer();
+ if (up0 != NULL) {
+ jmePhysicsSpace *dynamicsWorld = (jmePhysicsSpace *)up0->space;
+ if (dynamicsWorld != NULL) {
+ JNIEnv* env = dynamicsWorld->getEnv();
+ jobject javaPhysicsSpace = env->NewLocalRef(dynamicsWorld->getJavaPhysicsSpace());
+ if (javaPhysicsSpace != NULL) {
+ jobject javaCollisionObject0 = env->NewLocalRef(up0->javaCollisionObject);
+ jobject javaCollisionObject1 = env->NewLocalRef(up1->javaCollisionObject);
+ env->CallVoidMethod(javaPhysicsSpace, jmeClasses::PhysicsSpace_addCollisionEvent, javaCollisionObject0, javaCollisionObject1, (jlong) & cp);
+ env->DeleteLocalRef(javaPhysicsSpace);
+ env->DeleteLocalRef(javaCollisionObject0);
+ env->DeleteLocalRef(javaCollisionObject1);
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return true;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+btDynamicsWorld* jmePhysicsSpace::getDynamicsWorld() {
+ return dynamicsWorld;
+}
+
+jobject jmePhysicsSpace::getJavaPhysicsSpace() {
+ return javaPhysicsSpace;
+}
+
+jmePhysicsSpace::~jmePhysicsSpace() {
+ delete(dynamicsWorld);
+} \ No newline at end of file
diff --git a/engine/src/bullet-native/jmePhysicsSpace.h b/engine/src/bullet-native/jmePhysicsSpace.h
new file mode 100644
index 0000000..7ba4c06
--- /dev/null
+++ b/engine/src/bullet-native/jmePhysicsSpace.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <jni.h>
+#include "btBulletDynamicsCommon.h"
+#include "btBulletCollisionCommon.h"
+#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
+#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include "BulletDynamics/Character/btKinematicCharacterController.h"
+#ifdef _WIN32
+#include "BulletMultiThreaded/Win32ThreadSupport.h"
+#else
+#include "BulletMultiThreaded/PosixThreadSupport.h"
+#endif
+#include "BulletMultiThreaded/btParallelConstraintSolver.h"
+#include "BulletMultiThreaded/SpuGatheringCollisionDispatcher.h"
+#include "BulletMultiThreaded/SpuCollisionTaskProcess.h"
+#include "BulletMultiThreaded/SequentialThreadSupport.h"
+#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h"
+#include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h"
+#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
+
+/**
+ * Author: Normen Hansen
+ */
+class jmePhysicsSpace {
+private:
+ JNIEnv* env;
+ JavaVM* vm;
+ btDynamicsWorld* dynamicsWorld;
+ jobject javaPhysicsSpace;
+ btThreadSupportInterface* createSolverThreadSupport(int);
+ btThreadSupportInterface* createDispatchThreadSupport(int);
+ void attachThread();
+public:
+ jmePhysicsSpace(){};
+ ~jmePhysicsSpace();
+ jmePhysicsSpace(JNIEnv*, jobject);
+ void stepSimulation(jfloat, jint, jfloat);
+ void createPhysicsSpace(jfloat, jfloat, jfloat, jfloat, jfloat, jfloat, jint, jboolean);
+ btDynamicsWorld* getDynamicsWorld();
+ jobject getJavaPhysicsSpace();
+ JNIEnv* getEnv();
+ static void preTickCallback(btDynamicsWorld*, btScalar);
+ static void postTickCallback(btDynamicsWorld*, btScalar);
+ static bool contactProcessedCallback(btManifoldPoint &, void *, void *);
+}; \ No newline at end of file
diff --git a/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java b/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java
new file mode 100644
index 0000000..14cc635
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java
@@ -0,0 +1,921 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet;
+
+import com.jme3.app.AppTask;
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.collision.*;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.PhysicsControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.joints.PhysicsJoint;
+import com.jme3.bullet.objects.PhysicsCharacter;
+import com.jme3.bullet.objects.PhysicsGhostObject;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>PhysicsSpace - The central jbullet-jme physics space</p>
+ * @author normenhansen
+ */
+public class PhysicsSpace {
+
+ public static final int AXIS_X = 0;
+ public static final int AXIS_Y = 1;
+ public static final int AXIS_Z = 2;
+ private long physicsSpaceId = 0;
+ private static ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>> pQueueTL =
+ new ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>>() {
+
+ @Override
+ protected ConcurrentLinkedQueue<AppTask<?>> initialValue() {
+ return new ConcurrentLinkedQueue<AppTask<?>>();
+ }
+ };
+ private ConcurrentLinkedQueue<AppTask<?>> pQueue = new ConcurrentLinkedQueue<AppTask<?>>();
+ private static ThreadLocal<PhysicsSpace> physicsSpaceTL = new ThreadLocal<PhysicsSpace>();
+ private BroadphaseType broadphaseType = BroadphaseType.DBVT;
+// private DiscreteDynamicsWorld dynamicsWorld = null;
+// private BroadphaseInterface broadphase;
+// private CollisionDispatcher dispatcher;
+// private ConstraintSolver solver;
+// private DefaultCollisionConfiguration collisionConfiguration;
+// private Map<GhostObject, PhysicsGhostObject> physicsGhostNodes = new ConcurrentHashMap<GhostObject, PhysicsGhostObject>();
+ private Map<Long, PhysicsRigidBody> physicsNodes = new ConcurrentHashMap<Long, PhysicsRigidBody>();
+ private List<PhysicsJoint> physicsJoints = new LinkedList<PhysicsJoint>();
+ private List<PhysicsCollisionListener> collisionListeners = new LinkedList<PhysicsCollisionListener>();
+ private List<PhysicsCollisionEvent> collisionEvents = new LinkedList<PhysicsCollisionEvent>();
+ private Map<Integer, PhysicsCollisionGroupListener> collisionGroupListeners = new ConcurrentHashMap<Integer, PhysicsCollisionGroupListener>();
+ private ConcurrentLinkedQueue<PhysicsTickListener> tickListeners = new ConcurrentLinkedQueue<PhysicsTickListener>();
+ private PhysicsCollisionEventFactory eventFactory = new PhysicsCollisionEventFactory();
+ private Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f);
+ private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
+ private float accuracy = 1f / 60f;
+ private int maxSubSteps = 4;
+ private AssetManager debugManager;
+
+ static {
+// System.loadLibrary("bulletjme");
+// initNativePhysics();
+ }
+
+ /**
+ * Get the current PhysicsSpace <b>running on this thread</b><br/>
+ * For parallel physics, this can also be called from the OpenGL thread to receive the PhysicsSpace
+ * @return the PhysicsSpace running on this thread
+ */
+ public static PhysicsSpace getPhysicsSpace() {
+ return physicsSpaceTL.get();
+ }
+
+ /**
+ * Used internally
+ * @param space
+ */
+ public static void setLocalThreadPhysicsSpace(PhysicsSpace space) {
+ physicsSpaceTL.set(space);
+ }
+
+ public PhysicsSpace() {
+ this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT);
+ }
+
+ public PhysicsSpace(BroadphaseType broadphaseType) {
+ this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
+ }
+
+ public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) {
+ this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
+ }
+
+ public PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) {
+ this.worldMin.set(worldMin);
+ this.worldMax.set(worldMax);
+ this.broadphaseType = broadphaseType;
+ create();
+ }
+
+ /**
+ * Has to be called from the (designated) physics thread
+ */
+ public void create() {
+ //TODO: boroadphase!
+ physicsSpaceId = createPhysicsSpace(worldMin.x, worldMin.y, worldMin.z, worldMax.x, worldMax.y, worldMax.z, 3, false);
+ pQueueTL.set(pQueue);
+ physicsSpaceTL.set(this);
+
+// collisionConfiguration = new DefaultCollisionConfiguration();
+// dispatcher = new CollisionDispatcher(collisionConfiguration);
+// switch (broadphaseType) {
+// case SIMPLE:
+// broadphase = new SimpleBroadphase();
+// break;
+// case AXIS_SWEEP_3:
+// broadphase = new AxisSweep3(Converter.convert(worldMin), Converter.convert(worldMax));
+// break;
+// case AXIS_SWEEP_3_32:
+// broadphase = new AxisSweep3_32(Converter.convert(worldMin), Converter.convert(worldMax));
+// break;
+// case DBVT:
+// broadphase = new DbvtBroadphase();
+// break;
+// }
+//
+// solver = new SequentialImpulseConstraintSolver();
+//
+// dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
+// dynamicsWorld.setGravity(new javax.vecmath.Vector3f(0, -9.81f, 0));
+//
+// broadphase.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback());
+// GImpactCollisionAlgorithm.registerAlgorithm(dispatcher);
+//
+// //register filter callback for tick / collision
+// setTickCallback();
+// setContactCallbacks();
+// //register filter callback for collision groups
+// setOverlapFilterCallback();
+ }
+
+ private native long createPhysicsSpace(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int broadphaseType, boolean threading);
+
+ private void preTick_native(float f) {
+ AppTask task = pQueue.poll();
+ task = pQueue.poll();
+ while (task != null) {
+ while (task.isCancelled()) {
+ task = pQueue.poll();
+ }
+ try {
+ task.invoke();
+ } catch (Exception ex) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ task = pQueue.poll();
+ }
+ for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
+ PhysicsTickListener physicsTickCallback = it.next();
+ physicsTickCallback.prePhysicsTick(this, f);
+ }
+ }
+
+ private void postTick_native(float f) {
+ for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
+ PhysicsTickListener physicsTickCallback = it.next();
+ physicsTickCallback.physicsTick(this, f);
+ }
+ }
+
+ private void addCollision_native() {
+ }
+
+ private boolean needCollision_native(PhysicsCollisionObject objectA, PhysicsCollisionObject objectB) {
+ return false;
+ }
+
+// private void setOverlapFilterCallback() {
+// OverlapFilterCallback callback = new OverlapFilterCallback() {
+//
+// public boolean needBroadphaseCollision(BroadphaseProxy bp, BroadphaseProxy bp1) {
+// boolean collides = (bp.collisionFilterGroup & bp1.collisionFilterMask) != 0;
+// if (collides) {
+// collides = (bp1.collisionFilterGroup & bp.collisionFilterMask) != 0;
+// }
+// if (collides) {
+// assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject);
+// com.bulletphysics.collision.dispatch.CollisionObject colOb = (com.bulletphysics.collision.dispatch.CollisionObject) bp.clientObject;
+// com.bulletphysics.collision.dispatch.CollisionObject colOb1 = (com.bulletphysics.collision.dispatch.CollisionObject) bp1.clientObject;
+// assert (colOb.getUserPointer() != null && colOb1.getUserPointer() != null);
+// PhysicsCollisionObject collisionObject = (PhysicsCollisionObject) colOb.getUserPointer();
+// PhysicsCollisionObject collisionObject1 = (PhysicsCollisionObject) colOb1.getUserPointer();
+// if ((collisionObject.getCollideWithGroups() & collisionObject1.getCollisionGroup()) > 0
+// || (collisionObject1.getCollideWithGroups() & collisionObject.getCollisionGroup()) > 0) {
+// PhysicsCollisionGroupListener listener = collisionGroupListeners.get(collisionObject.getCollisionGroup());
+// PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(collisionObject1.getCollisionGroup());
+// if (listener != null) {
+// return listener.collide(collisionObject, collisionObject1);
+// } else if (listener1 != null) {
+// return listener1.collide(collisionObject, collisionObject1);
+// }
+// return true;
+// } else {
+// return false;
+// }
+// }
+// return collides;
+// }
+// };
+// dynamicsWorld.getPairCache().setOverlapFilterCallback(callback);
+// }
+// private void setTickCallback() {
+// final PhysicsSpace space = this;
+// InternalTickCallback callback2 = new InternalTickCallback() {
+//
+// @Override
+// public void internalTick(DynamicsWorld dw, float f) {
+// //execute task list
+// AppTask task = pQueue.poll();
+// task = pQueue.poll();
+// while (task != null) {
+// while (task.isCancelled()) {
+// task = pQueue.poll();
+// }
+// try {
+// task.invoke();
+// } catch (Exception ex) {
+// Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex);
+// }
+// task = pQueue.poll();
+// }
+// for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
+// PhysicsTickListener physicsTickCallback = it.next();
+// physicsTickCallback.prePhysicsTick(space, f);
+// }
+// }
+// };
+// dynamicsWorld.setPreTickCallback(callback2);
+// InternalTickCallback callback = new InternalTickCallback() {
+//
+// @Override
+// public void internalTick(DynamicsWorld dw, float f) {
+// for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
+// PhysicsTickListener physicsTickCallback = it.next();
+// physicsTickCallback.physicsTick(space, f);
+// }
+// }
+// };
+// dynamicsWorld.setInternalTickCallback(callback, this);
+// }
+// private void setContactCallbacks() {
+// BulletGlobals.setContactAddedCallback(new ContactAddedCallback() {
+//
+// public boolean contactAdded(ManifoldPoint cp, com.bulletphysics.collision.dispatch.CollisionObject colObj0,
+// int partId0, int index0, com.bulletphysics.collision.dispatch.CollisionObject colObj1, int partId1,
+// int index1) {
+// System.out.println("contact added");
+// return true;
+// }
+// });
+//
+// BulletGlobals.setContactProcessedCallback(new ContactProcessedCallback() {
+//
+// public boolean contactProcessed(ManifoldPoint cp, Object body0, Object body1) {
+// if (body0 instanceof CollisionObject && body1 instanceof CollisionObject) {
+// PhysicsCollisionObject node = null, node1 = null;
+// CollisionObject rBody0 = (CollisionObject) body0;
+// CollisionObject rBody1 = (CollisionObject) body1;
+// node = (PhysicsCollisionObject) rBody0.getUserPointer();
+// node1 = (PhysicsCollisionObject) rBody1.getUserPointer();
+// collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, cp));
+// }
+// return true;
+// }
+// });
+//
+// BulletGlobals.setContactDestroyedCallback(new ContactDestroyedCallback() {
+//
+// public boolean contactDestroyed(Object userPersistentData) {
+// System.out.println("contact destroyed");
+// return true;
+// }
+// });
+// }
+ private void addCollisionEvent_native(PhysicsCollisionObject node, PhysicsCollisionObject node1, long manifoldPointObjectId) {
+// System.out.println("addCollisionEvent:"+node.getObjectId()+" "+ node1.getObjectId());
+ collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, manifoldPointObjectId));
+ }
+
+ /**
+ * updates the physics space
+ * @param time the current time value
+ */
+ public void update(float time) {
+ update(time, maxSubSteps);
+ }
+
+ /**
+ * updates the physics space, uses maxSteps<br>
+ * @param time the current time value
+ * @param maxSteps
+ */
+ public void update(float time, int maxSteps) {
+// if (getDynamicsWorld() == null) {
+// return;
+// }
+ //step simulation
+ stepSimulation(physicsSpaceId, time, maxSteps, accuracy);
+ }
+
+ private native void stepSimulation(long space, float time, int maxSteps, float accuracy);
+
+ public void distributeEvents() {
+ //add collision callbacks
+ synchronized (collisionEvents) {
+ for (Iterator<PhysicsCollisionEvent> it = collisionEvents.iterator(); it.hasNext();) {
+ PhysicsCollisionEvent physicsCollisionEvent = it.next();
+ for (PhysicsCollisionListener listener : collisionListeners) {
+ listener.collision(physicsCollisionEvent);
+ }
+ //recycle events
+ eventFactory.recycle(physicsCollisionEvent);
+ it.remove();
+ }
+ }
+ }
+
+ public static <V> Future<V> enqueueOnThisThread(Callable<V> callable) {
+ AppTask<V> task = new AppTask<V>(callable);
+ System.out.println("created apptask");
+ pQueueTL.get().add(task);
+ return task;
+ }
+
+ /**
+ * calls the callable on the next physics tick (ensuring e.g. force applying)
+ * @param <V>
+ * @param callable
+ * @return
+ */
+ public <V> Future<V> enqueue(Callable<V> callable) {
+ AppTask<V> task = new AppTask<V>(callable);
+ pQueue.add(task);
+ return task;
+ }
+
+ /**
+ * adds an object to the physics space
+ * @param obj the PhysicsControl or Spatial with PhysicsControl to add
+ */
+ public void add(Object obj) {
+ if (obj instanceof PhysicsControl) {
+ ((PhysicsControl) obj).setPhysicsSpace(this);
+ } else if (obj instanceof Spatial) {
+ Spatial node = (Spatial) obj;
+ PhysicsControl control = node.getControl(PhysicsControl.class);
+ control.setPhysicsSpace(this);
+ } else if (obj instanceof PhysicsCollisionObject) {
+ addCollisionObject((PhysicsCollisionObject) obj);
+ } else if (obj instanceof PhysicsJoint) {
+ addJoint((PhysicsJoint) obj);
+ } else {
+ throw (new UnsupportedOperationException("Cannot add this kind of object to the physics space."));
+ }
+ }
+
+ public void addCollisionObject(PhysicsCollisionObject obj) {
+ if (obj instanceof PhysicsGhostObject) {
+ addGhostObject((PhysicsGhostObject) obj);
+ } else if (obj instanceof PhysicsRigidBody) {
+ addRigidBody((PhysicsRigidBody) obj);
+ } else if (obj instanceof PhysicsVehicle) {
+ addRigidBody((PhysicsVehicle) obj);
+ } else if (obj instanceof PhysicsCharacter) {
+ addCharacter((PhysicsCharacter) obj);
+ }
+ }
+
+ /**
+ * removes an object from the physics space
+ * @param obj the PhysicsControl or Spatial with PhysicsControl to remove
+ */
+ public void remove(Object obj) {
+ if (obj instanceof PhysicsControl) {
+ ((PhysicsControl) obj).setPhysicsSpace(null);
+ } else if (obj instanceof Spatial) {
+ Spatial node = (Spatial) obj;
+ PhysicsControl control = node.getControl(PhysicsControl.class);
+ control.setPhysicsSpace(null);
+ } else if (obj instanceof PhysicsCollisionObject) {
+ removeCollisionObject((PhysicsCollisionObject) obj);
+ } else if (obj instanceof PhysicsJoint) {
+ removeJoint((PhysicsJoint) obj);
+ } else {
+ throw (new UnsupportedOperationException("Cannot remove this kind of object from the physics space."));
+ }
+ }
+
+ public void removeCollisionObject(PhysicsCollisionObject obj) {
+ if (obj instanceof PhysicsGhostObject) {
+ removeGhostObject((PhysicsGhostObject) obj);
+ } else if (obj instanceof PhysicsRigidBody) {
+ removeRigidBody((PhysicsRigidBody) obj);
+ } else if (obj instanceof PhysicsCharacter) {
+ removeCharacter((PhysicsCharacter) obj);
+ }
+ }
+
+ /**
+ * adds all physics controls and joints in the given spatial node to the physics space
+ * (e.g. after loading from disk) - recursive if node
+ * @param spatial the rootnode containing the physics objects
+ */
+ public void addAll(Spatial spatial) {
+ if (spatial.getControl(RigidBodyControl.class) != null) {
+ RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class);
+ if (!physicsNodes.containsValue(physicsNode)) {
+ physicsNode.setPhysicsSpace(this);
+ }
+ //add joints
+ List<PhysicsJoint> joints = physicsNode.getJoints();
+ for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) {
+ PhysicsJoint physicsJoint = it1.next();
+ //add connected physicsnodes if they are not already added
+ if (!physicsNodes.containsValue(physicsJoint.getBodyA())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ add(physicsJoint.getBodyA());
+ } else {
+ addRigidBody(physicsJoint.getBodyA());
+ }
+ }
+ if (!physicsNodes.containsValue(physicsJoint.getBodyB())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ add(physicsJoint.getBodyB());
+ } else {
+ addRigidBody(physicsJoint.getBodyB());
+ }
+ }
+ if (!physicsJoints.contains(physicsJoint)) {
+ addJoint(physicsJoint);
+ }
+ }
+ } else if (spatial.getControl(PhysicsControl.class) != null) {
+ spatial.getControl(PhysicsControl.class).setPhysicsSpace(this);
+ }
+ //recursion
+ if (spatial instanceof Node) {
+ List<Spatial> children = ((Node) spatial).getChildren();
+ for (Iterator<Spatial> it = children.iterator(); it.hasNext();) {
+ Spatial spat = it.next();
+ addAll(spat);
+ }
+ }
+ }
+
+ /**
+ * Removes all physics controls and joints in the given spatial from the physics space
+ * (e.g. before saving to disk) - recursive if node
+ * @param spatial the rootnode containing the physics objects
+ */
+ public void removeAll(Spatial spatial) {
+ if (spatial.getControl(RigidBodyControl.class) != null) {
+ RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class);
+ if (physicsNodes.containsValue(physicsNode)) {
+ physicsNode.setPhysicsSpace(null);
+ }
+ //remove joints
+ List<PhysicsJoint> joints = physicsNode.getJoints();
+ for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) {
+ PhysicsJoint physicsJoint = it1.next();
+ //add connected physicsnodes if they are not already added
+ if (physicsNodes.containsValue(physicsJoint.getBodyA())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ remove(physicsJoint.getBodyA());
+ } else {
+ removeRigidBody(physicsJoint.getBodyA());
+ }
+ }
+ if (physicsNodes.containsValue(physicsJoint.getBodyB())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ remove(physicsJoint.getBodyB());
+ } else {
+ removeRigidBody(physicsJoint.getBodyB());
+ }
+ }
+ if (physicsJoints.contains(physicsJoint)) {
+ removeJoint(physicsJoint);
+ }
+ }
+ } else if (spatial.getControl(PhysicsControl.class) != null) {
+ spatial.getControl(PhysicsControl.class).setPhysicsSpace(null);
+ }
+ //recursion
+ if (spatial instanceof Node) {
+ List<Spatial> children = ((Node) spatial).getChildren();
+ for (Iterator<Spatial> it = children.iterator(); it.hasNext();) {
+ Spatial spat = it.next();
+ removeAll(spat);
+ }
+ }
+ }
+
+ private native void addCollisionObject(long space, long id);
+
+ private native void removeCollisionObject(long space, long id);
+
+ private native void addRigidBody(long space, long id);
+
+ private native void removeRigidBody(long space, long id);
+
+ private native void addCharacterObject(long space, long id);
+
+ private native void removeCharacterObject(long space, long id);
+
+ private native void addAction(long space, long id);
+
+ private native void removeAction(long space, long id);
+
+ private native void addVehicle(long space, long id);
+
+ private native void removeVehicle(long space, long id);
+
+ private native void addConstraint(long space, long id);
+
+ private native void removeConstraint(long space, long id);
+
+ private void addGhostObject(PhysicsGhostObject node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding ghost object {0} to physics space.", Long.toHexString(node.getObjectId()));
+ addCollisionObject(physicsSpaceId, node.getObjectId());
+ }
+
+ private void removeGhostObject(PhysicsGhostObject node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing ghost object {0} from physics space.", Long.toHexString(node.getObjectId()));
+ removeCollisionObject(physicsSpaceId, node.getObjectId());
+ }
+
+ private void addCharacter(PhysicsCharacter node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding character {0} to physics space.", Long.toHexString(node.getObjectId()));
+ addCharacterObject(physicsSpaceId, node.getObjectId());
+ addAction(physicsSpaceId, node.getControllerId());
+// dynamicsWorld.addCollisionObject(node.getObjectId(), CollisionFilterGroups.CHARACTER_FILTER, (short) (CollisionFilterGroups.STATIC_FILTER | CollisionFilterGroups.DEFAULT_FILTER));
+// dynamicsWorld.addAction(node.getControllerId());
+ }
+
+ private void removeCharacter(PhysicsCharacter node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing character {0} from physics space.", Long.toHexString(node.getObjectId()));
+ removeAction(physicsSpaceId, node.getControllerId());
+ removeCharacterObject(physicsSpaceId, node.getObjectId());
+// dynamicsWorld.removeAction(node.getControllerId());
+// dynamicsWorld.removeCollisionObject(node.getObjectId());
+ }
+
+ private void addRigidBody(PhysicsRigidBody node) {
+ physicsNodes.put(node.getObjectId(), node);
+
+ //Workaround
+ //It seems that adding a Kinematic RigidBody to the dynamicWorld prevent it from being non kinematic again afterward.
+ //so we add it non kinematic, then set it kinematic again.
+ boolean kinematic = false;
+ if (node.isKinematic()) {
+ kinematic = true;
+ node.setKinematic(false);
+ }
+ addRigidBody(physicsSpaceId, node.getObjectId());
+ if (kinematic) {
+ node.setKinematic(true);
+ }
+
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding RigidBody {0} to physics space.", node.getObjectId());
+ if (node instanceof PhysicsVehicle) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding vehicle constraint {0} to physics space.", Long.toHexString(((PhysicsVehicle) node).getVehicleId()));
+ addVehicle(physicsSpaceId, ((PhysicsVehicle) node).getVehicleId());
+ }
+ }
+
+ private void removeRigidBody(PhysicsRigidBody node) {
+ if (node instanceof PhysicsVehicle) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing vehicle constraint {0} from physics space.", Long.toHexString(((PhysicsVehicle) node).getVehicleId()));
+ removeVehicle(physicsSpaceId, ((PhysicsVehicle) node).getVehicleId());
+ }
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing RigidBody {0} from physics space.", Long.toHexString(node.getObjectId()));
+ physicsNodes.remove(node.getObjectId());
+ removeRigidBody(physicsSpaceId, node.getObjectId());
+ }
+
+ private void addJoint(PhysicsJoint joint) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding Joint {0} to physics space.", Long.toHexString(joint.getObjectId()));
+ physicsJoints.add(joint);
+ addConstraint(physicsSpaceId, joint.getObjectId());
+// dynamicsWorld.addConstraint(joint.getObjectId(), !joint.isCollisionBetweenLinkedBodys());
+ }
+
+ private void removeJoint(PhysicsJoint joint) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing Joint {0} from physics space.", Long.toHexString(joint.getObjectId()));
+ physicsJoints.remove(joint);
+ removeConstraint(physicsSpaceId, joint.getObjectId());
+// dynamicsWorld.removeConstraint(joint.getObjectId());
+ }
+
+ /**
+ * Sets the gravity of the PhysicsSpace, set before adding physics objects!
+ * @param gravity
+ */
+ public void setGravity(Vector3f gravity) {
+// dynamicsWorld.setGravity(Converter.convert(gravity));
+ setGravity(physicsSpaceId, gravity);
+ }
+
+ private native void setGravity(long spaceId, Vector3f gravity);
+
+// /**
+// * applies gravity value to all objects
+// */
+// public void applyGravity() {
+//// dynamicsWorld.applyGravity();
+// }
+//
+// /**
+// * clears forces of all objects
+// */
+// public void clearForces() {
+//// dynamicsWorld.clearForces();
+// }
+//
+ /**
+ * Adds the specified listener to the physics tick listeners.
+ * The listeners are called on each physics step, which is not necessarily
+ * each frame but is determined by the accuracy of the physics space.
+ * @param listener
+ */
+ public void addTickListener(PhysicsTickListener listener) {
+ tickListeners.add(listener);
+ }
+
+ public void removeTickListener(PhysicsTickListener listener) {
+ tickListeners.remove(listener);
+ }
+
+ /**
+ * Adds a CollisionListener that will be informed about collision events
+ * @param listener the CollisionListener to add
+ */
+ public void addCollisionListener(PhysicsCollisionListener listener) {
+ collisionListeners.add(listener);
+ }
+
+ /**
+ * Removes a CollisionListener from the list
+ * @param listener the CollisionListener to remove
+ */
+ public void removeCollisionListener(PhysicsCollisionListener listener) {
+ collisionListeners.remove(listener);
+ }
+
+ /**
+ * Adds a listener for a specific collision group, such a listener can disable collisions when they happen.<br>
+ * There can be only one listener per collision group.
+ * @param listener
+ * @param collisionGroup
+ */
+ public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) {
+ collisionGroupListeners.put(collisionGroup, listener);
+ }
+
+ public void removeCollisionGroupListener(int collisionGroup) {
+ collisionGroupListeners.remove(collisionGroup);
+ }
+
+ /**
+ * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults
+ */
+ public List rayTest(Vector3f from, Vector3f to) {
+ List results = new LinkedList();
+ rayTest(from, to, results);
+ return (List<PhysicsRayTestResult>) results;
+ }
+
+ /**
+ * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults
+ */
+ public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results) {
+ results.clear();
+ rayTest_native(from, to, physicsSpaceId, results);
+ return results;
+ }
+
+ public native void rayTest_native(Vector3f from, Vector3f to, long physicsSpaceId, List<PhysicsRayTestResult> results);
+
+// private class InternalRayListener extends CollisionWorld.RayResultCallback {
+//
+// private List<PhysicsRayTestResult> results;
+//
+// public InternalRayListener(List<PhysicsRayTestResult> results) {
+// this.results = results;
+// }
+//
+// @Override
+// public float addSingleResult(LocalRayResult lrr, boolean bln) {
+// PhysicsCollisionObject obj = (PhysicsCollisionObject) lrr.collisionObject.getUserPointer();
+// results.add(new PhysicsRayTestResult(obj, Converter.convert(lrr.hitNormalLocal), lrr.hitFraction, bln));
+// return lrr.hitFraction;
+// }
+// }
+ /**
+ * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/>
+ * You have to use different Transforms for start and end (at least distance > 0.4f).
+ * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center.
+ */
+ public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) {
+ List<PhysicsSweepTestResult> results = new LinkedList<PhysicsSweepTestResult>();
+// if (!(shape.getCShape() instanceof ConvexShape)) {
+// Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!");
+// return results;
+// }
+// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
+ return results;
+
+ }
+
+ /**
+ * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/>
+ * You have to use different Transforms for start and end (at least distance > 0.4f).
+ * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center.
+ */
+ public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) {
+ results.clear();
+// if (!(shape.getCShape() instanceof ConvexShape)) {
+// Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!");
+// return results;
+// }
+// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
+ return results;
+ }
+
+// private class InternalSweepListener extends CollisionWorld.ConvexResultCallback {
+//
+// private List<PhysicsSweepTestResult> results;
+//
+// public InternalSweepListener(List<PhysicsSweepTestResult> results) {
+// this.results = results;
+// }
+//
+// @Override
+// public float addSingleResult(LocalConvexResult lcr, boolean bln) {
+// PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer();
+// results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln));
+// return lcr.hitFraction;
+// }
+// }
+ /**
+ * destroys the current PhysicsSpace so that a new one can be created
+ */
+ public void destroy() {
+ physicsNodes.clear();
+ physicsJoints.clear();
+
+// dynamicsWorld.destroy();
+// dynamicsWorld = null;
+ }
+
+ /**
+ // * used internally
+ // * @return the dynamicsWorld
+ // */
+ public long getSpaceId() {
+ return physicsSpaceId;
+ }
+
+ public BroadphaseType getBroadphaseType() {
+ return broadphaseType;
+ }
+
+ public void setBroadphaseType(BroadphaseType broadphaseType) {
+ this.broadphaseType = broadphaseType;
+ }
+
+ /**
+ * Sets the maximum amount of extra steps that will be used to step the physics
+ * when the fps is below the physics fps. Doing this maintains determinism in physics.
+ * For example a maximum number of 2 can compensate for framerates as low as 30fps
+ * when the physics has the default accuracy of 60 fps. Note that setting this
+ * value too high can make the physics drive down its own fps in case its overloaded.
+ * @param steps The maximum number of extra steps, default is 4.
+ */
+ public void setMaxSubSteps(int steps) {
+ maxSubSteps = steps;
+ }
+
+ /**
+ * get the current accuracy of the physics computation
+ * @return the current accuracy
+ */
+ public float getAccuracy() {
+ return accuracy;
+ }
+
+ /**
+ * sets the accuracy of the physics computation, default=1/60s<br>
+ * @param accuracy
+ */
+ public void setAccuracy(float accuracy) {
+ this.accuracy = accuracy;
+ }
+
+ public Vector3f getWorldMin() {
+ return worldMin;
+ }
+
+ /**
+ * only applies for AXIS_SWEEP broadphase
+ * @param worldMin
+ */
+ public void setWorldMin(Vector3f worldMin) {
+ this.worldMin.set(worldMin);
+ }
+
+ public Vector3f getWorldMax() {
+ return worldMax;
+ }
+
+ /**
+ * only applies for AXIS_SWEEP broadphase
+ * @param worldMax
+ */
+ public void setWorldMax(Vector3f worldMax) {
+ this.worldMax.set(worldMax);
+ }
+
+ /**
+ * Enable debug display for physics
+ * @param manager AssetManager to use to create debug materials
+ */
+ public void enableDebug(AssetManager manager) {
+ debugManager = manager;
+ }
+
+ /**
+ * Disable debug display
+ */
+ public void disableDebug() {
+ debugManager = null;
+ }
+
+ public AssetManager getDebugManager() {
+ return debugManager;
+ }
+
+ public static native void initNativePhysics();
+
+ /**
+ * interface with Broadphase types
+ */
+ public enum BroadphaseType {
+
+ /**
+ * basic Broadphase
+ */
+ SIMPLE,
+ /**
+ * better Broadphase, needs worldBounds , max Object number = 16384
+ */
+ AXIS_SWEEP_3,
+ /**
+ * better Broadphase, needs worldBounds , max Object number = 65536
+ */
+ AXIS_SWEEP_3_32,
+ /**
+ * Broadphase allowing quicker adding/removing of physics objects
+ */
+ DBVT;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing PhysicsSpace {0}", Long.toHexString(physicsSpaceId));
+ finalizeNative(physicsSpaceId);
+ }
+
+ private native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java b/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java
new file mode 100644
index 0000000..32c506c
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.util.EventObject;
+
+/**
+ * A CollisionEvent stores all information about a collision in the PhysicsWorld.
+ * Do not store this Object, as it will be reused after the collision() method has been called.
+ * Get/reference all data you need in the collide method.
+ * @author normenhansen
+ */
+public class PhysicsCollisionEvent extends EventObject {
+
+ public static final int TYPE_ADDED = 0;
+ public static final int TYPE_PROCESSED = 1;
+ public static final int TYPE_DESTROYED = 2;
+ private int type;
+ private PhysicsCollisionObject nodeA;
+ private PhysicsCollisionObject nodeB;
+ private long manifoldPointObjectId = 0;
+
+ public PhysicsCollisionEvent(int type, PhysicsCollisionObject nodeA, PhysicsCollisionObject nodeB, long manifoldPointObjectId) {
+ super(nodeA);
+ this.manifoldPointObjectId = manifoldPointObjectId;
+ }
+
+ /**
+ * used by event factory, called when event is destroyed
+ */
+ public void clean() {
+ source = null;
+ this.type = 0;
+ this.nodeA = null;
+ this.nodeB = null;
+ this.manifoldPointObjectId = 0;
+ }
+
+ /**
+ * used by event factory, called when event reused
+ */
+ public void refactor(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, long manifoldPointObjectId) {
+ this.source = source;
+ this.type = type;
+ this.nodeA = source;
+ this.nodeB = nodeB;
+ this.manifoldPointObjectId = manifoldPointObjectId;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial
+ */
+ public Spatial getNodeA() {
+ if (nodeA.getUserObject() instanceof Spatial) {
+ return (Spatial) nodeA.getUserObject();
+ }
+ return null;
+ }
+
+ /**
+ * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial
+ */
+ public Spatial getNodeB() {
+ if (nodeB.getUserObject() instanceof Spatial) {
+ return (Spatial) nodeB.getUserObject();
+ }
+ return null;
+ }
+
+ public PhysicsCollisionObject getObjectA() {
+ return nodeA;
+ }
+
+ public PhysicsCollisionObject getObjectB() {
+ return nodeB;
+ }
+
+ public float getAppliedImpulse() {
+ return getAppliedImpulse(manifoldPointObjectId);
+ }
+ private native float getAppliedImpulse(long manifoldPointObjectId);
+
+ public float getAppliedImpulseLateral1() {
+ return getAppliedImpulseLateral1(manifoldPointObjectId);
+ }
+ private native float getAppliedImpulseLateral1(long manifoldPointObjectId);
+
+ public float getAppliedImpulseLateral2() {
+ return getAppliedImpulseLateral2(manifoldPointObjectId);
+ }
+ private native float getAppliedImpulseLateral2(long manifoldPointObjectId);
+
+ public float getCombinedFriction() {
+ return getCombinedFriction(manifoldPointObjectId);
+ }
+ private native float getCombinedFriction(long manifoldPointObjectId);
+
+ public float getCombinedRestitution() {
+ return getCombinedRestitution(manifoldPointObjectId);
+ }
+ private native float getCombinedRestitution(long manifoldPointObjectId);
+
+ public float getDistance1() {
+ return getDistance1(manifoldPointObjectId);
+ }
+ private native float getDistance1(long manifoldPointObjectId);
+
+ public int getIndex0() {
+ return getIndex0(manifoldPointObjectId);
+ }
+ private native int getIndex0(long manifoldPointObjectId);
+
+ public int getIndex1() {
+ return getIndex1(manifoldPointObjectId);
+ }
+ private native int getIndex1(long manifoldPointObjectId);
+
+ public Vector3f getLateralFrictionDir1() {
+ return getLateralFrictionDir1(new Vector3f());
+ }
+
+ public Vector3f getLateralFrictionDir1(Vector3f lateralFrictionDir1) {
+ getLateralFrictionDir1(manifoldPointObjectId, lateralFrictionDir1);
+ return lateralFrictionDir1;
+ }
+ private native void getLateralFrictionDir1(long manifoldPointObjectId, Vector3f lateralFrictionDir1);
+
+ public Vector3f getLateralFrictionDir2() {
+ return getLateralFrictionDir2(new Vector3f());
+ }
+
+ public Vector3f getLateralFrictionDir2(Vector3f lateralFrictionDir2) {
+ getLateralFrictionDir2(manifoldPointObjectId, lateralFrictionDir2);
+ return lateralFrictionDir2;
+ }
+ private native void getLateralFrictionDir2(long manifoldPointObjectId, Vector3f lateralFrictionDir2);
+
+ public boolean isLateralFrictionInitialized() {
+ return isLateralFrictionInitialized(manifoldPointObjectId);
+ }
+ private native boolean isLateralFrictionInitialized(long manifoldPointObjectId);
+
+ public int getLifeTime() {
+ return getLifeTime(manifoldPointObjectId);
+ }
+ private native int getLifeTime(long manifoldPointObjectId);
+
+ public Vector3f getLocalPointA() {
+ return getLocalPointA(new Vector3f());
+ }
+
+ public Vector3f getLocalPointA(Vector3f localPointA) {
+ getLocalPointA(manifoldPointObjectId, localPointA);
+ return localPointA;
+ }
+ private native void getLocalPointA(long manifoldPointObjectId, Vector3f localPointA);
+
+ public Vector3f getLocalPointB() {
+ return getLocalPointB(new Vector3f());
+ }
+
+ public Vector3f getLocalPointB(Vector3f localPointB) {
+ getLocalPointB(manifoldPointObjectId, localPointB);
+ return localPointB;
+ }
+ private native void getLocalPointB(long manifoldPointObjectId, Vector3f localPointB);
+
+ public Vector3f getNormalWorldOnB() {
+ return getNormalWorldOnB(new Vector3f());
+ }
+
+ public Vector3f getNormalWorldOnB(Vector3f normalWorldOnB) {
+ getNormalWorldOnB(manifoldPointObjectId, normalWorldOnB);
+ return normalWorldOnB;
+ }
+ private native void getNormalWorldOnB(long manifoldPointObjectId, Vector3f normalWorldOnB);
+
+ public int getPartId0() {
+ return getPartId0(manifoldPointObjectId);
+ }
+ private native int getPartId0(long manifoldPointObjectId);
+
+ public int getPartId1() {
+ return getPartId1(manifoldPointObjectId);
+ }
+
+ private native int getPartId1(long manifoldPointObjectId);
+
+ public Vector3f getPositionWorldOnA() {
+ return getPositionWorldOnA(new Vector3f());
+ }
+
+ public Vector3f getPositionWorldOnA(Vector3f positionWorldOnA) {
+ getPositionWorldOnA(positionWorldOnA);
+ return positionWorldOnA;
+ }
+ private native void getPositionWorldOnA(long manifoldPointObjectId, Vector3f positionWorldOnA);
+
+ public Vector3f getPositionWorldOnB() {
+ return getPositionWorldOnB(new Vector3f());
+ }
+
+ public Vector3f getPositionWorldOnB(Vector3f positionWorldOnB) {
+ getPositionWorldOnB(manifoldPointObjectId, positionWorldOnB);
+ return positionWorldOnB;
+ }
+ private native void getPositionWorldOnB(long manifoldPointObjectId, Vector3f positionWorldOnB);
+
+// public Object getUserPersistentData() {
+// return userPersistentData;
+// }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java b/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java
new file mode 100644
index 0000000..4d88fc0
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class PhysicsCollisionEventFactory {
+
+ private ConcurrentLinkedQueue<PhysicsCollisionEvent> eventBuffer = new ConcurrentLinkedQueue<PhysicsCollisionEvent>();
+
+ public PhysicsCollisionEvent getEvent(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, long manifoldPointObjectId) {
+ PhysicsCollisionEvent event = eventBuffer.poll();
+ if (event == null) {
+ event = new PhysicsCollisionEvent(type, source, nodeB, manifoldPointObjectId);
+ }else{
+ event.refactor(type, source, nodeB, manifoldPointObjectId);
+ }
+ return event;
+ }
+
+ public void recycle(PhysicsCollisionEvent event) {
+ event.clean();
+ eventBuffer.add(event);
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionObject.java b/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionObject.java
new file mode 100644
index 0000000..5e1d65e
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/PhysicsCollisionObject.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.util.DebugShapeFactory;
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Base class for collision objects (PhysicsRigidBody, PhysicsGhostObject)
+ * @author normenhansen
+ */
+public abstract class PhysicsCollisionObject implements Savable {
+
+ protected long objectId = 0;
+ protected Spatial debugShape;
+ protected Arrow debugArrow;
+ protected Geometry debugArrowGeom;
+ protected Material debugMaterialBlue;
+ protected Material debugMaterialRed;
+ protected Material debugMaterialGreen;
+ protected Material debugMaterialYellow;
+ protected CollisionShape collisionShape;
+ public static final int COLLISION_GROUP_NONE = 0x00000000;
+ public static final int COLLISION_GROUP_01 = 0x00000001;
+ public static final int COLLISION_GROUP_02 = 0x00000002;
+ public static final int COLLISION_GROUP_03 = 0x00000004;
+ public static final int COLLISION_GROUP_04 = 0x00000008;
+ public static final int COLLISION_GROUP_05 = 0x00000010;
+ public static final int COLLISION_GROUP_06 = 0x00000020;
+ public static final int COLLISION_GROUP_07 = 0x00000040;
+ public static final int COLLISION_GROUP_08 = 0x00000080;
+ public static final int COLLISION_GROUP_09 = 0x00000100;
+ public static final int COLLISION_GROUP_10 = 0x00000200;
+ public static final int COLLISION_GROUP_11 = 0x00000400;
+ public static final int COLLISION_GROUP_12 = 0x00000800;
+ public static final int COLLISION_GROUP_13 = 0x00001000;
+ public static final int COLLISION_GROUP_14 = 0x00002000;
+ public static final int COLLISION_GROUP_15 = 0x00004000;
+ public static final int COLLISION_GROUP_16 = 0x00008000;
+ protected int collisionGroup = 0x00000001;
+ protected int collisionGroupsMask = 0x00000001;
+ private Object userObject;
+
+ /**
+ * Sets a CollisionShape to this physics object, note that the object should
+ * not be in the physics space when adding a new collision shape as it is rebuilt
+ * on the physics side.
+ * @param collisionShape the CollisionShape to set
+ */
+ public void setCollisionShape(CollisionShape collisionShape) {
+ this.collisionShape = collisionShape;
+ updateDebugShape();
+ }
+
+ /**
+ * @return the CollisionShape of this PhysicsNode, to be able to reuse it with
+ * other physics nodes (increases performance)
+ */
+ public CollisionShape getCollisionShape() {
+ return collisionShape;
+ }
+
+ /**
+ * Returns the collision group for this collision shape
+ * @return
+ */
+ public int getCollisionGroup() {
+ return collisionGroup;
+ }
+
+ /**
+ * Sets the collision group number for this physics object. <br>
+ * The groups are integer bit masks and some pre-made variables are available in CollisionObject.
+ * All physics objects are by default in COLLISION_GROUP_01.<br>
+ * Two object will collide when <b>one</b> of the partys has the
+ * collisionGroup of the other in its collideWithGroups set.
+ * @param collisionGroup the collisionGroup to set
+ */
+ public void setCollisionGroup(int collisionGroup) {
+ this.collisionGroup = collisionGroup;
+ if (objectId != 0) {
+ setCollisionGroup(objectId, collisionGroup);
+ }
+ }
+
+ /**
+ * Add a group that this object will collide with.<br>
+ * Two object will collide when <b>one</b> of the partys has the
+ * collisionGroup of the other in its collideWithGroups set.<br>
+ * @param collisionGroup
+ */
+ public void addCollideWithGroup(int collisionGroup) {
+ this.collisionGroupsMask = this.collisionGroupsMask | collisionGroup;
+ if (objectId != 0) {
+ setCollideWithGroups(objectId, this.collisionGroupsMask);
+ }
+ }
+
+ /**
+ * Remove a group from the list this object collides with.
+ * @param collisionGroup
+ */
+ public void removeCollideWithGroup(int collisionGroup) {
+ this.collisionGroupsMask = this.collisionGroupsMask & ~collisionGroup;
+ if (objectId != 0) {
+ setCollideWithGroups(this.collisionGroupsMask);
+ }
+ }
+
+ /**
+ * Directly set the bitmask for collision groups that this object collides with.
+ * @param collisionGroup
+ */
+ public void setCollideWithGroups(int collisionGroups) {
+ this.collisionGroupsMask = collisionGroups;
+ if (objectId != 0) {
+ setCollideWithGroups(objectId, this.collisionGroupsMask);
+ }
+ }
+
+ /**
+ * Gets the bitmask of collision groups that this object collides with.
+ * @return
+ */
+ public int getCollideWithGroups() {
+ return collisionGroupsMask;
+ }
+
+ protected void initUserPointer() {
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "initUserPointer() objectId = {0}", Long.toHexString(objectId));
+ initUserPointer(objectId, collisionGroup, collisionGroupsMask);
+ }
+ native void initUserPointer(long objectId, int group, int groups);
+ /**
+ * Creates a visual debug shape of the current collision shape of this physics object<br/>
+ * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b>
+ * @param manager AssetManager to load the default wireframe material for the debug shape
+ */
+ protected Spatial attachDebugShape(AssetManager manager) {
+ debugMaterialBlue = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialBlue.getAdditionalRenderState().setWireframe(true);
+ debugMaterialBlue.setColor("Color", ColorRGBA.Blue);
+ debugMaterialGreen = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialGreen.getAdditionalRenderState().setWireframe(true);
+ debugMaterialGreen.setColor("Color", ColorRGBA.Green);
+ debugMaterialRed = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialRed.getAdditionalRenderState().setWireframe(true);
+ debugMaterialRed.setColor("Color", ColorRGBA.Red);
+ debugMaterialYellow = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialYellow.getAdditionalRenderState().setWireframe(true);
+ debugMaterialYellow.setColor("Color", ColorRGBA.Yellow);
+ debugArrow = new Arrow(Vector3f.UNIT_XYZ);
+ debugArrowGeom = new Geometry("DebugArrow", debugArrow);
+ debugArrowGeom.setMaterial(debugMaterialGreen);
+ return attachDebugShape();
+ }
+
+ /**
+ * creates a debug shape for this CollisionObject
+ * @param manager
+ * @return
+ */
+ public Spatial createDebugShape(AssetManager manager){
+ return attachDebugShape(manager);
+ }
+
+ protected Spatial attachDebugShape(Material material) {
+ debugMaterialBlue = material;
+ debugMaterialGreen = material;
+ debugMaterialRed = material;
+ debugMaterialYellow = material;
+ debugArrow = new Arrow(Vector3f.UNIT_XYZ);
+ debugArrowGeom = new Geometry("DebugArrow", debugArrow);
+ debugArrowGeom.setMaterial(debugMaterialGreen);
+ return attachDebugShape();
+ }
+
+ public Spatial debugShape() {
+ return debugShape;
+ }
+
+ /**
+ * Creates a visual debug shape of the current collision shape of this physics object<br/>
+ * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b>
+ * @param material Material to use for the debug shape
+ */
+ protected Spatial attachDebugShape() {
+ if (debugShape != null) {
+ detachDebugShape();
+ }
+ Spatial spatial = getDebugShape();
+ this.debugShape = spatial;
+ return debugShape;
+ }
+
+ protected void updateDebugShape() {
+ if (debugShape != null) {
+ detachDebugShape();
+ attachDebugShape();
+ }
+ }
+
+ protected Spatial getDebugShape() {
+ Spatial spatial = DebugShapeFactory.getDebugShape(collisionShape);
+ if (spatial == null) {
+ return new Node("nullnode");
+ }
+ if (spatial instanceof Node) {
+ List<Spatial> children = ((Node) spatial).getChildren();
+ for (Iterator<Spatial> it1 = children.iterator(); it1.hasNext();) {
+ Spatial spatial1 = it1.next();
+ Geometry geom = ((Geometry) spatial1);
+ geom.setMaterial(debugMaterialBlue);
+ geom.setCullHint(Spatial.CullHint.Never);
+ }
+ } else {
+ Geometry geom = ((Geometry) spatial);
+ geom.setMaterial(debugMaterialBlue);
+ geom.setCullHint(Spatial.CullHint.Never);
+ }
+ spatial.setCullHint(Spatial.CullHint.Never);
+ return spatial;
+ }
+
+ /**
+ * Removes the debug shape
+ */
+ public void detachDebugShape() {
+ debugShape = null;
+ }
+
+ /**
+ * @return the userObject
+ */
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ /**
+ * @param userObject the userObject to set
+ */
+ public void setUserObject(Object userObject) {
+ this.userObject = userObject;
+ }
+
+ public long getObjectId(){
+ return objectId;
+ }
+
+ protected native void attachCollisionShape(long objectId, long collisionShapeId);
+ native void setCollisionGroup(long objectId, int collisionGroup);
+ native void setCollideWithGroups(long objectId, int collisionGroups);
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(collisionGroup, "collisionGroup", 0x00000001);
+ capsule.write(collisionGroupsMask, "collisionGroupsMask", 0x00000001);
+ capsule.write(debugShape, "debugShape", null);
+ capsule.write(collisionShape, "collisionShape", null);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ collisionGroup = capsule.readInt("collisionGroup", 0x00000001);
+ collisionGroupsMask = capsule.readInt("collisionGroupsMask", 0x00000001);
+ debugShape = (Spatial) capsule.readSavable("debugShape", null);
+ CollisionShape shape = (CollisionShape) capsule.readSavable("collisionShape", null);
+ collisionShape = shape;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing CollisionObject {0}", Long.toHexString(objectId));
+ finalizeNative(objectId);
+ }
+
+ protected native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/PhysicsRayTestResult.java b/engine/src/bullet/com/jme3/bullet/collision/PhysicsRayTestResult.java
new file mode 100644
index 0000000..d4b092d
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/PhysicsRayTestResult.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.math.Vector3f;
+
+/**
+ * Contains the results of a PhysicsSpace rayTest
+ * bulletAppState.getPhysicsSpace().rayTest(new Vector3f(0,1000,0),new Vector3f(0,-1000,0));
+ javap -s java.util.List
+ * @author Empire-Phoenix,normenhansen
+ */
+public class PhysicsRayTestResult {
+
+ private PhysicsCollisionObject collisionObject;
+ private Vector3f hitNormalLocal;
+ private float hitFraction;
+ private boolean normalInWorldSpace = true;
+
+ /**
+ * allocated by native code only
+ */
+ private PhysicsRayTestResult() {
+ }
+
+ /**
+ * @return the collisionObject
+ */
+ public PhysicsCollisionObject getCollisionObject() {
+ return collisionObject;
+ }
+
+ /**
+ * @return the hitNormalLocal
+ */
+ public Vector3f getHitNormalLocal() {
+ return hitNormalLocal;
+ }
+
+ /**
+ * @return the hitFraction
+ */
+ public float getHitFraction() {
+ return hitFraction;
+ }
+
+ /**
+ * @return the normalInWorldSpace
+ */
+ public boolean isNormalInWorldSpace() {
+ return normalInWorldSpace;
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java b/engine/src/bullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java
new file mode 100644
index 0000000..d513204
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.math.Vector3f;
+
+/**
+ * Contains the results of a PhysicsSpace rayTest
+ * @author normenhansen
+ */
+public class PhysicsSweepTestResult {
+
+ private PhysicsCollisionObject collisionObject;
+ private Vector3f hitNormalLocal;
+ private float hitFraction;
+ private boolean normalInWorldSpace;
+
+ public PhysicsSweepTestResult() {
+ }
+
+ public PhysicsSweepTestResult(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) {
+ this.collisionObject = collisionObject;
+ this.hitNormalLocal = hitNormalLocal;
+ this.hitFraction = hitFraction;
+ this.normalInWorldSpace = normalInWorldSpace;
+ }
+
+ /**
+ * @return the collisionObject
+ */
+ public PhysicsCollisionObject getCollisionObject() {
+ return collisionObject;
+ }
+
+ /**
+ * @return the hitNormalLocal
+ */
+ public Vector3f getHitNormalLocal() {
+ return hitNormalLocal;
+ }
+
+ /**
+ * @return the hitFraction
+ */
+ public float getHitFraction() {
+ return hitFraction;
+ }
+
+ /**
+ * @return the normalInWorldSpace
+ */
+ public boolean isNormalInWorldSpace() {
+ return normalInWorldSpace;
+ }
+
+ public void fill(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) {
+ this.collisionObject = collisionObject;
+ this.hitNormalLocal = hitNormalLocal;
+ this.hitFraction = hitFraction;
+ this.normalInWorldSpace = normalInWorldSpace;
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java
new file mode 100644
index 0000000..bfcf287
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic box collision shape
+ * @author normenhansen
+ */
+public class BoxCollisionShape extends CollisionShape {
+
+ private Vector3f halfExtents;
+
+ public BoxCollisionShape() {
+ }
+
+ /**
+ * creates a collision box from the given halfExtents
+ * @param halfExtents the halfExtents of the CollisionBox
+ */
+ public BoxCollisionShape(Vector3f halfExtents) {
+ this.halfExtents = halfExtents;
+ createShape();
+ }
+
+ public final Vector3f getHalfExtents() {
+ return halfExtents;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(halfExtents, "halfExtents", new Vector3f(1, 1, 1));
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ Vector3f halfExtents = (Vector3f) capsule.readSavable("halfExtents", new Vector3f(1, 1, 1));
+ this.halfExtents = halfExtents;
+ createShape();
+ }
+
+ protected void createShape() {
+ objectId = createShape(halfExtents);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+// cShape = new BoxShape(Converter.convert(halfExtents));
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(Vector3f halfExtents);
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java
new file mode 100644
index 0000000..2f5ccc5
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic capsule collision shape
+ * @author normenhansen
+ */
+public class CapsuleCollisionShape extends CollisionShape{
+ protected float radius,height;
+ protected int axis;
+
+ public CapsuleCollisionShape() {
+ }
+
+ /**
+ * creates a new CapsuleCollisionShape with the given radius and height
+ * @param radius the radius of the capsule
+ * @param height the height of the capsule
+ */
+ public CapsuleCollisionShape(float radius, float height) {
+ this.radius=radius;
+ this.height=height;
+ this.axis=1;
+ createShape();
+ }
+
+ /**
+ * creates a capsule shape around the given axis (0=X,1=Y,2=Z)
+ * @param radius
+ * @param height
+ * @param axis
+ */
+ public CapsuleCollisionShape(float radius, float height, int axis) {
+ this.radius=radius;
+ this.height=height;
+ this.axis=axis;
+ createShape();
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public int getAxis() {
+ return axis;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(radius, "radius", 0.5f);
+ capsule.write(height, "height", 1);
+ capsule.write(axis, "axis", 1);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ radius = capsule.readFloat("radius", 0.5f);
+ height = capsule.readFloat("height", 0.5f);
+ axis = capsule.readInt("axis", 1);
+ createShape();
+ }
+
+ protected void createShape(){
+ objectId = createShape(axis, radius, height);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+ setScale(scale);
+ setMargin(margin);
+// switch(axis){
+// case 0:
+// objectId=new CapsuleShapeX(radius,height);
+// break;
+// case 1:
+// objectId=new CapsuleShape(radius,height);
+// break;
+// case 2:
+// objectId=new CapsuleShapeZ(radius,height);
+// break;
+// }
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ }
+
+ private native long createShape(int axis, float radius, float height);
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/CollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/CollisionShape.java
new file mode 100644
index 0000000..23a90d9
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/CollisionShape.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.*;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This Object holds information about a jbullet CollisionShape to be able to reuse
+ * CollisionShapes (as suggested in bullet manuals)
+ * TODO: add static methods to create shapes from nodes (like jbullet-jme constructor)
+ * @author normenhansen
+ */
+public abstract class CollisionShape implements Savable {
+
+ protected long objectId = 0;
+ protected Vector3f scale = new Vector3f(1, 1, 1);
+ protected float margin = 0.0f;
+
+ public CollisionShape() {
+ }
+
+// /**
+// * used internally, not safe
+// */
+// public void calculateLocalInertia(long objectId, float mass) {
+// if (this.objectId == 0) {
+// return;
+// }
+//// if (this instanceof MeshCollisionShape) {
+//// vector.set(0, 0, 0);
+//// } else {
+// calculateLocalInertia(objectId, this.objectId, mass);
+//// objectId.calculateLocalInertia(mass, vector);
+//// }
+// }
+//
+// private native void calculateLocalInertia(long objectId, long shapeId, float mass);
+
+ /**
+ * used internally
+ */
+ public long getObjectId() {
+ return objectId;
+ }
+
+ /**
+ * used internally
+ */
+ public void setObjectId(long id) {
+ this.objectId = id;
+ }
+
+ public void setScale(Vector3f scale) {
+ this.scale.set(scale);
+ setLocalScaling(objectId, scale);
+ }
+
+ public Vector3f getScale() {
+ return scale;
+ }
+
+ public float getMargin() {
+ return getMargin(objectId);
+ }
+
+ private native float getMargin(long objectId);
+
+ public void setMargin(float margin) {
+ setMargin(objectId, margin);
+ this.margin = margin;
+ }
+
+ private native void setLocalScaling(long obectId, Vector3f scale);
+
+ private native void setMargin(long objectId, float margin);
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(scale, "scale", new Vector3f(1, 1, 1));
+ capsule.write(getMargin(), "margin", 0.0f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ this.scale = (Vector3f) capsule.readSavable("scale", new Vector3f(1, 1, 1));
+ this.margin = capsule.readFloat("margin", 0.0f);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing CollisionShape {0}", Long.toHexString(objectId));
+ finalizeNative(objectId);
+ }
+
+ private native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java
new file mode 100644
index 0000000..ee4d86d
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A CompoundCollisionShape allows combining multiple base shapes
+ * to generate a more sophisticated shape.
+ * @author normenhansen
+ */
+public class CompoundCollisionShape extends CollisionShape {
+
+ protected ArrayList<ChildCollisionShape> children = new ArrayList<ChildCollisionShape>();
+
+ public CompoundCollisionShape() {
+ objectId = createShape();//new CompoundShape();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+ }
+
+ /**
+ * adds a child shape at the given local translation
+ * @param shape the child shape to add
+ * @param location the local location of the child shape
+ */
+ public void addChildShape(CollisionShape shape, Vector3f location) {
+// Transform transA = new Transform(Converter.convert(new Matrix3f()));
+// Converter.convert(location, transA.origin);
+// children.add(new ChildCollisionShape(location.clone(), new Matrix3f(), shape));
+// ((CompoundShape) objectId).addChildShape(transA, shape.getObjectId());
+ addChildShape(shape, location, new Matrix3f());
+ }
+
+ /**
+ * adds a child shape at the given local translation
+ * @param shape the child shape to add
+ * @param location the local location of the child shape
+ */
+ public void addChildShape(CollisionShape shape, Vector3f location, Matrix3f rotation) {
+ if(shape instanceof CompoundCollisionShape){
+ throw new IllegalStateException("CompoundCollisionShapes cannot have CompoundCollisionShapes as children!");
+ }
+// Transform transA = new Transform(Converter.convert(rotation));
+// Converter.convert(location, transA.origin);
+// Converter.convert(rotation, transA.basis);
+ children.add(new ChildCollisionShape(location.clone(), rotation.clone(), shape));
+ addChildShape(objectId, shape.getObjectId(), location, rotation);
+// ((CompoundShape) objectId).addChildShape(transA, shape.getObjectId());
+ }
+
+ private void addChildShapeDirect(CollisionShape shape, Vector3f location, Matrix3f rotation) {
+ if(shape instanceof CompoundCollisionShape){
+ throw new IllegalStateException("CompoundCollisionShapes cannot have CompoundCollisionShapes as children!");
+ }
+// Transform transA = new Transform(Converter.convert(rotation));
+// Converter.convert(location, transA.origin);
+// Converter.convert(rotation, transA.basis);
+ addChildShape(objectId, shape.getObjectId(), location, rotation);
+// ((CompoundShape) objectId).addChildShape(transA, shape.getObjectId());
+ }
+
+ /**
+ * removes a child shape
+ * @param shape the child shape to remove
+ */
+ public void removeChildShape(CollisionShape shape) {
+ removeChildShape(objectId, shape.getObjectId());
+// ((CompoundShape) objectId).removeChildShape(shape.getObjectId());
+ for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
+ ChildCollisionShape childCollisionShape = it.next();
+ if (childCollisionShape.shape == shape) {
+ it.remove();
+ }
+ }
+ }
+
+ public List<ChildCollisionShape> getChildren() {
+ return children;
+ }
+
+ /**
+ * WARNING - CompoundCollisionShape scaling has no effect.
+ */
+ @Override
+ public void setScale(Vector3f scale) {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "CompoundCollisionShape cannot be scaled");
+ }
+
+ private native long createShape();
+
+ private native long addChildShape(long objectId, long childId, Vector3f location, Matrix3f rotation);
+
+ private native long removeChildShape(long objectId, long childId);
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.writeSavableArrayList(children, "children", new ArrayList<ChildCollisionShape>());
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ children = capsule.readSavableArrayList("children", new ArrayList<ChildCollisionShape>());
+ setScale(scale);
+ setMargin(margin);
+ loadChildren();
+ }
+
+ private void loadChildren() {
+ for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
+ ChildCollisionShape child = it.next();
+ addChildShapeDirect(child.shape, child.location, child.rotation);
+ }
+ }
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java
new file mode 100644
index 0000000..f01b585
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java
@@ -0,0 +1,81 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class ConeCollisionShape extends CollisionShape {
+
+ protected float radius;
+ protected float height;
+ protected int axis;
+
+ public ConeCollisionShape() {
+ }
+
+ public ConeCollisionShape(float radius, float height, int axis) {
+ this.radius = radius;
+ this.height = radius;
+ this.axis = axis;
+ createShape();
+ }
+
+ public ConeCollisionShape(float radius, float height) {
+ this.radius = radius;
+ this.height = radius;
+ this.axis = PhysicsSpace.AXIS_Y;
+ createShape();
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(radius, "radius", 0.5f);
+ capsule.write(height, "height", 0.5f);
+ capsule.write(axis, "axis", 0.5f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ radius = capsule.readFloat("radius", 0.5f);
+ radius = capsule.readFloat("height", 0.5f);
+ radius = capsule.readFloat("axis", 0.5f);
+ createShape();
+ }
+
+ protected void createShape() {
+ objectId = createShape(axis, radius, height);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+// if (axis == PhysicsSpace.AXIS_X) {
+// objectId = new ConeShapeX(radius, height);
+// } else if (axis == PhysicsSpace.AXIS_Y) {
+// objectId = new ConeShape(radius, height);
+// } else if (axis == PhysicsSpace.AXIS_Z) {
+// objectId = new ConeShapeZ(radius, height);
+// }
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(int axis, float radius, float height);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java
new file mode 100644
index 0000000..fa651df
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic cylinder collision shape
+ * @author normenhansen
+ */
+public class CylinderCollisionShape extends CollisionShape {
+
+ protected Vector3f halfExtents;
+ protected int axis;
+
+ public CylinderCollisionShape() {
+ }
+
+ /**
+ * creates a cylinder shape from the given halfextents
+ * @param halfExtents the halfextents to use
+ */
+ public CylinderCollisionShape(Vector3f halfExtents) {
+ this.halfExtents = halfExtents;
+ this.axis = 2;
+ createShape();
+ }
+
+ /**
+ * Creates a cylinder shape around the given axis from the given halfextents
+ * @param halfExtents the halfextents to use
+ * @param axis (0=X,1=Y,2=Z)
+ */
+ public CylinderCollisionShape(Vector3f halfExtents, int axis) {
+ this.halfExtents = halfExtents;
+ this.axis = axis;
+ createShape();
+ }
+
+ public final Vector3f getHalfExtents() {
+ return halfExtents;
+ }
+
+ public int getAxis() {
+ return axis;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(halfExtents, "halfExtents", new Vector3f(0.5f, 0.5f, 0.5f));
+ capsule.write(axis, "axis", 1);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ halfExtents = (Vector3f) capsule.readSavable("halfExtents", new Vector3f(0.5f, 0.5f, 0.5f));
+ axis = capsule.readInt("axis", 1);
+ createShape();
+ }
+
+ protected void createShape() {
+ objectId = createShape(axis, halfExtents);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+// switch (axis) {
+// case 0:
+// objectId = new CylinderShapeX(Converter.convert(halfExtents));
+// break;
+// case 1:
+// objectId = new CylinderShape(Converter.convert(halfExtents));
+// break;
+// case 2:
+// objectId = new CylinderShapeZ(Converter.convert(halfExtents));
+// break;
+// }
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(int axis, Vector3f halfExtents);
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java
new file mode 100644
index 0000000..49478b4
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.bullet.util.NativeMeshUtil;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic mesh collision shape
+ * @author normenhansen
+ */
+public class GImpactCollisionShape extends CollisionShape {
+
+// protected Vector3f worldScale;
+ protected int numVertices, numTriangles, vertexStride, triangleIndexStride;
+ protected ByteBuffer triangleIndexBase, vertexBase;
+ protected long meshId = 0;
+// protected IndexedMesh bulletMesh;
+
+ public GImpactCollisionShape() {
+ }
+
+ /**
+ * creates a collision shape from the given Mesh
+ * @param mesh the Mesh to use
+ */
+ public GImpactCollisionShape(Mesh mesh) {
+ createCollisionMesh(mesh);
+ }
+
+ private void createCollisionMesh(Mesh mesh) {
+ triangleIndexBase = BufferUtils.createByteBuffer(mesh.getTriangleCount() * 3 * 4);
+ vertexBase = BufferUtils.createByteBuffer(mesh.getVertexCount() * 3 * 4);
+// triangleIndexBase = ByteBuffer.allocate(mesh.getTriangleCount() * 3 * 4);
+// vertexBase = ByteBuffer.allocate(mesh.getVertexCount() * 3 * 4);
+ numVertices = mesh.getVertexCount();
+ vertexStride = 12; //3 verts * 4 bytes per.
+ numTriangles = mesh.getTriangleCount();
+ triangleIndexStride = 12; //3 index entries * 4 bytes each.
+
+ IndexBuffer indices = mesh.getIndexBuffer();
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ vertices.rewind();
+
+ int verticesLength = mesh.getVertexCount() * 3;
+ for (int i = 0; i < verticesLength; i++) {
+ float tempFloat = vertices.get();
+ vertexBase.putFloat(tempFloat);
+ }
+
+ int indicesLength = mesh.getTriangleCount() * 3;
+ for (int i = 0; i < indicesLength; i++) {
+ triangleIndexBase.putInt(indices.get(i));
+ }
+ vertices.rewind();
+ vertices.clear();
+
+ createShape();
+ }
+
+// /**
+// * creates a jme mesh from the collision shape, only needed for debugging
+// */
+// public Mesh createJmeMesh() {
+// return Converter.convert(bulletMesh);
+// }
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+// capsule.write(worldScale, "worldScale", new Vector3f(1, 1, 1));
+ capsule.write(numVertices, "numVertices", 0);
+ capsule.write(numTriangles, "numTriangles", 0);
+ capsule.write(vertexStride, "vertexStride", 0);
+ capsule.write(triangleIndexStride, "triangleIndexStride", 0);
+
+ capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]);
+ capsule.write(vertexBase.array(), "vertexBase", new byte[0]);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+// worldScale = (Vector3f) capsule.readSavable("worldScale", new Vector3f(1, 1, 1));
+ numVertices = capsule.readInt("numVertices", 0);
+ numTriangles = capsule.readInt("numTriangles", 0);
+ vertexStride = capsule.readInt("vertexStride", 0);
+ triangleIndexStride = capsule.readInt("triangleIndexStride", 0);
+
+ triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0]));
+ vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0]));
+ createShape();
+ }
+
+ protected void createShape() {
+// bulletMesh = new IndexedMesh();
+// bulletMesh.numVertices = numVertices;
+// bulletMesh.numTriangles = numTriangles;
+// bulletMesh.vertexStride = vertexStride;
+// bulletMesh.triangleIndexStride = triangleIndexStride;
+// bulletMesh.triangleIndexBase = triangleIndexBase;
+// bulletMesh.vertexBase = vertexBase;
+// bulletMesh.triangleIndexBase = triangleIndexBase;
+// TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride);
+// objectId = new GImpactMeshShape(tiv);
+// objectId.setLocalScaling(Converter.convert(worldScale));
+// ((GImpactMeshShape)objectId).updateBound();
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ meshId = NativeMeshUtil.createTriangleIndexVertexArray(triangleIndexBase, vertexBase, numTriangles, numVertices, vertexStride, triangleIndexStride);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Mesh {0}", Long.toHexString(meshId));
+ objectId = createShape(meshId);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(long meshId);
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Mesh {0}", Long.toHexString(meshId));
+ finalizeNative(meshId);
+ }
+
+ private native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java
new file mode 100644
index 0000000..5bef2e3
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java
@@ -0,0 +1,145 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster
+ * than using a regular mesh.
+ * There are a couple tricks though:
+ * -No rotation or translation is supported.
+ * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being
+ * equal on either side. If not, the whole collision box is shifted vertically and things don't collide
+ * as they should.
+ *
+ * @author Brent Owens
+ */
+public class HeightfieldCollisionShape extends CollisionShape {
+
+ protected int heightStickWidth;
+ protected int heightStickLength;
+ protected float[] heightfieldData;
+ protected float heightScale;
+ protected float minHeight;
+ protected float maxHeight;
+ protected int upAxis;
+ protected boolean flipQuadEdges;
+ protected ByteBuffer bbuf;
+// protected FloatBuffer fbuf;
+
+ public HeightfieldCollisionShape() {
+ }
+
+ public HeightfieldCollisionShape(float[] heightmap) {
+ createCollisionHeightfield(heightmap, Vector3f.UNIT_XYZ);
+ }
+
+ public HeightfieldCollisionShape(float[] heightmap, Vector3f scale) {
+ createCollisionHeightfield(heightmap, scale);
+ }
+
+ protected void createCollisionHeightfield(float[] heightmap, Vector3f worldScale) {
+ this.scale = worldScale;
+ this.heightScale = 1;//don't change away from 1, we use worldScale instead to scale
+
+ this.heightfieldData = heightmap;
+
+ float min = heightfieldData[0];
+ float max = heightfieldData[0];
+ // calculate min and max height
+ for (int i = 0; i < heightfieldData.length; i++) {
+ if (heightfieldData[i] < min) {
+ min = heightfieldData[i];
+ }
+ if (heightfieldData[i] > max) {
+ max = heightfieldData[i];
+ }
+ }
+ // we need to center the terrain collision box at 0,0,0 for BulletPhysics. And to do that we need to set the
+ // min and max height to be equal on either side of the y axis, otherwise it gets shifted and collision is incorrect.
+ if (max < 0) {
+ max = -min;
+ } else {
+ if (Math.abs(max) > Math.abs(min)) {
+ min = -max;
+ } else {
+ max = -min;
+ }
+ }
+ this.minHeight = min;
+ this.maxHeight = max;
+
+ this.upAxis = 1;
+ this.flipQuadEdges = false;
+
+ heightStickWidth = (int) FastMath.sqrt(heightfieldData.length);
+ heightStickLength = heightStickWidth;
+
+
+ createShape();
+ }
+
+ protected void createShape() {
+ bbuf = BufferUtils.createByteBuffer(heightfieldData.length * 4);
+// fbuf = bbuf.asFloatBuffer();//FloatBuffer.wrap(heightfieldData);
+// fbuf.rewind();
+// fbuf.put(heightfieldData);
+ for (int i = 0; i < heightfieldData.length; i++) {
+ float f = heightfieldData[i];
+ bbuf.putFloat(f);
+ }
+// fbuf.rewind();
+ objectId = createShape(heightStickWidth, heightStickLength, bbuf, heightScale, minHeight, maxHeight, upAxis, flipQuadEdges);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(int heightStickWidth, int heightStickLength, ByteBuffer heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, boolean flipQuadEdges);
+
+ public Mesh createJmeMesh() {
+ //TODO return Converter.convert(bulletMesh);
+ return null;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(heightStickWidth, "heightStickWidth", 0);
+ capsule.write(heightStickLength, "heightStickLength", 0);
+ capsule.write(heightScale, "heightScale", 0);
+ capsule.write(minHeight, "minHeight", 0);
+ capsule.write(maxHeight, "maxHeight", 0);
+ capsule.write(upAxis, "upAxis", 1);
+ capsule.write(heightfieldData, "heightfieldData", new float[0]);
+ capsule.write(flipQuadEdges, "flipQuadEdges", false);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ heightStickWidth = capsule.readInt("heightStickWidth", 0);
+ heightStickLength = capsule.readInt("heightStickLength", 0);
+ heightScale = capsule.readFloat("heightScale", 0);
+ minHeight = capsule.readFloat("minHeight", 0);
+ maxHeight = capsule.readFloat("maxHeight", 0);
+ upAxis = capsule.readInt("upAxis", 1);
+ heightfieldData = capsule.readFloatArray("heightfieldData", new float[0]);
+ flipQuadEdges = capsule.readBoolean("flipQuadEdges", false);
+ createShape();
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java
new file mode 100644
index 0000000..9be760f
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java
@@ -0,0 +1,98 @@
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class HullCollisionShape extends CollisionShape {
+
+ private float[] points;
+// protected FloatBuffer fbuf;
+
+ public HullCollisionShape() {
+ }
+
+ public HullCollisionShape(Mesh mesh) {
+ this.points = getPoints(mesh);
+ createShape();
+ }
+
+ public HullCollisionShape(float[] points) {
+ this.points = points;
+ createShape();
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(points, "points", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+
+ // for backwards compatability
+ Mesh mesh = (Mesh) capsule.readSavable("hullMesh", null);
+ if (mesh != null) {
+ this.points = getPoints(mesh);
+ } else {
+ this.points = capsule.readFloatArray("points", null);
+
+ }
+// fbuf = ByteBuffer.allocateDirect(points.length * 4).asFloatBuffer();
+// fbuf.put(points);
+// fbuf = FloatBuffer.wrap(points).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ createShape();
+ }
+
+ protected void createShape() {
+// ObjectArrayList<Vector3f> pointList = new ObjectArrayList<Vector3f>();
+// for (int i = 0; i < points.length; i += 3) {
+// pointList.add(new Vector3f(points[i], points[i + 1], points[i + 2]));
+// }
+// objectId = new ConvexHullShape(pointList);
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ ByteBuffer bbuf=BufferUtils.createByteBuffer(points.length * 4);
+// fbuf = bbuf.asFloatBuffer();
+// fbuf.rewind();
+// fbuf.put(points);
+ for (int i = 0; i < points.length; i++) {
+ float f = points[i];
+ bbuf.putFloat(f);
+ }
+ bbuf.rewind();
+ objectId = createShape(bbuf);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(ByteBuffer points);
+
+ protected float[] getPoints(Mesh mesh) {
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ vertices.rewind();
+ int components = mesh.getVertexCount() * 3;
+ float[] pointsArray = new float[components];
+ for (int i = 0; i < components; i += 3) {
+ pointsArray[i] = vertices.get();
+ pointsArray[i + 1] = vertices.get();
+ pointsArray[i + 2] = vertices.get();
+ }
+ return pointsArray;
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java
new file mode 100644
index 0000000..c13bbd1
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.bullet.util.NativeMeshUtil;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic mesh collision shape
+ * @author normenhansen
+ */
+public class MeshCollisionShape extends CollisionShape {
+
+ protected int numVertices, numTriangles, vertexStride, triangleIndexStride;
+ protected ByteBuffer triangleIndexBase, vertexBase;
+ protected long meshId = 0;
+
+ public MeshCollisionShape() {
+ }
+
+ /**
+ * creates a collision shape from the given TriMesh
+ * @param mesh the TriMesh to use
+ */
+ public MeshCollisionShape(Mesh mesh) {
+ createCollisionMesh(mesh);
+ }
+
+ private void createCollisionMesh(Mesh mesh) {
+ triangleIndexBase = BufferUtils.createByteBuffer(mesh.getTriangleCount() * 3 * 4);
+ vertexBase = BufferUtils.createByteBuffer(mesh.getVertexCount() * 3 * 4);
+ numVertices = mesh.getVertexCount();
+ vertexStride = 12; //3 verts * 4 bytes per.
+ numTriangles = mesh.getTriangleCount();
+ triangleIndexStride = 12; //3 index entries * 4 bytes each.
+
+ IndexBuffer indices = mesh.getIndexBuffer();
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ vertices.rewind();
+
+ int verticesLength = mesh.getVertexCount() * 3;
+ for (int i = 0; i < verticesLength; i++) {
+ float tempFloat = vertices.get();
+ vertexBase.putFloat(tempFloat);
+ }
+
+ int indicesLength = mesh.getTriangleCount() * 3;
+ for (int i = 0; i < indicesLength; i++) {
+ triangleIndexBase.putInt(indices.get(i));
+ }
+ vertices.rewind();
+ vertices.clear();
+
+ createShape();
+ }
+
+ /**
+ * creates a jme mesh from the collision shape, only needed for debugging
+ */
+// public Mesh createJmeMesh(){
+// return Converter.convert(bulletMesh);
+// }
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(numVertices, "numVertices", 0);
+ capsule.write(numTriangles, "numTriangles", 0);
+ capsule.write(vertexStride, "vertexStride", 0);
+ capsule.write(triangleIndexStride, "triangleIndexStride", 0);
+
+ capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]);
+ capsule.write(vertexBase.array(), "vertexBase", new byte[0]);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ numVertices = capsule.readInt("numVertices", 0);
+ numTriangles = capsule.readInt("numTriangles", 0);
+ vertexStride = capsule.readInt("vertexStride", 0);
+ triangleIndexStride = capsule.readInt("triangleIndexStride", 0);
+
+ triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0]));
+ vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0])).order(ByteOrder.nativeOrder());
+ createShape();
+ }
+
+ protected void createShape() {
+// bulletMesh = new IndexedMesh();
+// bulletMesh.numVertices = numVertices;
+// bulletMesh.numTriangles = numTriangles;
+// bulletMesh.vertexStride = vertexStride;
+// bulletMesh.triangleIndexStride = triangleIndexStride;
+// bulletMesh.triangleIndexBase = triangleIndexBase;
+// bulletMesh.vertexBase = vertexBase;
+// bulletMesh.triangleIndexBase = triangleIndexBase;
+// TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride);
+// objectId = new BvhTriangleMeshShape(tiv, true);
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ meshId = NativeMeshUtil.createTriangleIndexVertexArray(triangleIndexBase, vertexBase, numTriangles, numVertices, vertexStride, triangleIndexStride);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Mesh {0}", Long.toHexString(meshId));
+ objectId = createShape(meshId);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(long meshId);
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Mesh {0}", Long.toHexString(meshId));
+ finalizeNative(meshId);
+ }
+
+ private native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java
new file mode 100644
index 0000000..3e949bd
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java
@@ -0,0 +1,66 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class PlaneCollisionShape extends CollisionShape{
+ private Plane plane;
+
+ public PlaneCollisionShape() {
+ }
+
+ /**
+ * Creates a plane Collision shape
+ * @param plane the plane that defines the shape
+ */
+ public PlaneCollisionShape(Plane plane) {
+ this.plane = plane;
+ createShape();
+ }
+
+ public final Plane getPlane() {
+ return plane;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(plane, "collisionPlane", new Plane());
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ plane = (Plane) capsule.readSavable("collisionPlane", new Plane());
+ createShape();
+ }
+
+ protected void createShape() {
+ objectId = createShape(plane.getNormal(), plane.getConstant());
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+// objectId = new StaticPlaneShape(Converter.convert(plane.getNormal()),plane.getConstant());
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(Vector3f normal, float constant);
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java
new file mode 100644
index 0000000..cef33d7
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java
@@ -0,0 +1,100 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A simple point, line, triangle or quad collisionShape based on one to four points-
+ * @author normenhansen
+ */
+public class SimplexCollisionShape extends CollisionShape {
+
+ private Vector3f vector1, vector2, vector3, vector4;
+
+ public SimplexCollisionShape() {
+ }
+
+ public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3, Vector3f point4) {
+ vector1 = point1;
+ vector2 = point2;
+ vector3 = point3;
+ vector4 = point4;
+ createShape();
+ }
+
+ public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3) {
+ vector1 = point1;
+ vector2 = point2;
+ vector3 = point3;
+ createShape();
+ }
+
+ public SimplexCollisionShape(Vector3f point1, Vector3f point2) {
+ vector1 = point1;
+ vector2 = point2;
+ createShape();
+ }
+
+ public SimplexCollisionShape(Vector3f point1) {
+ vector1 = point1;
+ createShape();
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(vector1, "simplexPoint1", null);
+ capsule.write(vector2, "simplexPoint2", null);
+ capsule.write(vector3, "simplexPoint3", null);
+ capsule.write(vector4, "simplexPoint4", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ vector1 = (Vector3f) capsule.readSavable("simplexPoint1", null);
+ vector2 = (Vector3f) capsule.readSavable("simplexPoint2", null);
+ vector3 = (Vector3f) capsule.readSavable("simplexPoint3", null);
+ vector4 = (Vector3f) capsule.readSavable("simplexPoint4", null);
+ createShape();
+ }
+
+ protected void createShape() {
+ if (vector4 != null) {
+ objectId = createShape(vector1, vector2, vector3, vector4);
+// objectId = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2), Converter.convert(vector3), Converter.convert(vector4));
+ } else if (vector3 != null) {
+ objectId = createShape(vector1, vector2, vector3);
+// objectId = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2), Converter.convert(vector3));
+ } else if (vector2 != null) {
+ objectId = createShape(vector1, vector2);
+// objectId = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2));
+ } else {
+ objectId = createShape(vector1);
+// objectId = new BU_Simplex1to4(Converter.convert(vector1));
+ }
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(Vector3f vector1);
+
+ private native long createShape(Vector3f vector1, Vector3f vector2);
+
+ private native long createShape(Vector3f vector1, Vector3f vector2, Vector3f vector3);
+
+ private native long createShape(Vector3f vector1, Vector3f vector2, Vector3f vector3, Vector3f vector4);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java
new file mode 100644
index 0000000..2ccd816
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic sphere collision shape
+ * @author normenhansen
+ */
+public class SphereCollisionShape extends CollisionShape {
+
+ protected float radius;
+
+ public SphereCollisionShape() {
+ }
+
+ /**
+ * creates a SphereCollisionShape with the given radius
+ * @param radius
+ */
+ public SphereCollisionShape(float radius) {
+ this.radius = radius;
+ createShape();
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(radius, "radius", 0.5f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ radius = capsule.readFloat("radius", 0.5f);
+ createShape();
+ }
+
+ protected void createShape() {
+ objectId = createShape(radius);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId));
+// new SphereShape(radius);
+// objectId.setLocalScaling(Converter.convert(getScale()));
+// objectId.setMargin(margin);
+ setScale(scale);
+ setMargin(margin);
+ }
+
+ private native long createShape(float radius);
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/ConeJoint.java b/engine/src/bullet/com/jme3/bullet/joints/ConeJoint.java
new file mode 100644
index 0000000..fc803ff
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/ConeJoint.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * To create ragdolls, the conve twist constraint is very useful for limbs like the upper arm.
+ * It is a special point to point constraint that adds cone and twist axis limits.
+ * The x-axis serves as twist axis.
+ * @author normenhansen
+ */
+public class ConeJoint extends PhysicsJoint {
+
+ protected Matrix3f rotA, rotB;
+ protected float swingSpan1 = 1e30f;
+ protected float swingSpan2 = 1e30f;
+ protected float twistSpan = 1e30f;
+ protected boolean angularOnly = false;
+
+ public ConeJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA = new Matrix3f();
+ this.rotB = new Matrix3f();
+ createJoint();
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA = rotA;
+ this.rotB = rotB;
+ createJoint();
+ }
+
+ public void setLimit(float swingSpan1, float swingSpan2, float twistSpan) {
+ this.swingSpan1 = swingSpan1;
+ this.swingSpan2 = swingSpan2;
+ this.twistSpan = twistSpan;
+ setLimit(objectId, swingSpan1, swingSpan2, twistSpan);
+ }
+
+ private native void setLimit(long objectId, float swingSpan1, float swingSpan2, float twistSpan);
+
+ public void setAngularOnly(boolean value) {
+ angularOnly = value;
+ setAngularOnly(objectId, value);
+ }
+
+ private native void setAngularOnly(long objectId, boolean value);
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(rotA, "rotA", new Matrix3f());
+ capsule.write(rotB, "rotB", new Matrix3f());
+
+ capsule.write(angularOnly, "angularOnly", false);
+ capsule.write(swingSpan1, "swingSpan1", 1e30f);
+ capsule.write(swingSpan2, "swingSpan2", 1e30f);
+ capsule.write(twistSpan, "twistSpan", 1e30f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ this.rotA = (Matrix3f) capsule.readSavable("rotA", new Matrix3f());
+ this.rotB = (Matrix3f) capsule.readSavable("rotB", new Matrix3f());
+
+ this.angularOnly = capsule.readBoolean("angularOnly", false);
+ this.swingSpan1 = capsule.readFloat("swingSpan1", 1e30f);
+ this.swingSpan2 = capsule.readFloat("swingSpan2", 1e30f);
+ this.twistSpan = capsule.readFloat("twistSpan", 1e30f);
+ createJoint();
+ }
+
+ protected void createJoint() {
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ setLimit(objectId, swingSpan1, swingSpan2, twistSpan);
+ setAngularOnly(objectId, angularOnly);
+ }
+
+ private native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Matrix3f rotA, Vector3f pivotB, Matrix3f rotB);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/HingeJoint.java b/engine/src/bullet/com/jme3/bullet/joints/HingeJoint.java
new file mode 100644
index 0000000..93769d9
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/HingeJoint.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * Hinge constraint, or revolute joint restricts two additional angular degrees of freedom,
+ * so the body can only rotate around one axis, the hinge axis.
+ * This can be useful to represent doors or wheels rotating around one axis.
+ * The user can specify limits and motor for the hinge.
+ * @author normenhansen
+ */
+public class HingeJoint extends PhysicsJoint {
+
+ protected Vector3f axisA;
+ protected Vector3f axisB;
+ protected boolean angularOnly = false;
+ protected float biasFactor = 0.3f;
+ protected float relaxationFactor = 1.0f;
+ protected float limitSoftness = 0.9f;
+
+ public HingeJoint() {
+ }
+
+ /**
+ * Creates a new HingeJoint
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public HingeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Vector3f axisA, Vector3f axisB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.axisA = axisA;
+ this.axisB = axisB;
+ createJoint();
+ }
+
+ public void enableMotor(boolean enable, float targetVelocity, float maxMotorImpulse) {
+ enableMotor(objectId, enable, targetVelocity, maxMotorImpulse);
+ }
+
+ private native void enableMotor(long objectId, boolean enable, float targetVelocity, float maxMotorImpulse);
+
+ public boolean getEnableMotor() {
+ return getEnableAngularMotor(objectId);
+ }
+
+ private native boolean getEnableAngularMotor(long objectId);
+
+ public float getMotorTargetVelocity() {
+ return getMotorTargetVelocity(objectId);
+ }
+
+ private native float getMotorTargetVelocity(long objectId);
+
+ public float getMaxMotorImpulse() {
+ return getMaxMotorImpulse(objectId);
+ }
+
+ private native float getMaxMotorImpulse(long objectId);
+
+ public void setLimit(float low, float high) {
+ setLimit(objectId, low, high);
+ }
+
+ private native void setLimit(long objectId, float low, float high);
+
+ public void setLimit(float low, float high, float _softness, float _biasFactor, float _relaxationFactor) {
+ biasFactor = _biasFactor;
+ relaxationFactor = _relaxationFactor;
+ limitSoftness = _softness;
+ setLimit(objectId, low, high, _softness, _biasFactor, _relaxationFactor);
+ }
+
+ private native void setLimit(long objectId, float low, float high, float _softness, float _biasFactor, float _relaxationFactor);
+
+ public float getUpperLimit() {
+ return getUpperLimit(objectId);
+ }
+
+ private native float getUpperLimit(long objectId);
+
+ public float getLowerLimit() {
+ return getLowerLimit(objectId);
+ }
+
+ private native float getLowerLimit(long objectId);
+
+ public void setAngularOnly(boolean angularOnly) {
+ this.angularOnly = angularOnly;
+ setAngularOnly(objectId, angularOnly);
+ }
+
+ private native void setAngularOnly(long objectId, boolean angularOnly);
+
+ public float getHingeAngle() {
+ return getHingeAngle(objectId);
+ }
+
+ private native float getHingeAngle(long objectId);
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(axisA, "axisA", new Vector3f());
+ capsule.write(axisB, "axisB", new Vector3f());
+
+ capsule.write(angularOnly, "angularOnly", false);
+
+ capsule.write(getLowerLimit(), "lowerLimit", 1e30f);
+ capsule.write(getUpperLimit(), "upperLimit", -1e30f);
+
+ capsule.write(biasFactor, "biasFactor", 0.3f);
+ capsule.write(relaxationFactor, "relaxationFactor", 1f);
+ capsule.write(limitSoftness, "limitSoftness", 0.9f);
+
+ capsule.write(getEnableMotor(), "enableAngularMotor", false);
+ capsule.write(getMotorTargetVelocity(), "targetVelocity", 0.0f);
+ capsule.write(getMaxMotorImpulse(), "maxMotorImpulse", 0.0f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ this.axisA = (Vector3f) capsule.readSavable("axisA", new Vector3f());
+ this.axisB = (Vector3f) capsule.readSavable("axisB", new Vector3f());
+
+ this.angularOnly = capsule.readBoolean("angularOnly", false);
+ float lowerLimit = capsule.readFloat("lowerLimit", 1e30f);
+ float upperLimit = capsule.readFloat("upperLimit", -1e30f);
+
+ this.biasFactor = capsule.readFloat("biasFactor", 0.3f);
+ this.relaxationFactor = capsule.readFloat("relaxationFactor", 1f);
+ this.limitSoftness = capsule.readFloat("limitSoftness", 0.9f);
+
+ boolean enableAngularMotor = capsule.readBoolean("enableAngularMotor", false);
+ float targetVelocity = capsule.readFloat("targetVelocity", 0.0f);
+ float maxMotorImpulse = capsule.readFloat("maxMotorImpulse", 0.0f);
+
+ createJoint();
+ enableMotor(enableAngularMotor, targetVelocity, maxMotorImpulse);
+ setLimit(lowerLimit, upperLimit, limitSoftness, biasFactor, relaxationFactor);
+ }
+
+ protected void createJoint() {
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, axisA, pivotB, axisB);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ }
+
+ private native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Vector3f axisA, Vector3f pivotB, Vector3f axisB);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/PhysicsJoint.java b/engine/src/bullet/com/jme3/bullet/joints/PhysicsJoint.java
new file mode 100644
index 0000000..ecf517c
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/PhysicsJoint.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.*;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>PhysicsJoint - Basic Phyiscs Joint</p>
+ * @author normenhansen
+ */
+public abstract class PhysicsJoint implements Savable {
+
+ protected long objectId = 0;
+ protected PhysicsRigidBody nodeA;
+ protected PhysicsRigidBody nodeB;
+ protected Vector3f pivotA;
+ protected Vector3f pivotB;
+ protected boolean collisionBetweenLinkedBodys = true;
+
+ public PhysicsJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public PhysicsJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) {
+ this.nodeA = nodeA;
+ this.nodeB = nodeB;
+ this.pivotA = pivotA;
+ this.pivotB = pivotB;
+ nodeA.addJoint(this);
+ nodeB.addJoint(this);
+ }
+
+ public float getAppliedImpulse() {
+ return getAppliedImpulse(objectId);
+ }
+
+ private native float getAppliedImpulse(long objectId);
+
+ /**
+ * @return the constraint
+ */
+ public long getObjectId() {
+ return objectId;
+ }
+
+ /**
+ * @return the collisionBetweenLinkedBodys
+ */
+ public boolean isCollisionBetweenLinkedBodys() {
+ return collisionBetweenLinkedBodys;
+ }
+
+ /**
+ * toggles collisions between linked bodys<br>
+ * joint has to be removed from and added to PhyiscsSpace to apply this.
+ * @param collisionBetweenLinkedBodys set to false to have no collisions between linked bodys
+ */
+ public void setCollisionBetweenLinkedBodys(boolean collisionBetweenLinkedBodys) {
+ this.collisionBetweenLinkedBodys = collisionBetweenLinkedBodys;
+ }
+
+ public PhysicsRigidBody getBodyA() {
+ return nodeA;
+ }
+
+ public PhysicsRigidBody getBodyB() {
+ return nodeB;
+ }
+
+ public Vector3f getPivotA() {
+ return pivotA;
+ }
+
+ public Vector3f getPivotB() {
+ return pivotB;
+ }
+
+ /**
+ * destroys this joint and removes it from its connected PhysicsRigidBodys joint lists
+ */
+ public void destroy() {
+ getBodyA().removeJoint(this);
+ getBodyB().removeJoint(this);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(nodeA, "nodeA", null);
+ capsule.write(nodeB, "nodeB", null);
+ capsule.write(pivotA, "pivotA", null);
+ capsule.write(pivotB, "pivotB", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ this.nodeA = ((PhysicsRigidBody) capsule.readSavable("nodeA", new PhysicsRigidBody()));
+ this.nodeB = (PhysicsRigidBody) capsule.readSavable("nodeB", new PhysicsRigidBody());
+ this.pivotA = (Vector3f) capsule.readSavable("pivotA", new Vector3f());
+ this.pivotB = (Vector3f) capsule.readSavable("pivotB", new Vector3f());
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Joint {0}", Long.toHexString(objectId));
+ finalizeNative(objectId);
+ }
+
+ private native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/Point2PointJoint.java b/engine/src/bullet/com/jme3/bullet/joints/Point2PointJoint.java
new file mode 100644
index 0000000..15f6e3a
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/Point2PointJoint.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * Point to point constraint, also known as ball socket joint limits the translation
+ * so that the local pivot points of 2 rigidbodies match in worldspace.
+ * A chain of rigidbodies can be connected using this constraint.
+ * @author normenhansen
+ */
+public class Point2PointJoint extends PhysicsJoint {
+
+ public Point2PointJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public Point2PointJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ createJoint();
+ }
+
+ public void setDamping(float value) {
+ setDamping(objectId, value);
+ }
+
+ private native void setDamping(long objectId, float value);
+
+ public void setImpulseClamp(float value) {
+ setImpulseClamp(objectId, value);
+ }
+
+ private native void setImpulseClamp(long objectId, float value);
+
+ public void setTau(float value) {
+ setTau(objectId, value);
+ }
+
+ private native void setTau(long objectId, float value);
+
+ public float getDamping() {
+ return getDamping(objectId);
+ }
+
+ private native float getDamping(long objectId);
+
+ public float getImpulseClamp() {
+ return getImpulseClamp(objectId);
+ }
+
+ private native float getImpulseClamp(long objectId);
+
+ public float getTau() {
+ return getTau(objectId);
+ }
+
+ private native float getTau(long objectId);
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule cap = ex.getCapsule(this);
+ cap.write(getDamping(), "damping", 1.0f);
+ cap.write(getTau(), "tau", 0.3f);
+ cap.write(getImpulseClamp(), "impulseClamp", 0f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ createJoint();
+ InputCapsule cap = im.getCapsule(this);
+ setDamping(cap.readFloat("damping", 1.0f));
+ setDamping(cap.readFloat("tau", 0.3f));
+ setDamping(cap.readFloat("impulseClamp", 0f));
+ }
+
+ protected void createJoint() {
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, pivotB);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ }
+
+ private native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Vector3f pivotB);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/SixDofJoint.java b/engine/src/bullet/com/jme3/bullet/joints/SixDofJoint.java
new file mode 100644
index 0000000..2760322
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/SixDofJoint.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.joints.motors.RotationalLimitMotor;
+import com.jme3.bullet.joints.motors.TranslationalLimitMotor;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * This generic constraint can emulate a variety of standard constraints,
+ * by configuring each of the 6 degrees of freedom (dof).
+ * The first 3 dof axis are linear axis, which represent translation of rigidbodies,
+ * and the latter 3 dof axis represent the angular motion. Each axis can be either locked,
+ * free or limited. On construction of a new btGeneric6DofConstraint, all axis are locked.
+ * Afterwards the axis can be reconfigured. Note that several combinations that
+ * include free and/or limited angular degrees of freedom are undefined.
+ * @author normenhansen
+ */
+public class SixDofJoint extends PhysicsJoint {
+
+ Matrix3f rotA, rotB;
+ boolean useLinearReferenceFrameA;
+ LinkedList<RotationalLimitMotor> rotationalMotors = new LinkedList<RotationalLimitMotor>();
+ TranslationalLimitMotor translationalMotor;
+ Vector3f angularUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY);
+ Vector3f angularLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY);
+ Vector3f linearUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY);
+ Vector3f linearLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY);
+
+ public SixDofJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+ this.rotA = rotA;
+ this.rotB = rotB;
+
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB, useLinearReferenceFrameA);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ gatherMotors();
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+ rotA = new Matrix3f();
+ rotB = new Matrix3f();
+
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB, useLinearReferenceFrameA);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ gatherMotors();
+ }
+
+ private void gatherMotors() {
+ for (int i = 0; i < 3; i++) {
+ RotationalLimitMotor rmot = new RotationalLimitMotor(getRotationalLimitMotor(objectId, i));
+ rotationalMotors.add(rmot);
+ }
+ translationalMotor = new TranslationalLimitMotor(getTranslationalLimitMotor(objectId));
+ }
+
+ private native long getRotationalLimitMotor(long objectId, int index);
+
+ private native long getTranslationalLimitMotor(long objectId);
+
+ /**
+ * returns the TranslationalLimitMotor of this 6DofJoint which allows
+ * manipulating the translational axis
+ * @return the TranslationalLimitMotor
+ */
+ public TranslationalLimitMotor getTranslationalLimitMotor() {
+ return translationalMotor;
+ }
+
+ /**
+ * returns one of the three RotationalLimitMotors of this 6DofJoint which
+ * allow manipulating the rotational axes
+ * @param index the index of the RotationalLimitMotor
+ * @return the RotationalLimitMotor at the given index
+ */
+ public RotationalLimitMotor getRotationalLimitMotor(int index) {
+ return rotationalMotors.get(index);
+ }
+
+ public void setLinearUpperLimit(Vector3f vector) {
+ linearUpperLimit.set(vector);
+ setLinearUpperLimit(objectId, vector);
+ }
+
+ private native void setLinearUpperLimit(long objctId, Vector3f vector);
+
+ public void setLinearLowerLimit(Vector3f vector) {
+ linearLowerLimit.set(vector);
+ setLinearLowerLimit(objectId, vector);
+ }
+
+ private native void setLinearLowerLimit(long objctId, Vector3f vector);
+
+ public void setAngularUpperLimit(Vector3f vector) {
+ angularUpperLimit.set(vector);
+ setAngularUpperLimit(objectId, vector);
+ }
+
+ private native void setAngularUpperLimit(long objctId, Vector3f vector);
+
+ public void setAngularLowerLimit(Vector3f vector) {
+ angularLowerLimit.set(vector);
+ setAngularLowerLimit(objectId, vector);
+ }
+
+ private native void setAngularLowerLimit(long objctId, Vector3f vector);
+
+ native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Matrix3f rotA, Vector3f pivotB, Matrix3f rotB, boolean useLinearReferenceFrameA);
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB, useLinearReferenceFrameA);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ gatherMotors();
+
+ setAngularUpperLimit((Vector3f) capsule.readSavable("angularUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY)));
+ setAngularLowerLimit((Vector3f) capsule.readSavable("angularLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY)));
+ setLinearUpperLimit((Vector3f) capsule.readSavable("linearUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY)));
+ setLinearLowerLimit((Vector3f) capsule.readSavable("linearLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY)));
+
+ for (int i = 0; i < 3; i++) {
+ RotationalLimitMotor rotationalLimitMotor = getRotationalLimitMotor(i);
+ rotationalLimitMotor.setBounce(capsule.readFloat("rotMotor" + i + "_Bounce", 0.0f));
+ rotationalLimitMotor.setDamping(capsule.readFloat("rotMotor" + i + "_Damping", 1.0f));
+ rotationalLimitMotor.setERP(capsule.readFloat("rotMotor" + i + "_ERP", 0.5f));
+ rotationalLimitMotor.setHiLimit(capsule.readFloat("rotMotor" + i + "_HiLimit", Float.POSITIVE_INFINITY));
+ rotationalLimitMotor.setLimitSoftness(capsule.readFloat("rotMotor" + i + "_LimitSoftness", 0.5f));
+ rotationalLimitMotor.setLoLimit(capsule.readFloat("rotMotor" + i + "_LoLimit", Float.NEGATIVE_INFINITY));
+ rotationalLimitMotor.setMaxLimitForce(capsule.readFloat("rotMotor" + i + "_MaxLimitForce", 300.0f));
+ rotationalLimitMotor.setMaxMotorForce(capsule.readFloat("rotMotor" + i + "_MaxMotorForce", 0.1f));
+ rotationalLimitMotor.setTargetVelocity(capsule.readFloat("rotMotor" + i + "_TargetVelocity", 0));
+ rotationalLimitMotor.setEnableMotor(capsule.readBoolean("rotMotor" + i + "_EnableMotor", false));
+ }
+ getTranslationalLimitMotor().setAccumulatedImpulse((Vector3f) capsule.readSavable("transMotor_AccumulatedImpulse", Vector3f.ZERO));
+ getTranslationalLimitMotor().setDamping(capsule.readFloat("transMotor_Damping", 1.0f));
+ getTranslationalLimitMotor().setLimitSoftness(capsule.readFloat("transMotor_LimitSoftness", 0.7f));
+ getTranslationalLimitMotor().setLowerLimit((Vector3f) capsule.readSavable("transMotor_LowerLimit", Vector3f.ZERO));
+ getTranslationalLimitMotor().setRestitution(capsule.readFloat("transMotor_Restitution", 0.5f));
+ getTranslationalLimitMotor().setUpperLimit((Vector3f) capsule.readSavable("transMotor_UpperLimit", Vector3f.ZERO));
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(angularUpperLimit, "angularUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY));
+ capsule.write(angularLowerLimit, "angularLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY));
+ capsule.write(linearUpperLimit, "linearUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY));
+ capsule.write(linearLowerLimit, "linearLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY));
+ int i = 0;
+ for (Iterator<RotationalLimitMotor> it = rotationalMotors.iterator(); it.hasNext();) {
+ RotationalLimitMotor rotationalLimitMotor = it.next();
+ capsule.write(rotationalLimitMotor.getBounce(), "rotMotor" + i + "_Bounce", 0.0f);
+ capsule.write(rotationalLimitMotor.getDamping(), "rotMotor" + i + "_Damping", 1.0f);
+ capsule.write(rotationalLimitMotor.getERP(), "rotMotor" + i + "_ERP", 0.5f);
+ capsule.write(rotationalLimitMotor.getHiLimit(), "rotMotor" + i + "_HiLimit", Float.POSITIVE_INFINITY);
+ capsule.write(rotationalLimitMotor.getLimitSoftness(), "rotMotor" + i + "_LimitSoftness", 0.5f);
+ capsule.write(rotationalLimitMotor.getLoLimit(), "rotMotor" + i + "_LoLimit", Float.NEGATIVE_INFINITY);
+ capsule.write(rotationalLimitMotor.getMaxLimitForce(), "rotMotor" + i + "_MaxLimitForce", 300.0f);
+ capsule.write(rotationalLimitMotor.getMaxMotorForce(), "rotMotor" + i + "_MaxMotorForce", 0.1f);
+ capsule.write(rotationalLimitMotor.getTargetVelocity(), "rotMotor" + i + "_TargetVelocity", 0);
+ capsule.write(rotationalLimitMotor.isEnableMotor(), "rotMotor" + i + "_EnableMotor", false);
+ i++;
+ }
+ capsule.write(getTranslationalLimitMotor().getAccumulatedImpulse(), "transMotor_AccumulatedImpulse", Vector3f.ZERO);
+ capsule.write(getTranslationalLimitMotor().getDamping(), "transMotor_Damping", 1.0f);
+ capsule.write(getTranslationalLimitMotor().getLimitSoftness(), "transMotor_LimitSoftness", 0.7f);
+ capsule.write(getTranslationalLimitMotor().getLowerLimit(), "transMotor_LowerLimit", Vector3f.ZERO);
+ capsule.write(getTranslationalLimitMotor().getRestitution(), "transMotor_Restitution", 0.5f);
+ capsule.write(getTranslationalLimitMotor().getUpperLimit(), "transMotor_UpperLimit", Vector3f.ZERO);
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/SixDofSpringJoint.java b/engine/src/bullet/com/jme3/bullet/joints/SixDofSpringJoint.java
new file mode 100644
index 0000000..df77f49
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/SixDofSpringJoint.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * This generic constraint can emulate a variety of standard constraints,
+ * by configuring each of the 6 degrees of freedom (dof).
+ * The first 3 dof axis are linear axis, which represent translation of rigidbodies,
+ * and the latter 3 dof axis represent the angular motion. Each axis can be either locked,
+ * free or limited. On construction of a new btGeneric6DofConstraint, all axis are locked.
+ * Afterwards the axis can be reconfigured. Note that several combinations that
+ * include free and/or limited angular degrees of freedom are undefined.
+ * @author normenhansen
+ */
+public class SixDofSpringJoint extends SixDofJoint {
+
+ final boolean springEnabled[] = new boolean[6];
+ final float equilibriumPoint[] = new float[6];
+ final float springStiffness[] = new float[6];
+ final float springDamping[] = new float[6]; // between 0 and 1 (1 == no damping)
+
+ public SixDofSpringJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SixDofSpringJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB, rotA, rotB, useLinearReferenceFrameA);
+ }
+ public void enableSpring(int index, boolean onOff) {
+ enableSpring(objectId, index, onOff);
+ }
+ native void enableSpring(long objctId, int index, boolean onOff);
+
+ public void setStiffness(int index, float stiffness) {
+ setStiffness(objectId, index, stiffness);
+ }
+ native void setStiffness(long objctId, int index, float stiffness);
+
+ public void setDamping(int index, float damping) {
+ setDamping(objectId, index, damping);
+
+ }
+ native void setDamping(long objctId, int index, float damping);
+ public void setEquilibriumPoint() { // set the current constraint position/orientation as an equilibrium point for all DOF
+ setEquilibriumPoint(objectId);
+ }
+ native void setEquilibriumPoint(long objctId);
+ public void setEquilibriumPoint(int index){ // set the current constraint position/orientation as an equilibrium point for given DOF
+ setEquilibriumPoint(objectId, index);
+ }
+ native void setEquilibriumPoint(long objctId, int index);
+ @Override
+ native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Matrix3f rotA, Vector3f pivotB, Matrix3f rotB, boolean useLinearReferenceFrameA);
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/SliderJoint.java b/engine/src/bullet/com/jme3/bullet/joints/SliderJoint.java
new file mode 100644
index 0000000..400cd28
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/SliderJoint.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * The slider constraint allows the body to rotate around one axis and translate along this axis.
+ * @author normenhansen
+ */
+public class SliderJoint extends PhysicsJoint {
+
+ protected Matrix3f rotA, rotB;
+ protected boolean useLinearReferenceFrameA;
+
+ public SliderJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA = rotA;
+ this.rotB = rotB;
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+ createJoint();
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA = new Matrix3f();
+ this.rotB = new Matrix3f();
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+ createJoint();
+ }
+
+ public float getLowerLinLimit() {
+ return getLowerLinLimit(objectId);
+ }
+
+ private native float getLowerLinLimit(long objectId);
+
+ public void setLowerLinLimit(float lowerLinLimit) {
+ setLowerLinLimit(objectId, lowerLinLimit);
+ }
+
+ private native void setLowerLinLimit(long objectId, float value);
+
+ public float getUpperLinLimit() {
+ return getUpperLinLimit(objectId);
+ }
+
+ private native float getUpperLinLimit(long objectId);
+
+ public void setUpperLinLimit(float upperLinLimit) {
+ setUpperLinLimit(objectId, upperLinLimit);
+ }
+
+ private native void setUpperLinLimit(long objectId, float value);
+
+ public float getLowerAngLimit() {
+ return getLowerAngLimit(objectId);
+ }
+
+ private native float getLowerAngLimit(long objectId);
+
+ public void setLowerAngLimit(float lowerAngLimit) {
+ setLowerAngLimit(objectId, lowerAngLimit);
+ }
+
+ private native void setLowerAngLimit(long objectId, float value);
+
+ public float getUpperAngLimit() {
+ return getUpperAngLimit(objectId);
+ }
+
+ private native float getUpperAngLimit(long objectId);
+
+ public void setUpperAngLimit(float upperAngLimit) {
+ setUpperAngLimit(objectId, upperAngLimit);
+ }
+
+ private native void setUpperAngLimit(long objectId, float value);
+
+ public float getSoftnessDirLin() {
+ return getSoftnessDirLin(objectId);
+ }
+
+ private native float getSoftnessDirLin(long objectId);
+
+ public void setSoftnessDirLin(float softnessDirLin) {
+ setSoftnessDirLin(objectId, softnessDirLin);
+ }
+
+ private native void setSoftnessDirLin(long objectId, float value);
+
+ public float getRestitutionDirLin() {
+ return getRestitutionDirLin(objectId);
+ }
+
+ private native float getRestitutionDirLin(long objectId);
+
+ public void setRestitutionDirLin(float restitutionDirLin) {
+ setRestitutionDirLin(objectId, restitutionDirLin);
+ }
+
+ private native void setRestitutionDirLin(long objectId, float value);
+
+ public float getDampingDirLin() {
+ return getDampingDirLin(objectId);
+ }
+
+ private native float getDampingDirLin(long objectId);
+
+ public void setDampingDirLin(float dampingDirLin) {
+ setDampingDirLin(objectId, dampingDirLin);
+ }
+
+ private native void setDampingDirLin(long objectId, float value);
+
+ public float getSoftnessDirAng() {
+ return getSoftnessDirAng(objectId);
+ }
+
+ private native float getSoftnessDirAng(long objectId);
+
+ public void setSoftnessDirAng(float softnessDirAng) {
+ setSoftnessDirAng(objectId, softnessDirAng);
+ }
+
+ private native void setSoftnessDirAng(long objectId, float value);
+
+ public float getRestitutionDirAng() {
+ return getRestitutionDirAng(objectId);
+ }
+
+ private native float getRestitutionDirAng(long objectId);
+
+ public void setRestitutionDirAng(float restitutionDirAng) {
+ setRestitutionDirAng(objectId, restitutionDirAng);
+ }
+
+ private native void setRestitutionDirAng(long objectId, float value);
+
+ public float getDampingDirAng() {
+ return getDampingDirAng(objectId);
+ }
+
+ private native float getDampingDirAng(long objectId);
+
+ public void setDampingDirAng(float dampingDirAng) {
+ setDampingDirAng(objectId, dampingDirAng);
+ }
+
+ private native void setDampingDirAng(long objectId, float value);
+
+ public float getSoftnessLimLin() {
+ return getSoftnessLimLin(objectId);
+ }
+
+ private native float getSoftnessLimLin(long objectId);
+
+ public void setSoftnessLimLin(float softnessLimLin) {
+ setSoftnessLimLin(objectId, softnessLimLin);
+ }
+
+ private native void setSoftnessLimLin(long objectId, float value);
+
+ public float getRestitutionLimLin() {
+ return getRestitutionLimLin(objectId);
+ }
+
+ private native float getRestitutionLimLin(long objectId);
+
+ public void setRestitutionLimLin(float restitutionLimLin) {
+ setRestitutionLimLin(objectId, restitutionLimLin);
+ }
+
+ private native void setRestitutionLimLin(long objectId, float value);
+
+ public float getDampingLimLin() {
+ return getDampingLimLin(objectId);
+ }
+
+ private native float getDampingLimLin(long objectId);
+
+ public void setDampingLimLin(float dampingLimLin) {
+ setDampingLimLin(objectId, dampingLimLin);
+ }
+
+ private native void setDampingLimLin(long objectId, float value);
+
+ public float getSoftnessLimAng() {
+ return getSoftnessLimAng(objectId);
+ }
+
+ private native float getSoftnessLimAng(long objectId);
+
+ public void setSoftnessLimAng(float softnessLimAng) {
+ setSoftnessLimAng(objectId, softnessLimAng);
+ }
+
+ private native void setSoftnessLimAng(long objectId, float value);
+
+ public float getRestitutionLimAng() {
+ return getRestitutionLimAng(objectId);
+ }
+
+ private native float getRestitutionLimAng(long objectId);
+
+ public void setRestitutionLimAng(float restitutionLimAng) {
+ setRestitutionLimAng(objectId, restitutionLimAng);
+ }
+
+ private native void setRestitutionLimAng(long objectId, float value);
+
+ public float getDampingLimAng() {
+ return getDampingLimAng(objectId);
+ }
+
+ private native float getDampingLimAng(long objectId);
+
+ public void setDampingLimAng(float dampingLimAng) {
+ setDampingLimAng(objectId, dampingLimAng);
+ }
+
+ private native void setDampingLimAng(long objectId, float value);
+
+ public float getSoftnessOrthoLin() {
+ return getSoftnessOrthoLin(objectId);
+ }
+
+ private native float getSoftnessOrthoLin(long objectId);
+
+ public void setSoftnessOrthoLin(float softnessOrthoLin) {
+ setSoftnessOrthoLin(objectId, softnessOrthoLin);
+ }
+
+ private native void setSoftnessOrthoLin(long objectId, float value);
+
+ public float getRestitutionOrthoLin() {
+ return getRestitutionOrthoLin(objectId);
+ }
+
+ private native float getRestitutionOrthoLin(long objectId);
+
+ public void setRestitutionOrthoLin(float restitutionOrthoLin) {
+ setDampingOrthoLin(objectId, restitutionOrthoLin);
+ }
+
+ private native void setRestitutionOrthoLin(long objectId, float value);
+
+ public float getDampingOrthoLin() {
+ return getDampingOrthoLin(objectId);
+ }
+
+ private native float getDampingOrthoLin(long objectId);
+
+ public void setDampingOrthoLin(float dampingOrthoLin) {
+ setDampingOrthoLin(objectId, dampingOrthoLin);
+ }
+
+ private native void setDampingOrthoLin(long objectId, float value);
+
+ public float getSoftnessOrthoAng() {
+ return getSoftnessOrthoAng(objectId);
+ }
+
+ private native float getSoftnessOrthoAng(long objectId);
+
+ public void setSoftnessOrthoAng(float softnessOrthoAng) {
+ setSoftnessOrthoAng(objectId, softnessOrthoAng);
+ }
+
+ private native void setSoftnessOrthoAng(long objectId, float value);
+
+ public float getRestitutionOrthoAng() {
+ return getRestitutionOrthoAng(objectId);
+ }
+
+ private native float getRestitutionOrthoAng(long objectId);
+
+ public void setRestitutionOrthoAng(float restitutionOrthoAng) {
+ setRestitutionOrthoAng(objectId, restitutionOrthoAng);
+ }
+
+ private native void setRestitutionOrthoAng(long objectId, float value);
+
+ public float getDampingOrthoAng() {
+ return getDampingOrthoAng(objectId);
+ }
+
+ private native float getDampingOrthoAng(long objectId);
+
+ public void setDampingOrthoAng(float dampingOrthoAng) {
+ setDampingOrthoAng(objectId, dampingOrthoAng);
+ }
+
+ private native void setDampingOrthoAng(long objectId, float value);
+
+ public boolean isPoweredLinMotor() {
+ return isPoweredLinMotor(objectId);
+ }
+
+ private native boolean isPoweredLinMotor(long objectId);
+
+ public void setPoweredLinMotor(boolean poweredLinMotor) {
+ setPoweredLinMotor(objectId, poweredLinMotor);
+ }
+
+ private native void setPoweredLinMotor(long objectId, boolean value);
+
+ public float getTargetLinMotorVelocity() {
+ return getTargetLinMotorVelocity(objectId);
+ }
+
+ private native float getTargetLinMotorVelocity(long objectId);
+
+ public void setTargetLinMotorVelocity(float targetLinMotorVelocity) {
+ setTargetLinMotorVelocity(objectId, targetLinMotorVelocity);
+ }
+
+ private native void setTargetLinMotorVelocity(long objectId, float value);
+
+ public float getMaxLinMotorForce() {
+ return getMaxLinMotorForce(objectId);
+ }
+
+ private native float getMaxLinMotorForce(long objectId);
+
+ public void setMaxLinMotorForce(float maxLinMotorForce) {
+ setMaxLinMotorForce(objectId, maxLinMotorForce);
+ }
+
+ private native void setMaxLinMotorForce(long objectId, float value);
+
+ public boolean isPoweredAngMotor() {
+ return isPoweredAngMotor(objectId);
+ }
+
+ private native boolean isPoweredAngMotor(long objectId);
+
+ public void setPoweredAngMotor(boolean poweredAngMotor) {
+ setPoweredAngMotor(objectId, poweredAngMotor);
+ }
+
+ private native void setPoweredAngMotor(long objectId, boolean value);
+
+ public float getTargetAngMotorVelocity() {
+ return getTargetAngMotorVelocity(objectId);
+ }
+
+ private native float getTargetAngMotorVelocity(long objectId);
+
+ public void setTargetAngMotorVelocity(float targetAngMotorVelocity) {
+ setTargetAngMotorVelocity(objectId, targetAngMotorVelocity);
+ }
+
+ private native void setTargetAngMotorVelocity(long objectId, float value);
+
+ public float getMaxAngMotorForce() {
+ return getMaxAngMotorForce(objectId);
+ }
+
+ private native float getMaxAngMotorForce(long objectId);
+
+ public void setMaxAngMotorForce(float maxAngMotorForce) {
+ setMaxAngMotorForce(objectId, maxAngMotorForce);
+ }
+
+ private native void setMaxAngMotorForce(long objectId, float value);
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ //TODO: standard values..
+ capsule.write(getDampingDirAng(), "dampingDirAng", 0f);
+ capsule.write(getDampingDirLin(), "dampingDirLin", 0f);
+ capsule.write(getDampingLimAng(), "dampingLimAng", 0f);
+ capsule.write(getDampingLimLin(), "dampingLimLin", 0f);
+ capsule.write(getDampingOrthoAng(), "dampingOrthoAng", 0f);
+ capsule.write(getDampingOrthoLin(), "dampingOrthoLin", 0f);
+ capsule.write(getLowerAngLimit(), "lowerAngLimit", 0f);
+ capsule.write(getLowerLinLimit(), "lowerLinLimit", 0f);
+ capsule.write(getMaxAngMotorForce(), "maxAngMotorForce", 0f);
+ capsule.write(getMaxLinMotorForce(), "maxLinMotorForce", 0f);
+ capsule.write(isPoweredAngMotor(), "poweredAngMotor", false);
+ capsule.write(isPoweredLinMotor(), "poweredLinMotor", false);
+ capsule.write(getRestitutionDirAng(), "restitutionDirAng", 0f);
+ capsule.write(getRestitutionDirLin(), "restitutionDirLin", 0f);
+ capsule.write(getRestitutionLimAng(), "restitutionLimAng", 0f);
+ capsule.write(getRestitutionLimLin(), "restitutionLimLin", 0f);
+ capsule.write(getRestitutionOrthoAng(), "restitutionOrthoAng", 0f);
+ capsule.write(getRestitutionOrthoLin(), "restitutionOrthoLin", 0f);
+
+ capsule.write(getSoftnessDirAng(), "softnessDirAng", 0f);
+ capsule.write(getSoftnessDirLin(), "softnessDirLin", 0f);
+ capsule.write(getSoftnessLimAng(), "softnessLimAng", 0f);
+ capsule.write(getSoftnessLimLin(), "softnessLimLin", 0f);
+ capsule.write(getSoftnessOrthoAng(), "softnessOrthoAng", 0f);
+ capsule.write(getSoftnessOrthoLin(), "softnessOrthoLin", 0f);
+
+ capsule.write(getTargetAngMotorVelocity(), "targetAngMotorVelicoty", 0f);
+ capsule.write(getTargetLinMotorVelocity(), "targetLinMotorVelicoty", 0f);
+
+ capsule.write(getUpperAngLimit(), "upperAngLimit", 0f);
+ capsule.write(getUpperLinLimit(), "upperLinLimit", 0f);
+
+ capsule.write(useLinearReferenceFrameA, "useLinearReferenceFrameA", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ float dampingDirAng = capsule.readFloat("dampingDirAng", 0f);
+ float dampingDirLin = capsule.readFloat("dampingDirLin", 0f);
+ float dampingLimAng = capsule.readFloat("dampingLimAng", 0f);
+ float dampingLimLin = capsule.readFloat("dampingLimLin", 0f);
+ float dampingOrthoAng = capsule.readFloat("dampingOrthoAng", 0f);
+ float dampingOrthoLin = capsule.readFloat("dampingOrthoLin", 0f);
+ float lowerAngLimit = capsule.readFloat("lowerAngLimit", 0f);
+ float lowerLinLimit = capsule.readFloat("lowerLinLimit", 0f);
+ float maxAngMotorForce = capsule.readFloat("maxAngMotorForce", 0f);
+ float maxLinMotorForce = capsule.readFloat("maxLinMotorForce", 0f);
+ boolean poweredAngMotor = capsule.readBoolean("poweredAngMotor", false);
+ boolean poweredLinMotor = capsule.readBoolean("poweredLinMotor", false);
+ float restitutionDirAng = capsule.readFloat("restitutionDirAng", 0f);
+ float restitutionDirLin = capsule.readFloat("restitutionDirLin", 0f);
+ float restitutionLimAng = capsule.readFloat("restitutionLimAng", 0f);
+ float restitutionLimLin = capsule.readFloat("restitutionLimLin", 0f);
+ float restitutionOrthoAng = capsule.readFloat("restitutionOrthoAng", 0f);
+ float restitutionOrthoLin = capsule.readFloat("restitutionOrthoLin", 0f);
+
+ float softnessDirAng = capsule.readFloat("softnessDirAng", 0f);
+ float softnessDirLin = capsule.readFloat("softnessDirLin", 0f);
+ float softnessLimAng = capsule.readFloat("softnessLimAng", 0f);
+ float softnessLimLin = capsule.readFloat("softnessLimLin", 0f);
+ float softnessOrthoAng = capsule.readFloat("softnessOrthoAng", 0f);
+ float softnessOrthoLin = capsule.readFloat("softnessOrthoLin", 0f);
+
+ float targetAngMotorVelicoty = capsule.readFloat("targetAngMotorVelicoty", 0f);
+ float targetLinMotorVelicoty = capsule.readFloat("targetLinMotorVelicoty", 0f);
+
+ float upperAngLimit = capsule.readFloat("upperAngLimit", 0f);
+ float upperLinLimit = capsule.readFloat("upperLinLimit", 0f);
+
+ useLinearReferenceFrameA = capsule.readBoolean("useLinearReferenceFrameA", false);
+
+ createJoint();
+
+ setDampingDirAng(dampingDirAng);
+ setDampingDirLin(dampingDirLin);
+ setDampingLimAng(dampingLimAng);
+ setDampingLimLin(dampingLimLin);
+ setDampingOrthoAng(dampingOrthoAng);
+ setDampingOrthoLin(dampingOrthoLin);
+ setLowerAngLimit(lowerAngLimit);
+ setLowerLinLimit(lowerLinLimit);
+ setMaxAngMotorForce(maxAngMotorForce);
+ setMaxLinMotorForce(maxLinMotorForce);
+ setPoweredAngMotor(poweredAngMotor);
+ setPoweredLinMotor(poweredLinMotor);
+ setRestitutionDirAng(restitutionDirAng);
+ setRestitutionDirLin(restitutionDirLin);
+ setRestitutionLimAng(restitutionLimAng);
+ setRestitutionLimLin(restitutionLimLin);
+ setRestitutionOrthoAng(restitutionOrthoAng);
+ setRestitutionOrthoLin(restitutionOrthoLin);
+
+ setSoftnessDirAng(softnessDirAng);
+ setSoftnessDirLin(softnessDirLin);
+ setSoftnessLimAng(softnessLimAng);
+ setSoftnessLimLin(softnessLimLin);
+ setSoftnessOrthoAng(softnessOrthoAng);
+ setSoftnessOrthoLin(softnessOrthoLin);
+
+ setTargetAngMotorVelocity(targetAngMotorVelicoty);
+ setTargetLinMotorVelocity(targetLinMotorVelicoty);
+
+ setUpperAngLimit(upperAngLimit);
+ setUpperLinLimit(upperLinLimit);
+ }
+
+ protected void createJoint() {
+ objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB, useLinearReferenceFrameA);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Joint {0}", Long.toHexString(objectId));
+ // = new SliderConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA);
+ }
+
+ private native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Matrix3f rotA, Vector3f pivotB, Matrix3f rotB, boolean useLinearReferenceFrameA);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java b/engine/src/bullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java
new file mode 100644
index 0000000..83b397b
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints.motors;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class RotationalLimitMotor {
+
+ private long motorId = 0;
+
+ public RotationalLimitMotor(long motor) {
+ this.motorId = motor;
+ }
+
+ public long getMotor() {
+ return motorId;
+ }
+
+ public float getLoLimit() {
+ return getLoLimit(motorId);
+ }
+
+ private native float getLoLimit(long motorId);
+
+ public void setLoLimit(float loLimit) {
+ setLoLimit(motorId, loLimit);
+ }
+
+ private native void setLoLimit(long motorId, float loLimit);
+
+ public float getHiLimit() {
+ return getHiLimit(motorId);
+ }
+
+ private native float getHiLimit(long motorId);
+
+ public void setHiLimit(float hiLimit) {
+ setHiLimit(motorId, hiLimit);
+ }
+
+ private native void setHiLimit(long motorId, float hiLimit);
+
+ public float getTargetVelocity() {
+ return getTargetVelocity(motorId);
+ }
+
+ private native float getTargetVelocity(long motorId);
+
+ public void setTargetVelocity(float targetVelocity) {
+ setTargetVelocity(motorId, targetVelocity);
+ }
+
+ private native void setTargetVelocity(long motorId, float targetVelocity);
+
+ public float getMaxMotorForce() {
+ return getMaxMotorForce(motorId);
+ }
+
+ private native float getMaxMotorForce(long motorId);
+
+ public void setMaxMotorForce(float maxMotorForce) {
+ setMaxMotorForce(motorId, maxMotorForce);
+ }
+
+ private native void setMaxMotorForce(long motorId, float maxMotorForce);
+
+ public float getMaxLimitForce() {
+ return getMaxLimitForce(motorId);
+ }
+
+ private native float getMaxLimitForce(long motorId);
+
+ public void setMaxLimitForce(float maxLimitForce) {
+ setMaxLimitForce(motorId, maxLimitForce);
+ }
+
+ private native void setMaxLimitForce(long motorId, float maxLimitForce);
+
+ public float getDamping() {
+ return getDamping(motorId);
+ }
+
+ private native float getDamping(long motorId);
+
+ public void setDamping(float damping) {
+ setDamping(motorId, damping);
+ }
+
+ private native void setDamping(long motorId, float damping);
+
+ public float getLimitSoftness() {
+ return getLimitSoftness(motorId);
+ }
+
+ private native float getLimitSoftness(long motorId);
+
+ public void setLimitSoftness(float limitSoftness) {
+ setLimitSoftness(motorId, limitSoftness);
+ }
+
+ private native void setLimitSoftness(long motorId, float limitSoftness);
+
+ public float getERP() {
+ return getERP(motorId);
+ }
+
+ private native float getERP(long motorId);
+
+ public void setERP(float ERP) {
+ setERP(motorId, ERP);
+ }
+
+ private native void setERP(long motorId, float ERP);
+
+ public float getBounce() {
+ return getBounce(motorId);
+ }
+
+ private native float getBounce(long motorId);
+
+ public void setBounce(float bounce) {
+ setBounce(motorId, bounce);
+ }
+
+ private native void setBounce(long motorId, float limitSoftness);
+
+ public boolean isEnableMotor() {
+ return isEnableMotor(motorId);
+ }
+
+ private native boolean isEnableMotor(long motorId);
+
+ public void setEnableMotor(boolean enableMotor) {
+ setEnableMotor(motorId, enableMotor);
+ }
+
+ private native void setEnableMotor(long motorId, boolean enableMotor);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java b/engine/src/bullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java
new file mode 100644
index 0000000..2e3910c
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints.motors;
+
+import com.jme3.math.Vector3f;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TranslationalLimitMotor {
+
+ private long motorId = 0;
+
+ public TranslationalLimitMotor(long motor) {
+ this.motorId = motor;
+ }
+
+ public long getMotor() {
+ return motorId;
+ }
+
+ public Vector3f getLowerLimit() {
+ Vector3f vec = new Vector3f();
+ getLowerLimit(motorId, vec);
+ return vec;
+ }
+
+ private native void getLowerLimit(long motorId, Vector3f vector);
+
+ public void setLowerLimit(Vector3f lowerLimit) {
+ setLowerLimit(motorId, lowerLimit);
+ }
+
+ private native void setLowerLimit(long motorId, Vector3f vector);
+
+ public Vector3f getUpperLimit() {
+ Vector3f vec = new Vector3f();
+ getUpperLimit(motorId, vec);
+ return vec;
+ }
+
+ private native void getUpperLimit(long motorId, Vector3f vector);
+
+ public void setUpperLimit(Vector3f upperLimit) {
+ setUpperLimit(motorId, upperLimit);
+ }
+
+ private native void setUpperLimit(long motorId, Vector3f vector);
+
+ public Vector3f getAccumulatedImpulse() {
+ Vector3f vec = new Vector3f();
+ getAccumulatedImpulse(motorId, vec);
+ return vec;
+ }
+
+ private native void getAccumulatedImpulse(long motorId, Vector3f vector);
+
+ public void setAccumulatedImpulse(Vector3f accumulatedImpulse) {
+ setAccumulatedImpulse(motorId, accumulatedImpulse);
+ }
+
+ private native void setAccumulatedImpulse(long motorId, Vector3f vector);
+
+ public float getLimitSoftness() {
+ return getLimitSoftness(motorId);
+ }
+
+ private native float getLimitSoftness(long motorId);
+
+ public void setLimitSoftness(float limitSoftness) {
+ setLimitSoftness(motorId, limitSoftness);
+ }
+
+ private native void setLimitSoftness(long motorId, float limitSoftness);
+
+ public float getDamping() {
+ return getDamping(motorId);
+ }
+
+ private native float getDamping(long motorId);
+
+ public void setDamping(float damping) {
+ setDamping(motorId, damping);
+ }
+
+ private native void setDamping(long motorId, float damping);
+
+ public float getRestitution() {
+ return getRestitution(motorId);
+ }
+
+ private native float getRestitution(long motorId);
+
+ public void setRestitution(float restitution) {
+ setRestitution(motorId, restitution);
+ }
+
+ private native void setRestitution(long motorId, float restitution);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java
new file mode 100644
index 0000000..fb98fec
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic Bullet Character
+ * @author normenhansen
+ */
+public class PhysicsCharacter extends PhysicsCollisionObject {
+
+ protected long characterId = 0;
+ protected float stepHeight;
+ protected Vector3f walkDirection = new Vector3f();
+ protected float fallSpeed = 55.0f;
+ protected float jumpSpeed = 10.0f;
+ protected int upAxis = 1;
+ protected boolean locationDirty = false;
+ //TEMP VARIABLES
+ protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
+
+ public PhysicsCharacter() {
+ }
+
+ /**
+ * @param shape The CollisionShape (no Mesh or CompoundCollisionShapes)
+ * @param stepHeight The quantization size for vertical movement
+ */
+ public PhysicsCharacter(CollisionShape shape, float stepHeight) {
+ this.collisionShape = shape;
+// if (shape instanceof MeshCollisionShape || shape instanceof CompoundCollisionShape) {
+// throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh or compound collision shapes"));
+// }
+ this.stepHeight = stepHeight;
+ buildObject();
+ }
+
+ protected void buildObject() {
+ if (objectId == 0) {
+ objectId = createGhostObject();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Creating GhostObject {0}", Long.toHexString(objectId));
+ initUserPointer();
+ }
+ setCharacterFlags(objectId);
+ attachCollisionShape(objectId, collisionShape.getObjectId());
+ if (characterId != 0) {
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing Character {0}", Long.toHexString(objectId));
+ finalizeNativeCharacter(characterId);
+ }
+ characterId = createCharacterObject(objectId, collisionShape.getObjectId(), stepHeight);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Creating Character {0}", Long.toHexString(characterId));
+ }
+
+ private native long createGhostObject();
+
+ private native void setCharacterFlags(long objectId);
+
+ private native long createCharacterObject(long objectId, long shapeId, float stepHeight);
+
+ /**
+ * Sets the location of this physics character
+ * @param location
+ */
+ public void warp(Vector3f location) {
+ warp(characterId, location);
+ }
+
+ private native void warp(long characterId, Vector3f location);
+
+ /**
+ * Set the walk direction, works continuously.
+ * This should probably be called setPositionIncrementPerSimulatorStep.
+ * This is neither a direction nor a velocity, but the amount to
+ * increment the position each physics tick. So vector length = accuracy*speed in m/s
+ * @param vec the walk direction to set
+ */
+ public void setWalkDirection(Vector3f vec) {
+ walkDirection.set(vec);
+ setWalkDirection(characterId, vec);
+ }
+
+ private native void setWalkDirection(long characterId, Vector3f vec);
+
+ /**
+ * @return the currently set walkDirection
+ */
+ public Vector3f getWalkDirection() {
+ return walkDirection;
+ }
+
+ public void setUpAxis(int axis) {
+ upAxis = axis;
+ setUpAxis(characterId, axis);
+ }
+
+ private native void setUpAxis(long characterId, int axis);
+
+ public int getUpAxis() {
+ return upAxis;
+ }
+
+ public void setFallSpeed(float fallSpeed) {
+ this.fallSpeed = fallSpeed;
+ setFallSpeed(characterId, fallSpeed);
+ }
+
+ private native void setFallSpeed(long characterId, float fallSpeed);
+
+ public float getFallSpeed() {
+ return fallSpeed;
+ }
+
+ public void setJumpSpeed(float jumpSpeed) {
+ this.jumpSpeed = jumpSpeed;
+ setJumpSpeed(characterId, jumpSpeed);
+ }
+
+ private native void setJumpSpeed(long characterId, float jumpSpeed);
+
+ public float getJumpSpeed() {
+ return jumpSpeed;
+ }
+
+ public void setGravity(float value) {
+ setGravity(characterId, value);
+ }
+
+ private native void setGravity(long characterId, float gravity);
+
+ public float getGravity() {
+ return getGravity(characterId);
+ }
+
+ private native float getGravity(long characterId);
+
+ public void setMaxSlope(float slopeRadians) {
+ setMaxSlope(characterId, slopeRadians);
+ }
+
+ private native void setMaxSlope(long characterId, float slopeRadians);
+
+ public float getMaxSlope() {
+ return getMaxSlope(characterId);
+ }
+
+ private native float getMaxSlope(long characterId);
+
+ public boolean onGround() {
+ return onGround(characterId);
+ }
+
+ private native boolean onGround(long characterId);
+
+ public void jump() {
+ jump(characterId);
+ }
+
+ private native void jump(long characterId);
+
+ @Override
+ public void setCollisionShape(CollisionShape collisionShape) {
+// if (!(collisionShape.getObjectId() instanceof ConvexShape)) {
+// throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes"));
+// }
+ super.setCollisionShape(collisionShape);
+ if (objectId == 0) {
+ buildObject();
+ } else {
+ attachCollisionShape(objectId, collisionShape.getObjectId());
+ }
+ }
+
+ /**
+ * Set the physics location (same as warp())
+ * @param location the location of the actual physics object
+ */
+ public void setPhysicsLocation(Vector3f location) {
+ warp(location);
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation(Vector3f trans) {
+ if (trans == null) {
+ trans = new Vector3f();
+ }
+ getPhysicsLocation(objectId, trans);
+ return trans;
+ }
+
+ private native void getPhysicsLocation(long objectId, Vector3f vec);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation() {
+ return getPhysicsLocation(null);
+ }
+
+ public void setCcdSweptSphereRadius(float radius) {
+ setCcdSweptSphereRadius(objectId, radius);
+ }
+
+ private native void setCcdSweptSphereRadius(long objectId, float radius);
+
+ public void setCcdMotionThreshold(float threshold) {
+ setCcdMotionThreshold(objectId, threshold);
+ }
+
+ private native void setCcdMotionThreshold(long objectId, float threshold);
+
+ public float getCcdSweptSphereRadius() {
+ return getCcdSweptSphereRadius(objectId);
+ }
+
+ private native float getCcdSweptSphereRadius(long objectId);
+
+ public float getCcdMotionThreshold() {
+ return getCcdMotionThreshold(objectId);
+ }
+
+ private native float getCcdMotionThreshold(long objectId);
+
+ public float getCcdSquareMotionThreshold() {
+ return getCcdSquareMotionThreshold(objectId);
+ }
+
+ private native float getCcdSquareMotionThreshold(long objectId);
+
+ /**
+ * used internally
+ */
+ public long getControllerId() {
+ return characterId;
+ }
+
+ public void destroy() {
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(stepHeight, "stepHeight", 1.0f);
+ capsule.write(getGravity(), "gravity", 9.8f * 3);
+ capsule.write(getMaxSlope(), "maxSlope", 1.0f);
+ capsule.write(fallSpeed, "fallSpeed", 55.0f);
+ capsule.write(jumpSpeed, "jumpSpeed", 10.0f);
+ capsule.write(upAxis, "upAxis", 1);
+ capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
+ capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
+ capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ stepHeight = capsule.readFloat("stepHeight", 1.0f);
+ buildObject();
+ setGravity(capsule.readFloat("gravity", 9.8f * 3));
+ setMaxSlope(capsule.readFloat("maxSlope", 1.0f));
+ setFallSpeed(capsule.readFloat("fallSpeed", 55.0f));
+ setJumpSpeed(capsule.readFloat("jumpSpeed", 10.0f));
+ setUpAxis(capsule.readInt("upAxis", 1));
+ setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
+ setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
+ setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ finalizeNativeCharacter(characterId);
+ }
+
+ private native void finalizeNativeCharacter(long characterId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java
new file mode 100644
index 0000000..2f87164
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <i>From Bullet manual:</i><br>
+ * GhostObject can keep track of all objects that are overlapping.
+ * By default, this overlap is based on the AABB.
+ * This is useful for creating a character controller,
+ * collision sensors/triggers, explosions etc.<br>
+ * @author normenhansen
+ */
+public class PhysicsGhostObject extends PhysicsCollisionObject {
+
+ protected boolean locationDirty = false;
+ protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
+ private List<PhysicsCollisionObject> overlappingObjects = new LinkedList<PhysicsCollisionObject>();
+
+ public PhysicsGhostObject() {
+ }
+
+ public PhysicsGhostObject(CollisionShape shape) {
+ collisionShape = shape;
+ buildObject();
+ }
+
+ public PhysicsGhostObject(Spatial child, CollisionShape shape) {
+ collisionShape = shape;
+ buildObject();
+ }
+
+ protected void buildObject() {
+ if (objectId == 0) {
+// gObject = new PairCachingGhostObject();
+ objectId = createGhostObject();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Ghost Object {0}", Long.toHexString(objectId));
+ setGhostFlags(objectId);
+ initUserPointer();
+ }
+// if (gObject == null) {
+// gObject = new PairCachingGhostObject();
+// gObject.setCollisionFlags(gObject.getCollisionFlags() | CollisionFlags.NO_CONTACT_RESPONSE);
+// }
+ attachCollisionShape(objectId, collisionShape.getObjectId());
+ }
+
+ private native long createGhostObject();
+
+ private native void setGhostFlags(long objectId);
+
+ @Override
+ public void setCollisionShape(CollisionShape collisionShape) {
+ super.setCollisionShape(collisionShape);
+ if (objectId == 0) {
+ buildObject();
+ } else {
+ attachCollisionShape(objectId, collisionShape.getObjectId());
+ }
+ }
+
+ /**
+ * Sets the physics object location
+ * @param location the location of the actual physics object
+ */
+ public void setPhysicsLocation(Vector3f location) {
+ setPhysicsLocation(objectId, location);
+ }
+
+ private native void setPhysicsLocation(long objectId, Vector3f location);
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Matrix3f rotation) {
+ setPhysicsRotation(objectId, rotation);
+ }
+
+ private native void setPhysicsRotation(long objectId, Matrix3f rotation);
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Quaternion rotation) {
+ setPhysicsRotation(objectId, rotation);
+ }
+
+ private native void setPhysicsRotation(long objectId, Quaternion rotation);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation(Vector3f trans) {
+ if (trans == null) {
+ trans = new Vector3f();
+ }
+ getPhysicsLocation(objectId, trans);
+ return trans;
+ }
+
+ private native void getPhysicsLocation(long objectId, Vector3f vector);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Quaternion getPhysicsRotation(Quaternion rot) {
+ if (rot == null) {
+ rot = new Quaternion();
+ }
+ getPhysicsRotation(objectId, rot);
+ return rot;
+ }
+
+ private native void getPhysicsRotation(long objectId, Quaternion rot);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) {
+ if (rot == null) {
+ rot = new Matrix3f();
+ }
+ getPhysicsRotationMatrix(objectId, rot);
+ return rot;
+ }
+
+ private native void getPhysicsRotationMatrix(long objectId, Matrix3f rot);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation() {
+ Vector3f vec = new Vector3f();
+ getPhysicsLocation(objectId, vec);
+ return vec;
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Quaternion getPhysicsRotation() {
+ Quaternion quat = new Quaternion();
+ getPhysicsRotation(objectId, quat);
+ return quat;
+ }
+
+ public Matrix3f getPhysicsRotationMatrix() {
+ Matrix3f mtx = new Matrix3f();
+ getPhysicsRotationMatrix(objectId, mtx);
+ return mtx;
+ }
+
+ /**
+ * used internally
+ */
+// public PairCachingGhostObject getObjectId() {
+// return gObject;
+// }
+ /**
+ * destroys this PhysicsGhostNode and removes it from memory
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Another Object is overlapping with this GhostNode,
+ * if and if only there CollisionShapes overlaps.
+ * They could be both regular PhysicsRigidBodys or PhysicsGhostObjects.
+ * @return All CollisionObjects overlapping with this GhostNode.
+ */
+ public List<PhysicsCollisionObject> getOverlappingObjects() {
+ overlappingObjects.clear();
+ getOverlappingObjects(objectId);
+// for (com.bulletphysics.collision.dispatch.CollisionObject collObj : gObject.getOverlappingPairs()) {
+// overlappingObjects.add((PhysicsCollisionObject) collObj.getUserPointer());
+// }
+ return overlappingObjects;
+ }
+
+ protected native void getOverlappingObjects(long objectId);
+
+ private void addOverlappingObject_native(PhysicsCollisionObject co) {
+ overlappingObjects.add(co);
+ }
+
+ /**
+ *
+ * @return With how many other CollisionObjects this GhostNode is currently overlapping.
+ */
+ public int getOverlappingCount() {
+ return getOverlappingCount(objectId);
+ }
+
+ private native int getOverlappingCount(long objectId);
+
+ /**
+ *
+ * @param index The index of the overlapping Node to retrieve.
+ * @return The Overlapping CollisionObject at the given index.
+ */
+ public PhysicsCollisionObject getOverlapping(int index) {
+ return overlappingObjects.get(index);
+ }
+
+ public void setCcdSweptSphereRadius(float radius) {
+ setCcdSweptSphereRadius(objectId, radius);
+ }
+
+ private native void setCcdSweptSphereRadius(long objectId, float radius);
+
+ public void setCcdMotionThreshold(float threshold) {
+ setCcdMotionThreshold(objectId, threshold);
+ }
+
+ private native void setCcdMotionThreshold(long objectId, float threshold);
+
+ public float getCcdSweptSphereRadius() {
+ return getCcdSweptSphereRadius(objectId);
+ }
+
+ private native float getCcdSweptSphereRadius(long objectId);
+
+ public float getCcdMotionThreshold() {
+ return getCcdMotionThreshold(objectId);
+ }
+
+ private native float getCcdMotionThreshold(long objectId);
+
+ public float getCcdSquareMotionThreshold() {
+ return getCcdSquareMotionThreshold(objectId);
+ }
+
+ private native float getCcdSquareMotionThreshold(long objectId);
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
+ capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f());
+ capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
+ capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ buildObject();
+ setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
+ setPhysicsRotation(((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f())));
+ setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
+ setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java
new file mode 100644
index 0000000..0d41798
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.joints.PhysicsJoint;
+import com.jme3.bullet.objects.infos.RigidBodyMotionState;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>PhysicsRigidBody - Basic physics object</p>
+ * @author normenhansen
+ */
+public class PhysicsRigidBody extends PhysicsCollisionObject {
+
+ protected RigidBodyMotionState motionState = new RigidBodyMotionState();
+ protected float mass = 1.0f;
+ protected boolean kinematic = false;
+ protected ArrayList<PhysicsJoint> joints = new ArrayList<PhysicsJoint>();
+
+ public PhysicsRigidBody() {
+ }
+
+ /**
+ * Creates a new PhysicsRigidBody with the supplied collision shape
+ * @param child
+ * @param shape
+ */
+ public PhysicsRigidBody(CollisionShape shape) {
+ collisionShape = shape;
+ rebuildRigidBody();
+ }
+
+ public PhysicsRigidBody(CollisionShape shape, float mass) {
+ collisionShape = shape;
+ this.mass = mass;
+ rebuildRigidBody();
+ }
+
+ /**
+ * Builds/rebuilds the phyiscs body when parameters have changed
+ */
+ protected void rebuildRigidBody() {
+ boolean removed = false;
+ if (collisionShape instanceof MeshCollisionShape && mass != 0) {
+ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!");
+ }
+ if (objectId != 0) {
+ if (isInWorld(objectId)) {
+ PhysicsSpace.getPhysicsSpace().remove(this);
+ removed = true;
+ }
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing RigidBody {0}", Long.toHexString(objectId));
+ finalizeNative(objectId);
+ }
+ preRebuild();
+ objectId = createRigidBody(mass, motionState.getObjectId(), collisionShape.getObjectId());
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created RigidBody {0}", Long.toHexString(objectId));
+ postRebuild();
+ if (removed) {
+ PhysicsSpace.getPhysicsSpace().add(this);
+ }
+ }
+
+ protected void preRebuild() {
+ }
+
+ private native long createRigidBody(float mass, long motionStateId, long collisionShapeId);
+
+ protected void postRebuild() {
+ if (mass == 0.0f) {
+ setStatic(objectId, true);
+ } else {
+ setStatic(objectId, false);
+ }
+ initUserPointer();
+ }
+
+ /**
+ * @return the motionState
+ */
+ public RigidBodyMotionState getMotionState() {
+ return motionState;
+ }
+
+ public boolean isInWorld() {
+ return isInWorld(objectId);
+ }
+
+ private native boolean isInWorld(long objectId);
+
+ /**
+ * Sets the physics object location
+ * @param location the location of the actual physics object
+ */
+ public void setPhysicsLocation(Vector3f location) {
+ setPhysicsLocation(objectId, location);
+ }
+
+ private native void setPhysicsLocation(long objectId, Vector3f location);
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Matrix3f rotation) {
+ setPhysicsRotation(objectId, rotation);
+ }
+
+ private native void setPhysicsRotation(long objectId, Matrix3f rotation);
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Quaternion rotation) {
+ setPhysicsRotation(objectId, rotation);
+ }
+
+ private native void setPhysicsRotation(long objectId, Quaternion rotation);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation(Vector3f trans) {
+ if (trans == null) {
+ trans = new Vector3f();
+ }
+ getPhysicsLocation(objectId, trans);
+ return trans;
+ }
+
+ private native void getPhysicsLocation(long objectId, Vector3f vector);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Quaternion getPhysicsRotation(Quaternion rot) {
+ if (rot == null) {
+ rot = new Quaternion();
+ }
+ getPhysicsRotation(objectId, rot);
+ return rot;
+ }
+
+ private native void getPhysicsRotation(long objectId, Quaternion rot);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) {
+ if (rot == null) {
+ rot = new Matrix3f();
+ }
+ getPhysicsRotationMatrix(objectId, rot);
+ return rot;
+ }
+
+ private native void getPhysicsRotationMatrix(long objectId, Matrix3f rot);
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation() {
+ Vector3f vec = new Vector3f();
+ getPhysicsLocation(objectId, vec);
+ return vec;
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Quaternion getPhysicsRotation() {
+ Quaternion quat = new Quaternion();
+ getPhysicsRotation(objectId, quat);
+ return quat;
+ }
+
+ public Matrix3f getPhysicsRotationMatrix() {
+ Matrix3f mtx = new Matrix3f();
+ getPhysicsRotationMatrix(objectId, mtx);
+ return mtx;
+ }
+
+// /**
+// * Gets the physics object location
+// * @param location the location of the actual physics object is stored in this Vector3f
+// */
+// public Vector3f getInterpolatedPhysicsLocation(Vector3f location) {
+// if (location == null) {
+// location = new Vector3f();
+// }
+// rBody.getInterpolationWorldTransform(tempTrans);
+// return Converter.convert(tempTrans.origin, location);
+// }
+//
+// /**
+// * Gets the physics object rotation
+// * @param rotation the rotation of the actual physics object is stored in this Matrix3f
+// */
+// public Matrix3f getInterpolatedPhysicsRotation(Matrix3f rotation) {
+// if (rotation == null) {
+// rotation = new Matrix3f();
+// }
+// rBody.getInterpolationWorldTransform(tempTrans);
+// return Converter.convert(tempTrans.basis, rotation);
+// }
+ /**
+ * Sets the node to kinematic mode. in this mode the node is not affected by physics
+ * but affects other physics objects. Iits kinetic force is calculated by the amount
+ * of movement it is exposed to and its weight.
+ * @param kinematic
+ */
+ public void setKinematic(boolean kinematic) {
+ this.kinematic = kinematic;
+ setKinematic(objectId, kinematic);
+ }
+
+ private native void setKinematic(long objectId, boolean kinematic);
+
+ public boolean isKinematic() {
+ return kinematic;
+ }
+
+ public void setCcdSweptSphereRadius(float radius) {
+ setCcdSweptSphereRadius(objectId, radius);
+ }
+
+ private native void setCcdSweptSphereRadius(long objectId, float radius);
+
+ /**
+ * Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection<br/>
+ * This avoids the problem of fast objects moving through other objects, set to zero to disable (default)
+ * @param threshold
+ */
+ public void setCcdMotionThreshold(float threshold) {
+ setCcdMotionThreshold(objectId, threshold);
+ }
+
+ private native void setCcdMotionThreshold(long objectId, float threshold);
+
+ public float getCcdSweptSphereRadius() {
+ return getCcdSweptSphereRadius(objectId);
+ }
+
+ private native float getCcdSweptSphereRadius(long objectId);
+
+ public float getCcdMotionThreshold() {
+ return getCcdMotionThreshold(objectId);
+ }
+
+ private native float getCcdMotionThreshold(long objectId);
+
+ public float getCcdSquareMotionThreshold() {
+ return getCcdSquareMotionThreshold(objectId);
+ }
+
+ private native float getCcdSquareMotionThreshold(long objectId);
+
+ public float getMass() {
+ return mass;
+ }
+
+ /**
+ * Sets the mass of this PhysicsRigidBody, objects with mass=0 are static.
+ * @param mass
+ */
+ public void setMass(float mass) {
+ this.mass = mass;
+ if (collisionShape instanceof MeshCollisionShape && mass != 0) {
+ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!");
+ }
+ if (objectId != 0) {
+ if (collisionShape != null) {
+ updateMassProps(objectId, collisionShape.getObjectId(), mass);
+ }
+ if (mass == 0.0f) {
+ setStatic(objectId, true);
+ } else {
+ setStatic(objectId, false);
+ }
+ }
+ }
+
+ private native void setStatic(long objectId, boolean state);
+
+ private native long updateMassProps(long objectId, long collisionShapeId, float mass);
+
+ public Vector3f getGravity() {
+ return getGravity(null);
+ }
+
+ public Vector3f getGravity(Vector3f gravity) {
+ if (gravity == null) {
+ gravity = new Vector3f();
+ }
+ getGravity(objectId, gravity);
+ return gravity;
+ }
+
+ private native void getGravity(long objectId, Vector3f gravity);
+
+ /**
+ * Set the local gravity of this PhysicsRigidBody<br/>
+ * Set this after adding the node to the PhysicsSpace,
+ * the PhysicsSpace assigns its current gravity to the physics node when its added.
+ * @param gravity the gravity vector to set
+ */
+ public void setGravity(Vector3f gravity) {
+ setGravity(objectId, gravity);
+ }
+
+ private native void setGravity(long objectId, Vector3f gravity);
+
+ public float getFriction() {
+ return getFriction(objectId);
+ }
+
+ private native float getFriction(long objectId);
+
+ /**
+ * Sets the friction of this physics object
+ * @param friction the friction of this physics object
+ */
+ public void setFriction(float friction) {
+ setFriction(objectId, friction);
+ }
+
+ private native void setFriction(long objectId, float friction);
+
+ public void setDamping(float linearDamping, float angularDamping) {
+ setDamping(objectId, linearDamping, angularDamping);
+ }
+
+ private native void setDamping(long objectId, float linearDamping, float angularDamping);
+
+// private native void setRestitution(long objectId, float factor);
+//
+// public void setLinearDamping(float linearDamping) {
+// constructionInfo.linearDamping = linearDamping;
+// rBody.setDamping(linearDamping, constructionInfo.angularDamping);
+// }
+//
+// private native void setRestitution(long objectId, float factor);
+//
+ public void setLinearDamping(float linearDamping) {
+ setDamping(objectId, linearDamping, getAngularDamping());
+ }
+
+ public void setAngularDamping(float angularDamping) {
+ setAngularDamping(objectId, angularDamping);
+ }
+ private native void setAngularDamping(long objectId, float factor);
+
+ public float getLinearDamping() {
+ return getLinearDamping(objectId);
+ }
+
+ private native float getLinearDamping(long objectId);
+
+ public float getAngularDamping() {
+ return getAngularDamping(objectId);
+ }
+
+ private native float getAngularDamping(long objectId);
+
+ public float getRestitution() {
+ return getRestitution(objectId);
+ }
+
+ private native float getRestitution(long objectId);
+
+ /**
+ * The "bouncyness" of the PhysicsRigidBody, best performance if restitution=0
+ * @param restitution
+ */
+ public void setRestitution(float restitution) {
+ setRestitution(objectId, restitution);
+ }
+
+ private native void setRestitution(long objectId, float factor);
+
+ /**
+ * Get the current angular velocity of this PhysicsRigidBody
+ * @return the current linear velocity
+ */
+ public Vector3f getAngularVelocity() {
+ Vector3f vec = new Vector3f();
+ getAngularVelocity(objectId, vec);
+ return vec;
+ }
+
+ private native void getAngularVelocity(long objectId, Vector3f vec);
+
+ /**
+ * Get the current angular velocity of this PhysicsRigidBody
+ * @param vec the vector to store the velocity in
+ */
+ public void getAngularVelocity(Vector3f vec) {
+ getAngularVelocity(objectId, vec);
+ }
+
+ /**
+ * Sets the angular velocity of this PhysicsRigidBody
+ * @param vec the angular velocity of this PhysicsRigidBody
+ */
+ public void setAngularVelocity(Vector3f vec) {
+ setAngularVelocity(objectId, vec);
+ activate();
+ }
+
+ private native void setAngularVelocity(long objectId, Vector3f vec);
+
+ /**
+ * Get the current linear velocity of this PhysicsRigidBody
+ * @return the current linear velocity
+ */
+ public Vector3f getLinearVelocity() {
+ Vector3f vec = new Vector3f();
+ getLinearVelocity(objectId, vec);
+ return vec;
+ }
+
+ private native void getLinearVelocity(long objectId, Vector3f vec);
+
+ /**
+ * Get the current linear velocity of this PhysicsRigidBody
+ * @param vec the vector to store the velocity in
+ */
+ public void getLinearVelocity(Vector3f vec) {
+ getLinearVelocity(objectId, vec);
+ }
+
+ /**
+ * Sets the linear velocity of this PhysicsRigidBody
+ * @param vec the linear velocity of this PhysicsRigidBody
+ */
+ public void setLinearVelocity(Vector3f vec) {
+ setLinearVelocity(objectId, vec);
+ activate();
+ }
+
+ private native void setLinearVelocity(long objectId, Vector3f vec);
+
+ /**
+ * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call
+ * updates the physics space.<br>
+ * To apply an impulse, use applyImpulse, use applyContinuousForce to apply continuous force.
+ * @param force the force
+ * @param location the location of the force
+ */
+ public void applyForce(Vector3f force, Vector3f location) {
+ applyForce(objectId, force, location);
+ activate();
+ }
+
+ private native void applyForce(long objectId, Vector3f force, Vector3f location);
+
+ /**
+ * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call
+ * updates the physics space.<br>
+ * To apply an impulse, use applyImpulse.
+ *
+ * @param force the force
+ */
+ public void applyCentralForce(Vector3f force) {
+ applyCentralForce(objectId, force);
+ activate();
+ }
+
+ private native void applyCentralForce(long objectId, Vector3f force);
+
+ /**
+ * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call
+ * updates the physics space.<br>
+ * To apply an impulse, use applyImpulse.
+ *
+ * @param torque the torque
+ */
+ public void applyTorque(Vector3f torque) {
+ applyTorque(objectId, torque);
+ activate();
+ }
+
+ private native void applyTorque(long objectId, Vector3f vec);
+
+ /**
+ * Apply an impulse to the PhysicsRigidBody in the next physics update.
+ * @param impulse applied impulse
+ * @param rel_pos location relative to object
+ */
+ public void applyImpulse(Vector3f impulse, Vector3f rel_pos) {
+ applyImpulse(objectId, impulse, rel_pos);
+ activate();
+ }
+
+ private native void applyImpulse(long objectId, Vector3f impulse, Vector3f rel_pos);
+
+ /**
+ * Apply a torque impulse to the PhysicsRigidBody in the next physics update.
+ * @param vec
+ */
+ public void applyTorqueImpulse(Vector3f vec) {
+ applyTorqueImpulse(objectId, vec);
+ activate();
+ }
+
+ private native void applyTorqueImpulse(long objectId, Vector3f vec);
+
+ /**
+ * Clear all forces from the PhysicsRigidBody
+ *
+ */
+ public void clearForces() {
+ clearForces(objectId);
+ }
+
+ private native void clearForces(long objectId);
+
+ public void setCollisionShape(CollisionShape collisionShape) {
+ super.setCollisionShape(collisionShape);
+ if (collisionShape instanceof MeshCollisionShape && mass != 0) {
+ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!");
+ }
+ if (objectId == 0) {
+ rebuildRigidBody();
+ } else {
+ setCollisionShape(objectId, collisionShape.getObjectId());
+ updateMassProps(objectId, collisionShape.getObjectId(), mass);
+ }
+ }
+
+ private native void setCollisionShape(long objectId, long collisionShapeId);
+
+ /**
+ * reactivates this PhysicsRigidBody when it has been deactivated because it was not moving
+ */
+ public void activate() {
+ activate(objectId);
+ }
+
+ private native void activate(long objectId);
+
+ public boolean isActive() {
+ return isActive(objectId);
+ }
+
+ private native boolean isActive(long objectId);
+
+ /**
+ * sets the sleeping thresholds, these define when the object gets deactivated
+ * to save ressources. Low values keep the object active when it barely moves
+ * @param linear the linear sleeping threshold
+ * @param angular the angular sleeping threshold
+ */
+ public void setSleepingThresholds(float linear, float angular) {
+ setSleepingThresholds(objectId, linear, angular);
+ }
+
+ private native void setSleepingThresholds(long objectId, float linear, float angular);
+
+ public void setLinearSleepingThreshold(float linearSleepingThreshold) {
+ setLinearSleepingThreshold(objectId, linearSleepingThreshold);
+ }
+
+ private native void setLinearSleepingThreshold(long objectId, float linearSleepingThreshold);
+
+ public void setAngularSleepingThreshold(float angularSleepingThreshold) {
+ setAngularSleepingThreshold(objectId, angularSleepingThreshold);
+ }
+
+ private native void setAngularSleepingThreshold(long objectId, float angularSleepingThreshold);
+
+ public float getLinearSleepingThreshold() {
+ return getLinearSleepingThreshold(objectId);
+ }
+
+ private native float getLinearSleepingThreshold(long objectId);
+
+ public float getAngularSleepingThreshold() {
+ return getAngularSleepingThreshold(objectId);
+ }
+
+ private native float getAngularSleepingThreshold(long objectId);
+
+ public float getAngularFactor() {
+ return getAngularFactor(objectId);
+ }
+
+ private native float getAngularFactor(long objectId);
+
+ public void setAngularFactor(float factor) {
+ setAngularFactor(objectId, factor);
+ }
+
+ private native void setAngularFactor(long objectId, float factor);
+
+ /**
+ * do not use manually, joints are added automatically
+ */
+ public void addJoint(PhysicsJoint joint) {
+ if (!joints.contains(joint)) {
+ joints.add(joint);
+ }
+ updateDebugShape();
+ }
+
+ /**
+ *
+ */
+ public void removeJoint(PhysicsJoint joint) {
+ joints.remove(joint);
+ }
+
+ /**
+ * Returns a list of connected joints. This list is only filled when
+ * the PhysicsRigidBody is actually added to the physics space or loaded from disk.
+ * @return list of active joints connected to this PhysicsRigidBody
+ */
+ public List<PhysicsJoint> getJoints() {
+ return joints;
+ }
+
+ @Override
+ protected Spatial getDebugShape() {
+ //add joints
+ Spatial shape = super.getDebugShape();
+ Node node = null;
+ if (shape instanceof Node) {
+ node = (Node) shape;
+ } else {
+ node = new Node("DebugShapeNode");
+ node.attachChild(shape);
+ }
+ int i = 0;
+ for (Iterator<PhysicsJoint> it = joints.iterator(); it.hasNext();) {
+ PhysicsJoint physicsJoint = it.next();
+ Vector3f pivot = null;
+ if (physicsJoint.getBodyA() == this) {
+ pivot = physicsJoint.getPivotA();
+ } else {
+ pivot = physicsJoint.getPivotB();
+ }
+ Arrow arrow = new Arrow(pivot);
+ Geometry geom = new Geometry("DebugBone" + i, arrow);
+ geom.setMaterial(debugMaterialGreen);
+ node.attachChild(geom);
+ i++;
+ }
+ return node;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+
+ capsule.write(getMass(), "mass", 1.0f);
+
+ capsule.write(getGravity(), "gravity", Vector3f.ZERO);
+ capsule.write(getFriction(), "friction", 0.5f);
+ capsule.write(getRestitution(), "restitution", 0);
+ capsule.write(getAngularFactor(), "angularFactor", 1);
+ capsule.write(kinematic, "kinematic", false);
+
+ capsule.write(getLinearDamping(), "linearDamping", 0);
+ capsule.write(getAngularDamping(), "angularDamping", 0);
+ capsule.write(getLinearSleepingThreshold(), "linearSleepingThreshold", 0.8f);
+ capsule.write(getAngularSleepingThreshold(), "angularSleepingThreshold", 1.0f);
+
+ capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
+ capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
+
+ capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
+ capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f());
+
+ capsule.writeSavableArrayList(joints, "joints", null);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+
+ InputCapsule capsule = e.getCapsule(this);
+ float mass = capsule.readFloat("mass", 1.0f);
+ this.mass = mass;
+ rebuildRigidBody();
+ setGravity((Vector3f) capsule.readSavable("gravity", Vector3f.ZERO.clone()));
+ setFriction(capsule.readFloat("friction", 0.5f));
+ setKinematic(capsule.readBoolean("kinematic", false));
+
+ setRestitution(capsule.readFloat("restitution", 0));
+ setAngularFactor(capsule.readFloat("angularFactor", 1));
+ setDamping(capsule.readFloat("linearDamping", 0), capsule.readFloat("angularDamping", 0));
+ setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f));
+ setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
+ setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
+
+ setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
+ setPhysicsRotation((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f()));
+
+ joints = capsule.readSavableArrayList("joints", null);
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java
new file mode 100644
index 0000000..fba1e83
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.objects.infos.VehicleTuning;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>PhysicsVehicleNode - Special PhysicsNode that implements vehicle functions</p>
+ * <p>
+ * <i>From bullet manual:</i><br>
+ * For most vehicle simulations, it is recommended to use the simplified Bullet
+ * vehicle model as provided in btRaycastVehicle. Instead of simulation each wheel
+ * and chassis as separate rigid bodies, connected by constraints, it uses a simplified model.
+ * This simplified model has many benefits, and is widely used in commercial driving games.<br>
+ * The entire vehicle is represented as a single rigidbody, the chassis.
+ * The collision detection of the wheels is approximated by ray casts,
+ * and the tire friction is a basic anisotropic friction model.
+ * </p>
+ * @author normenhansen
+ */
+public class PhysicsVehicle extends PhysicsRigidBody {
+
+ protected long vehicleId = 0;
+ protected long rayCasterId = 0;
+ protected VehicleTuning tuning = new VehicleTuning();
+ protected ArrayList<VehicleWheel> wheels = new ArrayList<VehicleWheel>();
+ protected PhysicsSpace physicsSpace;
+
+ public PhysicsVehicle() {
+ }
+
+ public PhysicsVehicle(CollisionShape shape) {
+ super(shape);
+ }
+
+ public PhysicsVehicle(CollisionShape shape, float mass) {
+ super(shape, mass);
+ }
+
+ /**
+ * used internally
+ */
+ public void updateWheels() {
+ if (vehicleId != 0) {
+ for (int i = 0; i < wheels.size(); i++) {
+ updateWheelTransform(vehicleId, i, true);
+ wheels.get(i).updatePhysicsState();
+ }
+ }
+ }
+
+ private native void updateWheelTransform(long vehicleId, int wheel, boolean interpolated);
+
+ /**
+ * used internally
+ */
+ public void applyWheelTransforms() {
+ if (wheels != null) {
+ for (int i = 0; i < wheels.size(); i++) {
+ wheels.get(i).applyWheelTransform();
+ }
+ }
+ }
+
+ @Override
+ protected void postRebuild() {
+ super.postRebuild();
+ motionState.setVehicle(this);
+ createVehicle(physicsSpace);
+ }
+
+ /**
+ * Used internally, creates the actual vehicle constraint when vehicle is added to phyicsspace
+ */
+ public void createVehicle(PhysicsSpace space) {
+ physicsSpace = space;
+ if (space == null) {
+ return;
+ }
+ if (space.getSpaceId() == 0) {
+ throw new IllegalStateException("Physics space is not initialized!");
+ }
+ if (rayCasterId != 0) {
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing RayCaster {0}", Long.toHexString(rayCasterId));
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing Vehicle {0}", Long.toHexString(vehicleId));
+ finalizeNative(rayCasterId, vehicleId);
+ }
+ rayCasterId = createVehicleRaycaster(objectId, space.getSpaceId());
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created RayCaster {0}", Long.toHexString(rayCasterId));
+ vehicleId = createRaycastVehicle(objectId, rayCasterId);
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Vehicle {0}", Long.toHexString(vehicleId));
+ setCoordinateSystem(vehicleId, 0, 1, 2);
+ for (VehicleWheel wheel : wheels) {
+ wheel.setVehicleId(vehicleId, addWheel(vehicleId, wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel()));
+ }
+ }
+
+ private native long createVehicleRaycaster(long objectId, long physicsSpaceId);
+
+ private native long createRaycastVehicle(long objectId, long rayCasterId);
+
+ private native void setCoordinateSystem(long objectId, int a, int b, int c);
+
+ private native int addWheel(long objectId, Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, VehicleTuning tuning, boolean frontWheel);
+
+ /**
+ * Add a wheel to this vehicle
+ * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space)
+ * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car)
+ * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car)
+ * @param suspensionRestLength The current length of the suspension (metres)
+ * @param wheelRadius the wheel radius
+ * @param isFrontWheel sets if this wheel is a front wheel (steering)
+ * @return the PhysicsVehicleWheel object to get/set infos on the wheel
+ */
+ public VehicleWheel addWheel(Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) {
+ return addWheel(null, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel);
+ }
+
+ /**
+ * Add a wheel to this vehicle
+ * @param spat the wheel Geometry
+ * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space)
+ * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car)
+ * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car)
+ * @param suspensionRestLength The current length of the suspension (metres)
+ * @param wheelRadius the wheel radius
+ * @param isFrontWheel sets if this wheel is a front wheel (steering)
+ * @return the PhysicsVehicleWheel object to get/set infos on the wheel
+ */
+ public VehicleWheel addWheel(Spatial spat, Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) {
+ VehicleWheel wheel = null;
+ if (spat == null) {
+ wheel = new VehicleWheel(connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel);
+ } else {
+ wheel = new VehicleWheel(spat, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel);
+ }
+ wheel.setFrictionSlip(tuning.frictionSlip);
+ wheel.setMaxSuspensionTravelCm(tuning.maxSuspensionTravelCm);
+ wheel.setSuspensionStiffness(tuning.suspensionStiffness);
+ wheel.setWheelsDampingCompression(tuning.suspensionCompression);
+ wheel.setWheelsDampingRelaxation(tuning.suspensionDamping);
+ wheel.setMaxSuspensionForce(tuning.maxSuspensionForce);
+ wheels.add(wheel);
+ if (vehicleId != 0) {
+ wheel.setVehicleId(vehicleId, addWheel(vehicleId, wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel()));
+ }
+ if (debugShape != null) {
+ updateDebugShape();
+ }
+ return wheel;
+ }
+
+ /**
+ * This rebuilds the vehicle as there is no way in bullet to remove a wheel.
+ * @param wheel
+ */
+ public void removeWheel(int wheel) {
+ wheels.remove(wheel);
+ rebuildRigidBody();
+// updateDebugShape();
+ }
+
+ /**
+ * You can get access to the single wheels via this method.
+ * @param wheel the wheel index
+ * @return the WheelInfo of the selected wheel
+ */
+ public VehicleWheel getWheel(int wheel) {
+ return wheels.get(wheel);
+ }
+
+ public int getNumWheels() {
+ return wheels.size();
+ }
+
+ /**
+ * @return the frictionSlip
+ */
+ public float getFrictionSlip() {
+ return tuning.frictionSlip;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The coefficient of friction between the tyre and the ground.
+ * Should be about 0.8 for realistic cars, but can increased for better handling.
+ * Set large (10000.0) for kart racers
+ * @param frictionSlip the frictionSlip to set
+ */
+ public void setFrictionSlip(float frictionSlip) {
+ tuning.frictionSlip = frictionSlip;
+ }
+
+ /**
+ * The coefficient of friction between the tyre and the ground.
+ * Should be about 0.8 for realistic cars, but can increased for better handling.
+ * Set large (10000.0) for kart racers
+ * @param wheel
+ * @param frictionSlip
+ */
+ public void setFrictionSlip(int wheel, float frictionSlip) {
+ wheels.get(wheel).setFrictionSlip(frictionSlip);
+ }
+
+ /**
+ * Reduces the rolling torque applied from the wheels that cause the vehicle to roll over.
+ * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour.
+ * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over.
+ * You should also try lowering the vehicle's centre of mass
+ */
+ public void setRollInfluence(int wheel, float rollInfluence) {
+ wheels.get(wheel).setRollInfluence(rollInfluence);
+ }
+
+ /**
+ * @return the maxSuspensionTravelCm
+ */
+ public float getMaxSuspensionTravelCm() {
+ return tuning.maxSuspensionTravelCm;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The maximum distance the suspension can be compressed (centimetres)
+ * @param maxSuspensionTravelCm the maxSuspensionTravelCm to set
+ */
+ public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) {
+ tuning.maxSuspensionTravelCm = maxSuspensionTravelCm;
+ }
+
+ /**
+ * The maximum distance the suspension can be compressed (centimetres)
+ * @param wheel
+ * @param maxSuspensionTravelCm
+ */
+ public void setMaxSuspensionTravelCm(int wheel, float maxSuspensionTravelCm) {
+ wheels.get(wheel).setMaxSuspensionTravelCm(maxSuspensionTravelCm);
+ }
+
+ public float getMaxSuspensionForce() {
+ return tuning.maxSuspensionForce;
+ }
+
+ /**
+ * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
+ * handle the weight of your vehcile.
+ * @param maxSuspensionForce
+ */
+ public void setMaxSuspensionForce(float maxSuspensionForce) {
+ tuning.maxSuspensionForce = maxSuspensionForce;
+ }
+
+ /**
+ * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
+ * handle the weight of your vehcile.
+ * @param wheel
+ * @param maxSuspensionForce
+ */
+ public void setMaxSuspensionForce(int wheel, float maxSuspensionForce) {
+ wheels.get(wheel).setMaxSuspensionForce(maxSuspensionForce);
+ }
+
+ /**
+ * @return the suspensionCompression
+ */
+ public float getSuspensionCompression() {
+ return tuning.suspensionCompression;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The damping coefficient for when the suspension is compressed.
+ * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
+ * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
+ * 0.1 to 0.3 are good values
+ * @param suspensionCompression the suspensionCompression to set
+ */
+ public void setSuspensionCompression(float suspensionCompression) {
+ tuning.suspensionCompression = suspensionCompression;
+ }
+
+ /**
+ * The damping coefficient for when the suspension is compressed.
+ * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
+ * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
+ * 0.1 to 0.3 are good values
+ * @param wheel
+ * @param suspensionCompression
+ */
+ public void setSuspensionCompression(int wheel, float suspensionCompression) {
+ wheels.get(wheel).setWheelsDampingCompression(suspensionCompression);
+ }
+
+ /**
+ * @return the suspensionDamping
+ */
+ public float getSuspensionDamping() {
+ return tuning.suspensionDamping;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The damping coefficient for when the suspension is expanding.
+ * See the comments for setSuspensionCompression for how to set k.
+ * @param suspensionDamping the suspensionDamping to set
+ */
+ public void setSuspensionDamping(float suspensionDamping) {
+ tuning.suspensionDamping = suspensionDamping;
+ }
+
+ /**
+ * The damping coefficient for when the suspension is expanding.
+ * See the comments for setSuspensionCompression for how to set k.
+ * @param wheel
+ * @param suspensionDamping
+ */
+ public void setSuspensionDamping(int wheel, float suspensionDamping) {
+ wheels.get(wheel).setWheelsDampingRelaxation(suspensionDamping);
+ }
+
+ /**
+ * @return the suspensionStiffness
+ */
+ public float getSuspensionStiffness() {
+ return tuning.suspensionStiffness;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
+ * @param suspensionStiffness
+ */
+ public void setSuspensionStiffness(float suspensionStiffness) {
+ tuning.suspensionStiffness = suspensionStiffness;
+ }
+
+ /**
+ * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
+ * @param wheel
+ * @param suspensionStiffness
+ */
+ public void setSuspensionStiffness(int wheel, float suspensionStiffness) {
+ wheels.get(wheel).setSuspensionStiffness(suspensionStiffness);
+ }
+
+ /**
+ * Reset the suspension
+ */
+ public void resetSuspension() {
+ resetSuspension(vehicleId);
+ }
+
+ private native void resetSuspension(long vehicleId);
+
+ /**
+ * Apply the given engine force to all wheels, works continuously
+ * @param force the force
+ */
+ public void accelerate(float force) {
+ for (int i = 0; i < wheels.size(); i++) {
+ accelerate(i, force);
+ }
+ }
+
+ /**
+ * Apply the given engine force, works continuously
+ * @param wheel the wheel to apply the force on
+ * @param force the force
+ */
+ public void accelerate(int wheel, float force) {
+ applyEngineForce(vehicleId, wheel, force);
+
+ }
+
+ private native void applyEngineForce(long vehicleId, int wheel, float force);
+
+ /**
+ * Set the given steering value to all front wheels (0 = forward)
+ * @param value the steering angle of the front wheels (Pi = 360deg)
+ */
+ public void steer(float value) {
+ for (int i = 0; i < wheels.size(); i++) {
+ if (getWheel(i).isFrontWheel()) {
+ steer(i, value);
+ }
+ }
+ }
+
+ /**
+ * Set the given steering value to the given wheel (0 = forward)
+ * @param wheel the wheel to set the steering on
+ * @param value the steering angle of the front wheels (Pi = 360deg)
+ */
+ public void steer(int wheel, float value) {
+ steer(vehicleId, wheel, value);
+ }
+
+ private native void steer(long vehicleId, int wheel, float value);
+
+ /**
+ * Apply the given brake force to all wheels, works continuously
+ * @param force the force
+ */
+ public void brake(float force) {
+ for (int i = 0; i < wheels.size(); i++) {
+ brake(i, force);
+ }
+ }
+
+ /**
+ * Apply the given brake force, works continuously
+ * @param wheel the wheel to apply the force on
+ * @param force the force
+ */
+ public void brake(int wheel, float force) {
+ brake(vehicleId, wheel, force);
+ }
+
+ private native void brake(long vehicleId, int wheel, float force);
+
+ /**
+ * Get the current speed of the vehicle in km/h
+ * @return
+ */
+ public float getCurrentVehicleSpeedKmHour() {
+ return getCurrentVehicleSpeedKmHour(vehicleId);
+ }
+
+ private native float getCurrentVehicleSpeedKmHour(long vehicleId);
+
+ /**
+ * Get the current forward vector of the vehicle in world coordinates
+ * @param vector
+ * @return
+ */
+ public Vector3f getForwardVector(Vector3f vector) {
+ if (vector == null) {
+ vector = new Vector3f();
+ }
+ getForwardVector(vehicleId, vector);
+ return vector;
+ }
+
+ private native void getForwardVector(long objectId, Vector3f vector);
+
+ /**
+ * used internally
+ */
+ public long getVehicleId() {
+ return vehicleId;
+ }
+
+ @Override
+ protected Spatial getDebugShape() {
+ Spatial shape = super.getDebugShape();
+ Node node = null;
+ if (shape instanceof Node) {
+ node = (Node) shape;
+ } else {
+ node = new Node("DebugShapeNode");
+ node.attachChild(shape);
+ }
+ int i = 0;
+ for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
+ VehicleWheel physicsVehicleWheel = it.next();
+ Vector3f location = physicsVehicleWheel.getLocation().clone();
+ Vector3f direction = physicsVehicleWheel.getDirection().clone();
+ Vector3f axle = physicsVehicleWheel.getAxle().clone();
+ float restLength = physicsVehicleWheel.getRestLength();
+ float radius = physicsVehicleWheel.getRadius();
+
+ Arrow locArrow = new Arrow(location);
+ Arrow axleArrow = new Arrow(axle.normalizeLocal().multLocal(0.3f));
+ Arrow wheelArrow = new Arrow(direction.normalizeLocal().multLocal(radius));
+ Arrow dirArrow = new Arrow(direction.normalizeLocal().multLocal(restLength));
+ Geometry locGeom = new Geometry("WheelLocationDebugShape" + i, locArrow);
+ Geometry dirGeom = new Geometry("WheelDirectionDebugShape" + i, dirArrow);
+ Geometry axleGeom = new Geometry("WheelAxleDebugShape" + i, axleArrow);
+ Geometry wheelGeom = new Geometry("WheelRadiusDebugShape" + i, wheelArrow);
+ dirGeom.setLocalTranslation(location);
+ axleGeom.setLocalTranslation(location.add(direction));
+ wheelGeom.setLocalTranslation(location.add(direction));
+ locGeom.setMaterial(debugMaterialGreen);
+ dirGeom.setMaterial(debugMaterialGreen);
+ axleGeom.setMaterial(debugMaterialGreen);
+ wheelGeom.setMaterial(debugMaterialGreen);
+ node.attachChild(locGeom);
+ node.attachChild(dirGeom);
+ node.attachChild(axleGeom);
+ node.attachChild(wheelGeom);
+ i++;
+ }
+ return node;
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ tuning = new VehicleTuning();
+ tuning.frictionSlip = capsule.readFloat("frictionSlip", 10.5f);
+ tuning.maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f);
+ tuning.maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f);
+ tuning.suspensionCompression = capsule.readFloat("suspensionCompression", 0.83f);
+ tuning.suspensionDamping = capsule.readFloat("suspensionDamping", 0.88f);
+ tuning.suspensionStiffness = capsule.readFloat("suspensionStiffness", 5.88f);
+ wheels = capsule.readSavableArrayList("wheelsList", new ArrayList<VehicleWheel>());
+ motionState.setVehicle(this);
+ super.read(im);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(tuning.frictionSlip, "frictionSlip", 10.5f);
+ capsule.write(tuning.maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f);
+ capsule.write(tuning.maxSuspensionForce, "maxSuspensionForce", 6000f);
+ capsule.write(tuning.suspensionCompression, "suspensionCompression", 0.83f);
+ capsule.write(tuning.suspensionDamping, "suspensionDamping", 0.88f);
+ capsule.write(tuning.suspensionStiffness, "suspensionStiffness", 5.88f);
+ capsule.writeSavableArrayList(wheels, "wheelsList", new ArrayList<VehicleWheel>());
+ super.write(ex);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing RayCaster {0}", Long.toHexString(rayCasterId));
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Vehicle {0}", Long.toHexString(vehicleId));
+ finalizeNative(rayCasterId, vehicleId);
+ }
+
+ private native void finalizeNative(long rayCaster, long vehicle);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java b/engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java
new file mode 100644
index 0000000..11eab60
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.export.*;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * Stores info about one wheel of a PhysicsVehicle
+ * @author normenhansen
+ */
+public class VehicleWheel implements Savable {
+
+ protected long wheelId = 0;
+ protected int wheelIndex = 0;
+ protected boolean frontWheel;
+ protected Vector3f location = new Vector3f();
+ protected Vector3f direction = new Vector3f();
+ protected Vector3f axle = new Vector3f();
+ protected float suspensionStiffness = 20.0f;
+ protected float wheelsDampingRelaxation = 2.3f;
+ protected float wheelsDampingCompression = 4.4f;
+ protected float frictionSlip = 10.5f;
+ protected float rollInfluence = 1.0f;
+ protected float maxSuspensionTravelCm = 500f;
+ protected float maxSuspensionForce = 6000f;
+ protected float radius = 0.5f;
+ protected float restLength = 1f;
+ protected Vector3f wheelWorldLocation = new Vector3f();
+ protected Quaternion wheelWorldRotation = new Quaternion();
+ protected Spatial wheelSpatial;
+ protected Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f();
+ protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
+ private boolean applyLocal = false;
+
+ public VehicleWheel() {
+ }
+
+ public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle,
+ float restLength, float radius, boolean frontWheel) {
+ this(location, direction, axle, restLength, radius, frontWheel);
+ wheelSpatial = spat;
+ }
+
+ public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle,
+ float restLength, float radius, boolean frontWheel) {
+ this.location.set(location);
+ this.direction.set(direction);
+ this.axle.set(axle);
+ this.frontWheel = frontWheel;
+ this.restLength = restLength;
+ this.radius = radius;
+ }
+
+ public synchronized void updatePhysicsState() {
+ getWheelLocation(wheelId, wheelIndex, wheelWorldLocation);
+ getWheelRotation(wheelId, wheelIndex, tmp_Matrix);
+ wheelWorldRotation.fromRotationMatrix(tmp_Matrix);
+ }
+
+ private native void getWheelLocation(long vehicleId, int wheelId, Vector3f location);
+
+ private native void getWheelRotation(long vehicleId, int wheelId, Matrix3f location);
+
+ public synchronized void applyWheelTransform() {
+ if (wheelSpatial == null) {
+ return;
+ }
+ Quaternion localRotationQuat = wheelSpatial.getLocalRotation();
+ Vector3f localLocation = wheelSpatial.getLocalTranslation();
+ if (!applyLocal && wheelSpatial.getParent() != null) {
+ localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation());
+ localLocation.divideLocal(wheelSpatial.getParent().getWorldScale());
+ tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
+
+ localRotationQuat.set(wheelWorldRotation);
+ tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat);
+
+ wheelSpatial.setLocalTranslation(localLocation);
+ wheelSpatial.setLocalRotation(localRotationQuat);
+ } else {
+ wheelSpatial.setLocalTranslation(wheelWorldLocation);
+ wheelSpatial.setLocalRotation(wheelWorldRotation);
+ }
+ }
+
+ public long getWheelId() {
+ return wheelId;
+ }
+
+ public void setVehicleId(long vehicleId, int wheelIndex) {
+ this.wheelId = vehicleId;
+ this.wheelIndex = wheelIndex;
+ applyInfo();
+ }
+
+ public boolean isFrontWheel() {
+ return frontWheel;
+ }
+
+ public void setFrontWheel(boolean frontWheel) {
+ this.frontWheel = frontWheel;
+ applyInfo();
+ }
+
+ public Vector3f getLocation() {
+ return location;
+ }
+
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ public Vector3f getAxle() {
+ return axle;
+ }
+
+ public float getSuspensionStiffness() {
+ return suspensionStiffness;
+ }
+
+ /**
+ * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
+ * @param suspensionStiffness
+ */
+ public void setSuspensionStiffness(float suspensionStiffness) {
+ this.suspensionStiffness = suspensionStiffness;
+ applyInfo();
+ }
+
+ public float getWheelsDampingRelaxation() {
+ return wheelsDampingRelaxation;
+ }
+
+ /**
+ * the damping coefficient for when the suspension is expanding.
+ * See the comments for setWheelsDampingCompression for how to set k.
+ * @param wheelsDampingRelaxation
+ */
+ public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) {
+ this.wheelsDampingRelaxation = wheelsDampingRelaxation;
+ applyInfo();
+ }
+
+ public float getWheelsDampingCompression() {
+ return wheelsDampingCompression;
+ }
+
+ /**
+ * the damping coefficient for when the suspension is compressed.
+ * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
+ * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
+ * 0.1 to 0.3 are good values
+ * @param wheelsDampingCompression
+ */
+ public void setWheelsDampingCompression(float wheelsDampingCompression) {
+ this.wheelsDampingCompression = wheelsDampingCompression;
+ applyInfo();
+ }
+
+ public float getFrictionSlip() {
+ return frictionSlip;
+ }
+
+ /**
+ * the coefficient of friction between the tyre and the ground.
+ * Should be about 0.8 for realistic cars, but can increased for better handling.
+ * Set large (10000.0) for kart racers
+ * @param frictionSlip
+ */
+ public void setFrictionSlip(float frictionSlip) {
+ this.frictionSlip = frictionSlip;
+ applyInfo();
+ }
+
+ public float getRollInfluence() {
+ return rollInfluence;
+ }
+
+ /**
+ * reduces the rolling torque applied from the wheels that cause the vehicle to roll over.
+ * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour.
+ * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over.
+ * You should also try lowering the vehicle's centre of mass
+ * @param rollInfluence the rollInfluence to set
+ */
+ public void setRollInfluence(float rollInfluence) {
+ this.rollInfluence = rollInfluence;
+ applyInfo();
+ }
+
+ public float getMaxSuspensionTravelCm() {
+ return maxSuspensionTravelCm;
+ }
+
+ /**
+ * the maximum distance the suspension can be compressed (centimetres)
+ * @param maxSuspensionTravelCm
+ */
+ public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) {
+ this.maxSuspensionTravelCm = maxSuspensionTravelCm;
+ applyInfo();
+ }
+
+ public float getMaxSuspensionForce() {
+ return maxSuspensionForce;
+ }
+
+ /**
+ * The maximum suspension force, raise this above the default 6000 if your suspension cannot
+ * handle the weight of your vehcile.
+ * @param maxSuspensionTravelCm
+ */
+ public void setMaxSuspensionForce(float maxSuspensionForce) {
+ this.maxSuspensionForce = maxSuspensionForce;
+ applyInfo();
+ }
+
+ private void applyInfo() {
+ if (wheelId == 0) {
+ return;
+ }
+ applyInfo(wheelId, wheelIndex, suspensionStiffness, wheelsDampingRelaxation, wheelsDampingCompression, frictionSlip, rollInfluence, maxSuspensionTravelCm, maxSuspensionForce, radius, frontWheel, restLength);
+ }
+
+ private native void applyInfo(long wheelId, int wheelIndex,
+ float suspensionStiffness,
+ float wheelsDampingRelaxation,
+ float wheelsDampingCompression,
+ float frictionSlip,
+ float rollInfluence,
+ float maxSuspensionTravelCm,
+ float maxSuspensionForce,
+ float wheelsRadius,
+ boolean frontWheel,
+ float suspensionRestLength);
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void setRadius(float radius) {
+ this.radius = radius;
+ applyInfo();
+ }
+
+ public float getRestLength() {
+ return restLength;
+ }
+
+ public void setRestLength(float restLength) {
+ this.restLength = restLength;
+ applyInfo();
+ }
+
+ /**
+ * returns the object this wheel is in contact with or null if no contact
+ * @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject)
+ */
+ public PhysicsCollisionObject getGroundObject() {
+// if (wheelInfo.raycastInfo.groundObject == null) {
+// return null;
+// } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) {
+// System.out.println("RigidBody");
+// return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer();
+// } else {
+ return null;
+// }
+ }
+
+ /**
+ * returns the location where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionLocation(Vector3f vec) {
+ getCollisionLocation(wheelId, wheelIndex, vec);
+ return vec;
+ }
+
+ private native void getCollisionLocation(long wheelId, int wheelIndex, Vector3f vec);
+
+ /**
+ * returns the location where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionLocation() {
+ Vector3f vec = new Vector3f();
+ getCollisionLocation(wheelId, wheelIndex, vec);
+ return vec;
+ }
+
+ /**
+ * returns the normal where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionNormal(Vector3f vec) {
+ getCollisionNormal(wheelId, wheelIndex, vec);
+ return vec;
+ }
+
+ private native void getCollisionNormal(long wheelId, int wheelIndex, Vector3f vec);
+
+ /**
+ * returns the normal where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionNormal() {
+ Vector3f vec = new Vector3f();
+ getCollisionNormal(wheelId, wheelIndex, vec);
+ return vec;
+ }
+
+ /**
+ * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)<br>
+ * 0.0 = wheels are sliding, 1.0 = wheels have traction.
+ */
+ public float getSkidInfo() {
+ return getSkidInfo(wheelId, wheelIndex);
+ }
+
+ public native float getSkidInfo(long wheelId, int wheelIndex);
+
+ /**
+ * returns how many degrees the wheel has turned since the last physics
+ * step.
+ */
+ public float getDeltaRotation() {
+ return getDeltaRotation(wheelId, wheelIndex);
+ }
+
+ public native float getDeltaRotation(long wheelId, int wheelIndex);
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null);
+ frontWheel = capsule.readBoolean("frontWheel", false);
+ location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f());
+ direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f());
+ axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f());
+ suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f);
+ wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f);
+ wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f);
+ frictionSlip = capsule.readFloat("frictionSlip", 10.5f);
+ rollInfluence = capsule.readFloat("rollInfluence", 1.0f);
+ maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f);
+ maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f);
+ radius = capsule.readFloat("wheelRadius", 0.5f);
+ restLength = capsule.readFloat("restLength", 1f);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(wheelSpatial, "wheelSpatial", null);
+ capsule.write(frontWheel, "frontWheel", false);
+ capsule.write(location, "wheelLocation", new Vector3f());
+ capsule.write(direction, "wheelDirection", new Vector3f());
+ capsule.write(axle, "wheelAxle", new Vector3f());
+ capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f);
+ capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f);
+ capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f);
+ capsule.write(frictionSlip, "frictionSlip", 10.5f);
+ capsule.write(rollInfluence, "rollInfluence", 1.0f);
+ capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f);
+ capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f);
+ capsule.write(radius, "wheelRadius", 0.5f);
+ capsule.write(restLength, "restLength", 1f);
+ }
+
+ /**
+ * @return the wheelSpatial
+ */
+ public Spatial getWheelSpatial() {
+ return wheelSpatial;
+ }
+
+ /**
+ * @param wheelSpatial the wheelSpatial to set
+ */
+ public void setWheelSpatial(Spatial wheelSpatial) {
+ this.wheelSpatial = wheelSpatial;
+ }
+
+ public boolean isApplyLocal() {
+ return applyLocal;
+ }
+
+ public void setApplyLocal(boolean applyLocal) {
+ this.applyLocal = applyLocal;
+ }
+
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java b/engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java
new file mode 100644
index 0000000..c60d6c5
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects.infos;
+
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * stores transform info of a PhysicsNode in a threadsafe manner to
+ * allow multithreaded access from the jme scenegraph and the bullet physicsspace
+ * @author normenhansen
+ */
+public class RigidBodyMotionState {
+ long motionStateId = 0;
+ private Vector3f worldLocation = new Vector3f();
+ private Matrix3f worldRotation = new Matrix3f();
+ private Quaternion worldRotationQuat = new Quaternion();
+ private Quaternion tmp_inverseWorldRotation = new Quaternion();
+ private PhysicsVehicle vehicle;
+ private boolean applyPhysicsLocal = false;
+// protected LinkedList<PhysicsMotionStateListener> listeners = new LinkedList<PhysicsMotionStateListener>();
+
+ public RigidBodyMotionState() {
+ this.motionStateId = createMotionState();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created MotionState {0}", Long.toHexString(motionStateId));
+ }
+
+ private native long createMotionState();
+
+ /**
+ * applies the current transform to the given jme Node if the location has been updated on the physics side
+ * @param spatial
+ */
+ public synchronized boolean applyTransform(Spatial spatial) {
+ Vector3f localLocation = spatial.getLocalTranslation();
+ Quaternion localRotationQuat = spatial.getLocalRotation();
+ boolean physicsLocationDirty = applyTransform(motionStateId, localLocation, localRotationQuat);
+ if (!physicsLocationDirty) {
+ return false;
+ }
+ if (!applyPhysicsLocal && spatial.getParent() != null) {
+ localLocation.subtractLocal(spatial.getParent().getWorldTranslation());
+ localLocation.divideLocal(spatial.getParent().getWorldScale());
+ tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
+
+// localRotationQuat.set(worldRotationQuat);
+ tmp_inverseWorldRotation.mult(localRotationQuat, localRotationQuat);
+
+ spatial.setLocalTranslation(localLocation);
+ spatial.setLocalRotation(localRotationQuat);
+ } else {
+ spatial.setLocalTranslation(localLocation);
+ spatial.setLocalRotation(localRotationQuat);
+// spatial.setLocalTranslation(worldLocation);
+// spatial.setLocalRotation(worldRotationQuat);
+ }
+ if (vehicle != null) {
+ vehicle.updateWheels();
+ }
+ return true;
+ }
+
+ private synchronized native boolean applyTransform(long stateId, Vector3f location, Quaternion rotation);
+
+ /**
+ * @return the worldLocation
+ */
+ public Vector3f getWorldLocation() {
+ getWorldLocation(motionStateId, worldLocation);
+ return worldLocation;
+ }
+
+ private native void getWorldLocation(long stateId, Vector3f vec);
+
+ /**
+ * @return the worldRotation
+ */
+ public Matrix3f getWorldRotation() {
+ getWorldRotation(motionStateId, worldRotation);
+ return worldRotation;
+ }
+
+ private native void getWorldRotation(long stateId, Matrix3f vec);
+
+ /**
+ * @return the worldRotationQuat
+ */
+ public Quaternion getWorldRotationQuat() {
+ getWorldRotationQuat(motionStateId, worldRotationQuat);
+ return worldRotationQuat;
+ }
+
+ private native void getWorldRotationQuat(long stateId, Quaternion vec);
+
+ /**
+ * @param vehicle the vehicle to set
+ */
+ public void setVehicle(PhysicsVehicle vehicle) {
+ this.vehicle = vehicle;
+ }
+
+ public boolean isApplyPhysicsLocal() {
+ return applyPhysicsLocal;
+ }
+
+ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
+ this.applyPhysicsLocal = applyPhysicsLocal;
+ }
+
+ public long getObjectId(){
+ return motionStateId;
+ }
+// public void addMotionStateListener(PhysicsMotionStateListener listener){
+// listeners.add(listener);
+// }
+//
+// public void removeMotionStateListener(PhysicsMotionStateListener listener){
+// listeners.remove(listener);
+// }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing MotionState {0}", Long.toHexString(motionStateId));
+ finalizeNative(motionStateId);
+ }
+
+ private native void finalizeNative(long objectId);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java b/engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java
new file mode 100644
index 0000000..892c9bd
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects.infos;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class VehicleTuning {
+ public float suspensionStiffness = 5.88f;
+ public float suspensionCompression = 0.83f;
+ public float suspensionDamping = 0.88f;
+ public float maxSuspensionTravelCm = 500f;
+ public float maxSuspensionForce = 6000f;
+ public float frictionSlip = 10.5f;
+}
diff --git a/engine/src/bullet/com/jme3/bullet/util/DebugMeshCallback.java b/engine/src/bullet/com/jme3/bullet/util/DebugMeshCallback.java
new file mode 100644
index 0000000..9960011
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/util/DebugMeshCallback.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.util;
+
+import com.jme3.math.Vector3f;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class DebugMeshCallback {
+
+ private ArrayList<Vector3f> list = new ArrayList<Vector3f>();
+
+ public void addVector(float x, float y, float z, int part, int index) {
+ list.add(new Vector3f(x, y, z));
+ }
+
+ public FloatBuffer getVertices() {
+ FloatBuffer buf = BufferUtils.createFloatBuffer(list.size() * 3);
+ for (int i = 0; i < list.size(); i++) {
+ Vector3f vector3f = list.get(i);
+ buf.put(vector3f.x);
+ buf.put(vector3f.y);
+ buf.put(vector3f.z);
+ }
+ return buf;
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/util/DebugShapeFactory.java b/engine/src/bullet/com/jme3/bullet/util/DebugShapeFactory.java
new file mode 100644
index 0000000..b03e185
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/util/DebugShapeFactory.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.util;
+
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
+import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
+import com.jme3.math.Matrix3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.TempVars;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ * @author CJ Hare, normenhansen
+ */
+public class DebugShapeFactory {
+
+ /** The maximum corner for the aabb used for triangles to include in ConcaveShape processing.*/
+// private static final Vector3f aabbMax = new Vector3f(1e30f, 1e30f, 1e30f);
+ /** The minimum corner for the aabb used for triangles to include in ConcaveShape processing.*/
+// private static final Vector3f aabbMin = new Vector3f(-1e30f, -1e30f, -1e30f);
+
+ /**
+ * Creates a debug shape from the given collision shape. This is mostly used internally.<br>
+ * To attach a debug shape to a physics object, call <code>attachDebugShape(AssetManager manager);</code> on it.
+ * @param collisionShape
+ * @return
+ */
+ public static Spatial getDebugShape(CollisionShape collisionShape) {
+ if (collisionShape == null) {
+ return null;
+ }
+ Spatial debugShape;
+ if (collisionShape instanceof CompoundCollisionShape) {
+ CompoundCollisionShape shape = (CompoundCollisionShape) collisionShape;
+ List<ChildCollisionShape> children = shape.getChildren();
+ Node node = new Node("DebugShapeNode");
+ for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
+ ChildCollisionShape childCollisionShape = it.next();
+ CollisionShape ccollisionShape = childCollisionShape.shape;
+ Geometry geometry = createDebugShape(ccollisionShape);
+
+ // apply translation
+ geometry.setLocalTranslation(childCollisionShape.location);
+
+ // apply rotation
+ TempVars vars = TempVars.get();
+ Matrix3f tempRot = vars.tempMat3;
+
+ tempRot.set(geometry.getLocalRotation());
+ childCollisionShape.rotation.mult(tempRot, tempRot);
+ geometry.setLocalRotation(tempRot);
+
+ vars.release();
+
+ node.attachChild(geometry);
+ }
+ debugShape = node;
+ } else {
+ debugShape = createDebugShape(collisionShape);
+ }
+ if (debugShape == null) {
+ return null;
+ }
+ debugShape.updateGeometricState();
+ return debugShape;
+ }
+
+ private static Geometry createDebugShape(CollisionShape shape) {
+ Geometry geom = new Geometry();
+ geom.setMesh(DebugShapeFactory.getDebugMesh(shape));
+// geom.setLocalScale(shape.getScale());
+ geom.updateModelBound();
+ return geom;
+ }
+
+ public static Mesh getDebugMesh(CollisionShape shape) {
+ Mesh mesh = new Mesh();
+ mesh = new Mesh();
+ DebugMeshCallback callback = new DebugMeshCallback();
+ getVertices(shape.getObjectId(), callback);
+ mesh.setBuffer(Type.Position, 3, callback.getVertices());
+ mesh.getFloatBuffer(Type.Position).clear();
+ return mesh;
+ }
+
+ private static native void getVertices(long shapeId, DebugMeshCallback buffer);
+}
diff --git a/engine/src/bullet/com/jme3/bullet/util/NativeMeshUtil.java b/engine/src/bullet/com/jme3/bullet/util/NativeMeshUtil.java
new file mode 100644
index 0000000..4e14b8e
--- /dev/null
+++ b/engine/src/bullet/com/jme3/bullet/util/NativeMeshUtil.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.util;
+
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class NativeMeshUtil {
+
+ public static long getTriangleIndexVertexArray(Mesh mesh){
+ ByteBuffer triangleIndexBase = BufferUtils.createByteBuffer(mesh.getTriangleCount() * 3 * 4);
+ ByteBuffer vertexBase = BufferUtils.createByteBuffer(mesh.getVertexCount() * 3 * 4);
+ int numVertices = mesh.getVertexCount();
+ int vertexStride = 12; //3 verts * 4 bytes per.
+ int numTriangles = mesh.getTriangleCount();
+ int triangleIndexStride = 12; //3 index entries * 4 bytes each.
+
+ IndexBuffer indices = mesh.getIndicesAsList();
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ vertices.rewind();
+
+ int verticesLength = mesh.getVertexCount() * 3;
+ for (int i = 0; i < verticesLength; i++) {
+ float tempFloat = vertices.get();
+ vertexBase.putFloat(tempFloat);
+ }
+
+ int indicesLength = mesh.getTriangleCount() * 3;
+ for (int i = 0; i < indicesLength; i++) {
+ triangleIndexBase.putInt(indices.get(i));
+ }
+ vertices.rewind();
+ vertices.clear();
+
+ return createTriangleIndexVertexArray(triangleIndexBase, vertexBase, numTriangles, numVertices, vertexStride, triangleIndexStride);
+ }
+
+ public static native long createTriangleIndexVertexArray(ByteBuffer triangleIndexBase, ByteBuffer vertexBase, int numTraingles, int numVertices, int vertextStride, int triangleIndexStride);
+
+}
diff --git a/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag
new file mode 100644
index 0000000..35ba78b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag
@@ -0,0 +1,24 @@
+uniform sampler2D m_Texture; // this should hold the texture rendered by the horizontal blur pass
+uniform float m_Size;
+uniform float m_Scale;
+
+varying vec2 texCoord;
+
+void main(){
+ float blurSize = m_Scale/m_Size;
+ vec4 sum = vec4(0.0);
+
+ // blur in x (vertical)
+ // take nine samples, with the distance blurSize between them
+ sum += texture2D(m_Texture, vec2(texCoord.x- 4.0*blurSize, texCoord.y )) * 0.05;
+ sum += texture2D(m_Texture, vec2(texCoord.x- 3.0*blurSize, texCoord.y )) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x - 2.0*blurSize, texCoord.y)) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x- blurSize, texCoord.y )) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y)) * 0.16;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ blurSize, texCoord.y )) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ 2.0*blurSize, texCoord.y )) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ 3.0*blurSize, texCoord.y )) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ 4.0*blurSize, texCoord.y )) * 0.05;
+
+ gl_FragColor = sum;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md
new file mode 100644
index 0000000..ddb262e
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md
@@ -0,0 +1,21 @@
+MaterialDef Bloom {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float Size
+ Float Scale
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Blur/HGaussianBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag
new file mode 100644
index 0000000..b3adfd4
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag
@@ -0,0 +1,47 @@
+uniform sampler2D m_Texture;
+uniform float m_SampleDist;
+uniform float m_SampleStrength;
+uniform float m_Samples[10];
+varying vec2 texCoord;
+
+void main(void)
+{
+ // some sample positions
+ //float samples[10] = float[](-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08);
+
+ // 0.5,0.5 is the center of the screen
+ // so substracting texCoord from it will result in
+ // a vector pointing to the middle of the screen
+ vec2 dir = 0.5 - texCoord;
+
+ // calculate the distance to the center of the screen
+ float dist = sqrt(dir.x*dir.x + dir.y*dir.y);
+
+ // normalize the direction (reuse the distance)
+ dir = dir/dist;
+
+ // this is the original colour of this fragment
+ // using only this would result in a nonblurred version
+ vec4 colorRes = texture2D(m_Texture,texCoord);
+
+ vec4 sum = colorRes;
+
+ // take 10 additional blur samples in the direction towards
+ // the center of the screen
+ for (int i = 0; i < 10; i++)
+ {
+ sum += texture2D( m_Texture, texCoord + dir * m_Samples[i] * m_SampleDist );
+ }
+
+ // we have taken eleven samples
+ sum *= 1.0/11.0;
+
+ // weighten the blur effect with the distance to the
+ // center of the screen ( further out is blurred more)
+ float t = dist * m_SampleStrength;
+ t = clamp( t ,0.0,1.0); //0 &lt;= t &lt;= 1
+
+ //Blend the original color with the averaged pixels
+ gl_FragColor =mix( colorRes, sum, t );
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md
new file mode 100644
index 0000000..1e397fa
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md
@@ -0,0 +1,36 @@
+MaterialDef Radial Blur {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Color Color
+ Float SampleDist
+ Float SampleStrength
+ FloatArray Samples
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Blur/RadialBlur15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL120: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL120: Common/MatDefs/Blur/RadialBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag
new file mode 100644
index 0000000..cc40350
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag
@@ -0,0 +1,50 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform float m_SampleDist;
+uniform float m_SampleStrength;
+uniform float m_Samples[10];
+
+in vec2 texCoord;
+out vec4 outFragColor;
+
+void main(void)
+{
+ // some sample positions
+ //float samples[10] = float[](-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08);
+
+ // 0.5,0.5 is the center of the screen
+ // so substracting texCoord from it will result in
+ // a vector pointing to the middle of the screen
+ vec2 dir = 0.5 - texCoord;
+
+ // calculate the distance to the center of the screen
+ float dist = sqrt(dir.x*dir.x + dir.y*dir.y);
+
+ // normalize the direction (reuse the distance)
+ dir = dir/dist;
+
+ // this is the original colour of this fragment
+ // using only this would result in a nonblurred version
+ vec4 colorRes = getColor(m_Texture,texCoord);
+
+ vec4 sum = colorRes;
+
+ // take 10 additional blur samples in the direction towards
+ // the center of the screen
+ for (int i = 0; i < 10; i++){
+ sum += getColor( m_Texture, texCoord + dir * m_Samples[i] * m_SampleDist );
+ }
+
+ // we have taken eleven samples
+ sum *= 1.0/11.0;
+
+ // weighten the blur effect with the distance to the
+ // center of the screen ( further out is blurred more)
+ float t = dist * m_SampleStrength;
+ t = clamp( t ,0.0,1.0); //0 &lt;= t &lt;= 1
+
+ //Blend the original color with the averaged pixels
+ outFragColor =mix( colorRes, sum, t );
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag
new file mode 100644
index 0000000..3e20fe5
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag
@@ -0,0 +1,25 @@
+uniform sampler2D m_Texture; // this should hold the texture rendered by the horizontal blur pass
+uniform float m_Size;
+uniform float m_Scale;
+varying vec2 texCoord;
+
+
+
+void main(void)
+{ float blurSize = m_Scale/m_Size;
+ vec4 sum = vec4(0.0);
+
+ // blur in y (vertical)
+ // take nine samples, with the distance blurSize between them
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - 4.0*blurSize)) * 0.05;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - 3.0*blurSize)) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - 2.0*blurSize)) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - blurSize)) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y)) * 0.16;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + blurSize)) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + 2.0*blurSize)) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + 3.0*blurSize)) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + 4.0*blurSize)) * 0.05;
+
+ gl_FragColor = sum;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md
new file mode 100644
index 0000000..163f9f3
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md
@@ -0,0 +1,21 @@
+MaterialDef Bloom {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float Size
+ Float Scale
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Blur/VGaussianBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.frag b/engine/src/core-data/Common/MatDefs/Gui/Gui.frag
new file mode 100644
index 0000000..caa666c
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.frag
@@ -0,0 +1,16 @@
+#ifdef TEXTURE
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+#endif
+
+varying vec4 color;
+
+void main() {
+ #ifdef TEXTURE
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ gl_FragColor = texVal * color;
+ #else
+ gl_FragColor = color;
+ #endif
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md b/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md
new file mode 100644
index 0000000..ea871b5
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md
@@ -0,0 +1,26 @@
+MaterialDef Default GUI {
+
+ MaterialParameters {
+ Texture2D Texture
+ Color Color ( Color )
+ Boolean VertexColor
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
+ FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TEXTURE : Texture
+ VERTEX_COLOR : VertexColor
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.vert b/engine/src/core-data/Common/MatDefs/Gui/Gui.vert
new file mode 100644
index 0000000..0591c5e
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.vert
@@ -0,0 +1,29 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform vec4 m_Color;
+
+attribute vec3 inPosition;
+
+#ifdef VERTEX_COLOR
+attribute vec4 inColor;
+#endif
+
+#ifdef TEXTURE
+attribute vec2 inTexCoord;
+varying vec2 texCoord;
+#endif
+
+varying vec4 color;
+
+void main() {
+ //vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ //gl_Position = vec4(pos, 0.0, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ #ifdef TEXTURE
+ texCoord = inTexCoord;
+ #endif
+ #ifdef VERTEX_COLOR
+ color = m_Color * inColor;
+ #else
+ color = m_Color;
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag
new file mode 100644
index 0000000..68f08e9
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag
@@ -0,0 +1,65 @@
+#import "Common/ShaderLib/Hdr.glsllib"
+
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+#ifdef BLOCKS
+ uniform vec2 m_PixelSize;
+ uniform vec2 m_BlockSize;
+ uniform float m_NumPixels;
+#endif
+
+vec4 blocks(vec2 halfBlockSize, vec2 pixelSize, float numPixels){
+ vec2 startUV = texCoord - halfBlockSize;
+ vec2 endUV = texCoord + halfBlockSize;
+
+ vec4 sum = vec4(0.0);
+ float numPix = 0.0;
+ //float maxLum = 0.0;
+
+ for (float x = startUV.x; x < endUV.x; x += pixelSize.x){
+ for (float y = startUV.y; y < endUV.y; y += pixelSize.y){
+ numPix += 1.0;
+ vec4 color = texture2D(m_Texture, vec2(x,y));
+
+ #ifdef ENCODE_LUM
+ color = HDR_EncodeLum(HDR_GetLum(color.rgb));
+ #endif
+ //#ifdef COMPUTE_MAX
+ //maxLum = max(color.r, maxLum);
+ //#endif
+ sum += color;
+ }
+ }
+ sum /= numPix;
+
+ #ifdef DECODE_LUM
+ sum = vec4(HDR_DecodeLum(sum));
+ //#ifdef COMPUTE_MAX
+ //maxLum = HDR_GetExpLum(maxLum);
+ //#endif
+ #endif
+
+ return sum;
+}
+
+vec4 fetch(){
+ vec4 color = texture2D(m_Texture, texCoord);
+ #ifdef ENCODE_LUM
+ return HDR_EncodeLum(HDR_GetLum(color.rgb));
+ #elif defined DECODE_LUM
+ return vec4(HDR_DecodeLum(color));
+ #else
+ return color;
+ #endif
+}
+
+void main() {
+ #ifdef BLOCKS
+ gl_FragColor = blocks(m_BlockSize * vec2(0.5), m_PixelSize, m_NumPixels);
+ #else
+ gl_FragColor = vec4(fetch());
+ #endif
+}
+
+
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md
new file mode 100644
index 0000000..0c4c6c8
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md
@@ -0,0 +1,31 @@
+MaterialDef Log Lum 2D {
+
+ MaterialParameters {
+ Texture2D Texture
+ Vector2 BlockSize
+ Vector2 PixelSize
+ Float NumPixels
+ Boolean DecodeLum
+ Boolean EncodeLum
+ Boolean Blocks
+ Boolean ComputeMax
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
+ FragmentShader GLSL100: Common/MatDefs/Hdr/LogLum.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TEXTURE
+ ENCODE_LUM : EncodeLum
+ DECODE_LUM : DecodeLum
+ BLOCKS : Blocks
+ COMPUTE_MAX : ComputeMax
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag
new file mode 100644
index 0000000..f221030
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag
@@ -0,0 +1,31 @@
+#import "Common/ShaderLib/Hdr.glsllib"
+
+varying vec2 texCoord;
+
+uniform sampler2D m_Texture;
+uniform sampler2D m_Lum;
+uniform sampler2D m_Lum2;
+
+uniform float m_A;
+uniform float m_White;
+uniform float m_BlendFactor;
+uniform float m_Gamma;
+
+void main() {
+ float avgLumA = HDR_DecodeLum( texture2D(m_Lum, vec2(0.0)) );
+ float avgLumB = HDR_DecodeLum( texture2D(m_Lum2, vec2(0.0)) );
+ float lerpedLum = mix(avgLumA, avgLumB, m_BlendFactor);
+
+ vec4 color = texture2D(m_Texture, texCoord);
+ vec3 c1 = HDR_ToneMap(color.rgb, lerpedLum, m_A, m_White);
+ //vec3 c2 = HDR_ToneMap2(color.rgb, lerpedLum, m_A * vec2(0.25), m_White);
+
+ //float l1 = HDR_GetLuminance(c1);
+ //float l2 = HDR_GetLuminance(c2);
+
+ //vec3 final = mix(c2, c1, clamp(l1, 0.0, 1.0));
+
+ //tonedColor = pow(tonedColor, vec3(m_Gamma));
+ gl_FragColor = vec4(c1, color.a);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md
new file mode 100644
index 0000000..24fbd04
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md
@@ -0,0 +1,23 @@
+MaterialDef Tone Mapper {
+ MaterialParameters {
+ Texture2D Texture
+ Texture2D Lum
+ Texture2D Lum2
+ Float BlendFactor
+ Float White
+ Float A
+ Float Gamma
+ }
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
+ FragmentShader GLSL100: Common/MatDefs/Hdr/ToneMap.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TEXTURE
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Deferred.frag b/engine/src/core-data/Common/MatDefs/Light/Deferred.frag
new file mode 100644
index 0000000..9fc7ebb
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Deferred.frag
@@ -0,0 +1,146 @@
+#define ATTENUATION
+//#define HQ_ATTENUATION
+
+varying vec2 texCoord;
+
+uniform sampler2D m_DiffuseData;
+uniform sampler2D m_SpecularData;
+uniform sampler2D m_NormalData;
+uniform sampler2D m_DepthData;
+
+uniform vec3 m_FrustumCorner;
+uniform vec2 m_FrustumNearFar;
+
+uniform vec4 g_LightColor;
+uniform vec4 g_LightPosition;
+uniform vec3 g_CameraPosition;
+
+uniform mat4 m_ViewProjectionMatrixInverse;
+
+#ifdef COLORRAMP
+ uniform sampler2D m_ColorRamp;
+#endif
+
+float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
+ #ifdef MINNAERT
+ float NdotL = max(0.0, dot(norm, lightdir));
+ float NdotV = max(0.0, dot(norm, viewdir));
+ return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5;
+ #else
+ return max(0.0, dot(norm, lightdir));
+ #endif
+}
+
+float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+//#ifdef LOW_QUALITY
+ // Blinn-Phong
+ // Note: preferably, H should be computed in the vertex shader
+ vec3 H = (viewdir + lightdir) * vec3(0.5);
+ return pow(max(dot(H, norm), 0.0), shiny);
+/*
+ #elif defined(WARDISO)
+ // Isotropic Ward
+ vec3 halfVec = normalize(viewdir + lightdir);
+ float NdotH = max(0.001, tangDot(norm, halfVec));
+ float NdotV = max(0.001, tangDot(norm, viewdir));
+ float NdotL = max(0.001, tangDot(norm, lightdir));
+ float a = tan(acos(NdotH));
+ float p = max(shiny/128.0, 0.001);
+ return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
+ #else
+ // Standard Phong
+ vec3 R = reflect(-lightdir, norm);
+ return pow(max(tangDot(R, viewdir), 0.0), shiny);
+ #endif
+*/
+}
+
+vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightDir, in float shiny){
+ float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir.xyz, wvViewDir);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir.xyz, shiny);
+ return vec2(diffuseFactor, specularFactor) * vec2(wvLightDir.w);
+}
+
+vec3 decodeNormal(in vec4 enc){
+ vec4 nn = enc * vec4(2.0,2.0,0.0,0.0) + vec4(-1.0,-1.0,1.0,-1.0);
+ float l = dot(nn.xyz, -nn.xyw);
+ nn.z = l;
+ nn.xy *= sqrt(l);
+ return nn.xyz * vec3(2.0) + vec3(0.0,0.0,-1.0);
+}
+
+vec3 getPosition(in vec2 newTexCoord){
+ //Reconstruction from depth
+ float depth = texture2D(m_DepthData, newTexCoord).r;
+ //if (depth == 1.0)
+ // return vec3(0.0, 0.0, 2.0);
+ //depth = (2.0 * m_FrustumNearFar.x)
+ /// (m_FrustumNearFar.y + m_FrustumNearFar.x - depth * (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ //one frustum corner method
+ //float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, newTexCoord.x);
+ //float y = mix(-m_FrustumCorner.y, m_FrustumCorner.y, newTexCoord.y);
+
+ //return depth * vec3(x, y, m_FrustumCorner.z);
+ vec4 pos;
+ pos.xy = (newTexCoord * vec2(2.0)) - vec2(1.0);
+ pos.z = depth;
+ pos.w = 1.0;
+ pos = m_ViewProjectionMatrixInverse * pos;
+ //pos /= pos.w;
+ return pos.xyz;
+}
+
+// JME3 lights in world space
+void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
+ #ifdef DIR_LIGHT
+ lightDir.xyz = -position.xyz;
+ #else
+ lightDir.xyz = position.xyz - worldPos.xyz;
+ float dist = length(lightDir.xyz);
+ lightDir.w = clamp(1.0 - position.w * dist, 0.0, 1.0);
+ lightDir.xyz /= dist;
+ #endif
+
+/*
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ #ifdef ATTENUATION
+ float dist = length(tempVec);
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / vec3(dist);
+ #ifdef HQ_ATTENUATION
+ lightVec = tempVec;
+ #endif
+ #else
+ lightDir = vec4(normalize(tempVec), 1.0);
+ #endif
+*/
+}
+
+void main(){
+ vec2 newTexCoord = texCoord;
+ vec4 diffuseColor = texture2D(m_DiffuseData, newTexCoord);
+ if (diffuseColor.a == 0.0)
+ discard;
+
+ vec4 specularColor = texture2D(m_SpecularData, newTexCoord);
+ vec3 worldPosition = getPosition(newTexCoord);
+ vec3 viewDir = normalize(g_CameraPosition - worldPosition);
+
+ vec4 normalInfo = vec4(texture2D(m_NormalData, newTexCoord).rg, 0.0, 0.0);
+ vec3 normal = decodeNormal(normalInfo);
+
+ vec4 lightDir;
+ lightComputeDir(worldPosition, g_LightColor, g_LightPosition, lightDir);
+
+ vec2 light = computeLighting(worldPosition, normal, viewDir, lightDir, specularColor.w*128.0);
+
+ #ifdef COLORRAMP
+ diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
+ specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
+ #endif
+
+ gl_FragColor = vec4(light.x * diffuseColor.xyz + light.y * specularColor.xyz, 1.0);
+ gl_FragColor.xyz *= g_LightColor.xyz;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Light/Deferred.j3md b/engine/src/core-data/Common/MatDefs/Light/Deferred.j3md
new file mode 100644
index 0000000..c386a61
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Deferred.j3md
@@ -0,0 +1,61 @@
+MaterialDef Phong Lighting Deferred {
+
+ MaterialParameters {
+
+ // Use more efficent algorithms to improve performance
+ Boolean LowQuality
+
+ // Improve quality at the cost of performance
+ Boolean HighQuality
+
+ // Activate shading along the tangent, instead of the normal
+ // Requires tangent data to be available on the model.
+ Boolean VTangent
+
+ // Use minnaert diffuse instead of lambert
+ Boolean Minnaert
+
+ // Use ward specular instead of phong
+ Boolean WardIso
+
+ Texture2D DiffuseData
+ Texture2D SpecularData
+ Texture2D NormalData
+ Texture2D DepthData
+
+ Vector3 FrustumCorner
+ Vector2 FrustumNearFar
+ Matrix4 ViewProjectionMatrixInverse
+
+ // Color ramp, will map diffuse and specular values through it.
+ Texture2D ColorRamp
+ }
+
+ Technique {
+ LightMode MultiPass
+
+ VertexShader GLSL100: Common/MatDefs/Light/Deferred.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Deferred.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ ViewMatrix
+ CameraPosition
+ }
+
+ Defines {
+ ATTENUATION : Attenuation
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+ LOW_QUALITY : LowQuality
+ HQ_ATTENUATION : HighQuality
+ COLORRAMP : ColorRamp
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Deferred.vert b/engine/src/core-data/Common/MatDefs/Light/Deferred.vert
new file mode 100644
index 0000000..0743cc1
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Deferred.vert
@@ -0,0 +1,10 @@
+varying vec2 texCoord;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+
+void main(){
+ texCoord = inTexCoord;
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = vec4(sign(pos.xy-vec2(0.5)), 0.0, 1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/GBuf.frag b/engine/src/core-data/Common/MatDefs/Light/GBuf.frag
new file mode 100644
index 0000000..3970624
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/GBuf.frag
@@ -0,0 +1,86 @@
+#import "Common/ShaderLib/Optics.glsllib"
+
+uniform float m_Shininess;
+
+varying vec2 texCoord;
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+varying float vDepth;
+varying vec3 vNormal;
+
+#ifdef DIFFUSEMAP
+ uniform sampler2D m_DiffuseMap;
+#endif
+
+#ifdef SPECULARMAP
+ uniform sampler2D m_SpecularMap;
+#endif
+
+#ifdef PARALLAXMAP
+ uniform sampler2D m_ParallaxMap;
+#endif
+
+#ifdef NORMALMAP
+ uniform sampler2D m_NormalMap;
+ varying mat3 tbnMat;
+#endif
+
+vec2 encodeNormal(in vec3 n){
+ vec2 enc = normalize(n.xy) * (sqrt(-n.z*0.5+0.5));
+ enc = enc*vec2(0.5)+vec2(0.5);
+ return enc;
+}
+
+void main(){
+ vec2 newTexCoord = texCoord;
+ float height = 0.0;
+ #if defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)
+ #ifdef PARALLAXMAP
+ height = texture2D(m_ParallaxMap, texCoord).r;
+ #else
+ height = texture2D(m_NormalMap, texCoord).a;
+ #endif
+ float heightScale = 0.05;
+ float heightBias = heightScale * -0.5;
+ height = (height * heightScale + heightBias);
+ #endif
+
+
+ // ***********************
+ // Read from textures
+ // ***********************
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+ vec3 normal = (normalHeight.xyz * vec3(2.0) - vec3(1.0));
+ normal.y = -normal.y;
+
+ normal = tbnMat * normal;
+ #else
+ vec3 normal = vNormal;
+ #if !defined(LOW_QUALITY) && !defined(V_TANGENT)
+ normal = normalize(normal);
+ #endif
+ #endif
+
+ #ifdef DIFFUSEMAP
+ vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
+ #else
+ vec4 diffuseColor = vec4(1.0);
+ #endif
+
+ #ifdef SPECULARMAP
+ vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+ #else
+ vec4 specularColor = vec4(1.0);
+ #endif
+
+ diffuseColor.rgb *= DiffuseSum.rgb;
+ specularColor.rgb *= SpecularSum.rgb;
+
+ gl_FragData[0] = vec4(diffuseColor.rgb, 1.0);
+ gl_FragData[1] = vec4(encodeNormal(normal), 0.0, 0.0);
+ /*encodeNormal(vNormal));*/
+ gl_FragData[2] = vec4(specularColor.rgb, m_Shininess / 128.0);
+}
diff --git a/engine/src/core-data/Common/MatDefs/Light/GBuf.vert b/engine/src/core-data/Common/MatDefs/Light/GBuf.vert
new file mode 100644
index 0000000..f4ad199
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/GBuf.vert
@@ -0,0 +1,71 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+
+uniform vec4 m_Ambient;
+uniform vec4 m_Diffuse;
+uniform vec4 m_Specular;
+uniform float m_Shininess;
+
+varying vec2 texCoord;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+#ifdef NORMALMAP
+attribute vec3 inTangent;
+varying mat3 tbnMat;
+#endif
+
+#ifdef VERTEX_COLOR
+ attribute vec4 inColor;
+#endif
+
+varying vec3 vNormal;
+varying float vDepth;
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ texCoord = inTexCoord;
+
+ #if defined(NORMALMAP)
+ vec4 wvNormal, wvTangent, wvBinormal;
+
+ wvNormal = vec4(inNormal, 0.0);
+ wvTangent = vec4(inTangent, 0.0);
+
+ wvNormal.xyz = normalize( (g_WorldMatrix * wvNormal).xyz );
+ wvTangent.xyz = normalize( (g_WorldMatrix * wvTangent).xyz );
+ wvBinormal.xyz = cross(wvNormal.xyz, wvTangent.xyz);
+ tbnMat = mat3(wvTangent.xyz, wvBinormal.xyz, wvNormal.xyz);
+
+ vNormal = wvNormal.xyz;
+ #else
+ vec4 wvNormal;
+ #ifdef V_TANGENT
+ wvNormal = vec4(inTangent, 0.0);
+ #else
+ wvNormal = vec4(inNormal, 0.0);
+ #endif
+ vNormal = normalize( (g_WorldMatrix * wvNormal).xyz );
+ #endif
+
+ #ifdef MATERIAL_COLORS
+ AmbientSum = m_Ambient;
+ DiffuseSum = m_Diffuse;
+ SpecularSum = m_Specular;
+ #else
+ AmbientSum = vec4(0.0);
+ DiffuseSum = vec4(1.0);
+ SpecularSum = vec4(1.0);
+ #endif
+
+ #ifdef VERTEX_COLOR
+ DiffuseSum *= inColor;
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Glow.frag b/engine/src/core-data/Common/MatDefs/Light/Glow.frag
new file mode 100644
index 0000000..a18a228
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Glow.frag
@@ -0,0 +1,32 @@
+
+#if defined(NEED_TEXCOORD1)
+ varying vec2 texCoord1;
+#else
+ varying vec2 texCoord;
+#endif
+
+
+#ifdef HAS_GLOWMAP
+ uniform sampler2D m_GlowMap;
+#endif
+
+#ifdef HAS_GLOWCOLOR
+ uniform vec4 m_GlowColor;
+#endif
+
+
+void main(){
+ #ifdef HAS_GLOWMAP
+ #if defined(NEED_TEXCOORD1)
+ gl_FragColor = texture2D(m_GlowMap, texCoord1);
+ #else
+ gl_FragColor = texture2D(m_GlowMap, texCoord);
+ #endif
+ #else
+ #ifdef HAS_GLOWCOLOR
+ gl_FragColor = m_GlowColor;
+ #else
+ gl_FragColor = vec4(0.0);
+ #endif
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
new file mode 100644
index 0000000..76f0e15
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
@@ -0,0 +1,278 @@
+#import "Common/ShaderLib/Parallax.glsllib"
+#import "Common/ShaderLib/Optics.glsllib"
+#define ATTENUATION
+//#define HQ_ATTENUATION
+
+varying vec2 texCoord;
+#ifdef SEPARATE_TEXCOORD
+ varying vec2 texCoord2;
+#endif
+
+varying vec3 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec3 SpecularSum;
+
+#ifndef VERTEX_LIGHTING
+ uniform vec4 g_LightDirection;
+ //varying vec3 vPosition;
+ varying vec3 vViewDir;
+ varying vec4 vLightDir;
+ varying vec3 lightVec;
+#else
+ varying vec2 vertexLightValues;
+#endif
+
+#ifdef DIFFUSEMAP
+ uniform sampler2D m_DiffuseMap;
+#endif
+
+#ifdef SPECULARMAP
+ uniform sampler2D m_SpecularMap;
+#endif
+
+#ifdef PARALLAXMAP
+ uniform sampler2D m_ParallaxMap;
+#endif
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
+ uniform float m_ParallaxHeight;
+#endif
+
+#ifdef LIGHTMAP
+ uniform sampler2D m_LightMap;
+#endif
+
+#ifdef NORMALMAP
+ uniform sampler2D m_NormalMap;
+#else
+ varying vec3 vNormal;
+#endif
+
+#ifdef ALPHAMAP
+ uniform sampler2D m_AlphaMap;
+#endif
+
+#ifdef COLORRAMP
+ uniform sampler2D m_ColorRamp;
+#endif
+
+uniform float m_AlphaDiscardThreshold;
+
+#ifndef VERTEX_LIGHTING
+uniform float m_Shininess;
+
+#ifdef HQ_ATTENUATION
+uniform vec4 g_LightPosition;
+#endif
+
+#ifdef USE_REFLECTION
+ uniform float m_ReflectionPower;
+ uniform float m_ReflectionIntensity;
+ varying vec4 refVec;
+
+ uniform ENVMAP m_EnvMap;
+#endif
+
+float tangDot(in vec3 v1, in vec3 v2){
+ float d = dot(v1,v2);
+ #ifdef V_TANGENT
+ d = 1.0 - d*d;
+ return step(0.0, d) * sqrt(d);
+ #else
+ return d;
+ #endif
+}
+
+float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
+ #ifdef MINNAERT
+ float NdotL = max(0.0, dot(norm, lightdir));
+ float NdotV = max(0.0, dot(norm, viewdir));
+ return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5;
+ #else
+ return max(0.0, dot(norm, lightdir));
+ #endif
+}
+
+float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+ // NOTE: check for shiny <= 1 removed since shininess is now
+ // 1.0 by default (uses matdefs default vals)
+ #ifdef LOW_QUALITY
+ // Blinn-Phong
+ // Note: preferably, H should be computed in the vertex shader
+ vec3 H = (viewdir + lightdir) * vec3(0.5);
+ return pow(max(tangDot(H, norm), 0.0), shiny);
+ #elif defined(WARDISO)
+ // Isotropic Ward
+ vec3 halfVec = normalize(viewdir + lightdir);
+ float NdotH = max(0.001, tangDot(norm, halfVec));
+ float NdotV = max(0.001, tangDot(norm, viewdir));
+ float NdotL = max(0.001, tangDot(norm, lightdir));
+ float a = tan(acos(NdotH));
+ float p = max(shiny/128.0, 0.001);
+ return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
+ #else
+ // Standard Phong
+ vec3 R = reflect(-lightdir, norm);
+ return pow(max(tangDot(R, viewdir), 0.0), shiny);
+ #endif
+}
+
+vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
+ float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);
+
+ #ifdef HQ_ATTENUATION
+ float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0);
+ #else
+ float att = vLightDir.w;
+ #endif
+
+ specularFactor *= diffuseFactor;
+
+ return vec2(diffuseFactor, specularFactor) * vec2(att);
+}
+#endif
+
+void main(){
+ vec2 newTexCoord;
+
+ #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
+
+ #ifdef STEEP_PARALLAX
+ #ifdef NORMALMAP_PARALLAX
+ //parallax map is stored in the alpha channel of the normal map
+ newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+ #else
+ //parallax map is a texture
+ newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+ #endif
+ #else
+ #ifdef NORMALMAP_PARALLAX
+ //parallax map is stored in the alpha channel of the normal map
+ newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+ #else
+ //parallax map is a texture
+ newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+ #endif
+ #endif
+ #else
+ newTexCoord = texCoord;
+ #endif
+
+ #ifdef DIFFUSEMAP
+ vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
+ #else
+ vec4 diffuseColor = vec4(1.0);
+ #endif
+
+ float alpha = DiffuseSum.a * diffuseColor.a;
+ #ifdef ALPHAMAP
+ alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
+ #endif
+ if(alpha < m_AlphaDiscardThreshold){
+ discard;
+ }
+
+ #ifndef VERTEX_LIGHTING
+ float spotFallOff = 1.0;
+
+ #if __VERSION__ >= 110
+ // allow use of control flow
+ if(g_LightDirection.w != 0.0){
+ #endif
+
+ vec3 L = normalize(lightVec.xyz);
+ vec3 spotdir = normalize(g_LightDirection.xyz);
+ float curAngleCos = dot(-L, spotdir);
+ float innerAngleCos = floor(g_LightDirection.w) * 0.001;
+ float outerAngleCos = fract(g_LightDirection.w);
+ float innerMinusOuter = innerAngleCos - outerAngleCos;
+ spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;
+
+ #if __VERSION__ >= 110
+ if(spotFallOff <= 0.0){
+ gl_FragColor.rgb = AmbientSum * diffuseColor.rgb;
+ gl_FragColor.a = alpha;
+ return;
+ }else{
+ spotFallOff = clamp(spotFallOff, 0.0, 1.0);
+ }
+ }
+ #else
+ spotFallOff = clamp(spotFallOff, step(g_LightDirection.w, 0.001), 1.0);
+ #endif
+ #endif
+
+ // ***********************
+ // Read from textures
+ // ***********************
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+ vec3 normal = (normalHeight.xyz * vec3(2.0) - vec3(1.0));
+ #ifdef LATC
+ normal.z = sqrt(1.0 - (normal.x * normal.x) - (normal.y * normal.y));
+ #endif
+ //normal.y = -normal.y;
+ #elif !defined(VERTEX_LIGHTING)
+ vec3 normal = vNormal;
+ #if !defined(LOW_QUALITY) && !defined(V_TANGENT)
+ normal = normalize(normal);
+ #endif
+ #endif
+
+ #ifdef SPECULARMAP
+ vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+ #else
+ vec4 specularColor = vec4(1.0);
+ #endif
+
+ #ifdef LIGHTMAP
+ vec3 lightMapColor;
+ #ifdef SEPARATE_TEXCOORD
+ lightMapColor = texture2D(m_LightMap, texCoord2).rgb;
+ #else
+ lightMapColor = texture2D(m_LightMap, texCoord).rgb;
+ #endif
+ specularColor.rgb *= lightMapColor;
+ diffuseColor.rgb *= lightMapColor;
+ #endif
+
+ #ifdef VERTEX_LIGHTING
+ vec2 light = vertexLightValues.xy;
+ #ifdef COLORRAMP
+ light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r;
+ light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r;
+ #endif
+
+ gl_FragColor.rgb = AmbientSum * diffuseColor.rgb +
+ DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) +
+ SpecularSum * specularColor.rgb * vec3(light.y);
+ #else
+ vec4 lightDir = vLightDir;
+ lightDir.xyz = normalize(lightDir.xyz);
+ vec3 viewDir = normalize(vViewDir);
+
+ vec2 light = computeLighting(normal, viewDir, lightDir.xyz) * spotFallOff;
+ #ifdef COLORRAMP
+ diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
+ specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
+ #endif
+
+ // Workaround, since it is not possible to modify varying variables
+ vec4 SpecularSum2 = vec4(SpecularSum, 1.0);
+ #ifdef USE_REFLECTION
+ vec4 refColor = Optics_GetEnvColor(m_EnvMap, refVec.xyz);
+
+ // Interpolate light specularity toward reflection color
+ // Multiply result by specular map
+ specularColor = mix(SpecularSum2 * light.y, refColor, refVec.w) * specularColor;
+
+ SpecularSum2 = vec4(1.0);
+ light.y = 1.0;
+ #endif
+
+ gl_FragColor.rgb = AmbientSum * diffuseColor.rgb +
+ DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) +
+ SpecularSum2.rgb * specularColor.rgb * vec3(light.y);
+ #endif
+ gl_FragColor.a = alpha;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
new file mode 100644
index 0000000..95042b7
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
@@ -0,0 +1,238 @@
+MaterialDef Phong Lighting {
+
+ MaterialParameters {
+
+ // Compute vertex lighting in the shader
+ // For better performance
+ Boolean VertexLighting
+
+ // Use more efficent algorithms to improve performance
+ Boolean LowQuality
+
+ // Improve quality at the cost of performance
+ Boolean HighQuality
+
+ // Output alpha from the diffuse map
+ Boolean UseAlpha
+
+ // Apha threshold for fragment discarding
+ Float AlphaDiscardThreshold
+
+ // Normal map is in BC5/ATI2n/LATC/3Dc compression format
+ Boolean LATC
+
+ // Use the provided ambient, diffuse, and specular colors
+ Boolean UseMaterialColors
+
+ // Activate shading along the tangent, instead of the normal
+ // Requires tangent data to be available on the model.
+ Boolean VTangent
+
+ // Use minnaert diffuse instead of lambert
+ Boolean Minnaert
+
+ // Use ward specular instead of phong
+ Boolean WardIso
+
+ // Use vertex color as an additional diffuse color.
+ Boolean UseVertexColor
+
+ // Ambient color
+ Color Ambient (MaterialAmbient)
+
+ // Diffuse color
+ Color Diffuse (MaterialDiffuse)
+
+ // Specular color
+ Color Specular (MaterialSpecular)
+
+ // Specular power/shininess
+ Float Shininess (MaterialShininess) : 1
+
+ // Diffuse map
+ Texture2D DiffuseMap
+
+ // Normal map
+ Texture2D NormalMap
+
+ // Specular/gloss map
+ Texture2D SpecularMap
+
+ // Parallax/height map
+ Texture2D ParallaxMap
+
+ //Set to true is parallax map is stored in the alpha channel of the normal map
+ Boolean PackedNormalParallax
+
+ //Sets the relief height for parallax mapping
+ Float ParallaxHeight : 0.05
+
+ //Set to true to activate Steep Parallax mapping
+ Boolean SteepParallax
+
+ // Texture that specifies alpha values
+ Texture2D AlphaMap
+
+ // Color ramp, will map diffuse and specular values through it.
+ Texture2D ColorRamp
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+
+ // Set to Use Lightmap
+ Texture2D LightMap
+
+ // Set to use TexCoord2 for the lightmap sampling
+ Boolean SeparateTexCoord
+
+ // The glow color of the object
+ Color GlowColor
+
+ // Parameters for fresnel
+ // X = bias
+ // Y = scale
+ // Z = power
+ Vector3 FresnelParams
+
+ // Env Map for reflection
+ TextureCubeMap EnvMap
+
+ // the env map is a spheremap and not a cube map
+ Boolean EnvMapAsSphereMap
+ }
+
+ Technique {
+
+ LightMode MultiPass
+
+ VertexShader GLSL100: Common/MatDefs/Light/Lighting.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Lighting.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ NormalMatrix
+ WorldViewMatrix
+ ViewMatrix
+ CameraPosition
+ WorldMatrix
+ }
+
+ Defines {
+ LATC : LATC
+ VERTEX_COLOR : UseVertexColor
+ VERTEX_LIGHTING : VertexLighting
+ ATTENUATION : Attenuation
+ MATERIAL_COLORS : UseMaterialColors
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+ LOW_QUALITY : LowQuality
+ HQ_ATTENUATION : HighQuality
+
+ DIFFUSEMAP : DiffuseMap
+ NORMALMAP : NormalMap
+ SPECULARMAP : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ NORMALMAP_PARALLAX : PackedNormalParallax
+ STEEP_PARALLAX : SteepParallax
+ ALPHAMAP : AlphaMap
+ COLORRAMP : ColorRamp
+ LIGHTMAP : LightMap
+ SEPARATE_TEXCOORD : SeparateTexCoord
+
+ USE_REFLECTION : EnvMap
+ SPHERE_MAP : SphereMap
+ }
+ }
+
+ Technique PreShadow {
+
+ VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
+ FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ }
+
+ RenderState {
+ FaceCull Off
+ DepthTest On
+ DepthWrite On
+ PolyOffset 5 0
+ ColorWrite Off
+ }
+
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ }
+
+ RenderState {
+
+ }
+
+ }
+
+ Technique GBuf {
+
+ VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ NormalMatrix
+ WorldViewMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ VERTEX_COLOR : UseVertexColor
+ MATERIAL_COLORS : UseMaterialColors
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+
+ DIFFUSEMAP : DiffuseMap
+ NORMALMAP : NormalMap
+ SPECULARMAP : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ }
+ }
+
+ Technique FixedFunc {
+ LightMode FixedPipeline
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.vert b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
new file mode 100644
index 0000000..042cba9
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
@@ -0,0 +1,207 @@
+#define ATTENUATION
+//#define HQ_ATTENUATION
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+uniform mat3 g_NormalMatrix;
+uniform mat4 g_ViewMatrix;
+
+uniform vec4 m_Ambient;
+uniform vec4 m_Diffuse;
+uniform vec4 m_Specular;
+uniform float m_Shininess;
+
+uniform vec4 g_LightColor;
+uniform vec4 g_LightPosition;
+uniform vec4 g_AmbientLightColor;
+
+varying vec2 texCoord;
+#ifdef SEPARATE_TEXCOORD
+ varying vec2 texCoord2;
+ attribute vec2 inTexCoord2;
+#endif
+
+varying vec3 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec3 SpecularSum;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+varying vec3 lightVec;
+//varying vec4 spotVec;
+
+#ifdef VERTEX_COLOR
+ attribute vec4 inColor;
+#endif
+
+#ifndef VERTEX_LIGHTING
+ attribute vec4 inTangent;
+
+ #ifndef NORMALMAP
+ varying vec3 vNormal;
+ #endif
+ //varying vec3 vPosition;
+ varying vec3 vViewDir;
+ varying vec4 vLightDir;
+#else
+ varying vec2 vertexLightValues;
+ uniform vec4 g_LightDirection;
+#endif
+
+#ifdef USE_REFLECTION
+ uniform vec3 g_CameraPosition;
+ uniform mat4 g_WorldMatrix;
+
+ uniform vec3 m_FresnelParams;
+ varying vec4 refVec;
+
+
+ /**
+ * Input:
+ * attribute inPosition
+ * attribute inNormal
+ * uniform g_WorldMatrix
+ * uniform g_CameraPosition
+ *
+ * Output:
+ * varying refVec
+ */
+ void computeRef(){
+ vec3 worldPos = (g_WorldMatrix * vec4(inPosition,1.0)).xyz;
+
+ vec3 I = normalize( g_CameraPosition - worldPos ).xyz;
+ vec3 N = normalize( (g_WorldMatrix * vec4(inNormal, 0.0)).xyz );
+
+ refVec.xyz = reflect(I, N);
+ refVec.w = m_FresnelParams.x + m_FresnelParams.y * pow(1.0 + dot(I, N), m_FresnelParams.z);
+ }
+#endif
+
+// JME3 lights in world space
+void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ lightVec = tempVec;
+ #ifdef ATTENUATION
+ float dist = length(tempVec);
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / vec3(dist);
+ #else
+ lightDir = vec4(normalize(tempVec), 1.0);
+ #endif
+}
+
+#ifdef VERTEX_LIGHTING
+ float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){
+ return max(0.0, dot(norm, lightdir));
+ }
+
+ float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+ if (shiny <= 1.0){
+ return 0.0;
+ }
+ #ifndef LOW_QUALITY
+ vec3 H = (viewdir + lightdir) * vec3(0.5);
+ return pow(max(dot(H, norm), 0.0), shiny);
+ #else
+ return 0.0;
+ #endif
+ }
+
+vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){
+ vec4 lightDir;
+ lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir);
+ float spotFallOff = 1.0;
+ if(g_LightDirection.w != 0.0){
+ vec3 L=normalize(lightVec.xyz);
+ vec3 spotdir = normalize(g_LightDirection.xyz);
+ float curAngleCos = dot(-L, spotdir);
+ float innerAngleCos = floor(g_LightDirection.w) * 0.001;
+ float outerAngleCos = fract(g_LightDirection.w);
+ float innerMinusOuter = innerAngleCos - outerAngleCos;
+ spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0);
+ }
+ float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess);
+ //specularFactor *= step(0.01, diffuseFactor);
+ return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff;
+ }
+#endif
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ texCoord = inTexCoord;
+ #ifdef SEPARATE_TEXCOORD
+ texCoord2 = inTexCoord2;
+ #endif
+
+ vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
+ vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
+ vec3 viewDir = normalize(-wvPosition);
+
+ //vec4 lightColor = g_LightColor[gl_InstanceID];
+ //vec4 lightPos = g_LightPosition[gl_InstanceID];
+ //vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w));
+ //wvLightPos.w = lightPos.w;
+
+ vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
+ wvLightPos.w = g_LightPosition.w;
+ vec4 lightColor = g_LightColor;
+
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec3 wvTangent = normalize(g_NormalMatrix * inTangent.xyz);
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+
+ mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal);
+
+ //vPosition = wvPosition * tbnMat;
+ //vViewDir = viewDir * tbnMat;
+ vViewDir = -wvPosition * tbnMat;
+ lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+ vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
+ #elif !defined(VERTEX_LIGHTING)
+ vNormal = wvNormal;
+
+ //vPosition = wvPosition;
+ vViewDir = viewDir;
+
+ lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+
+ #ifdef V_TANGENT
+ vNormal = normalize(g_NormalMatrix * inTangent.xyz);
+ vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal);
+ #endif
+ #endif
+
+ //computing spot direction in view space and unpacking spotlight cos
+// spotVec = (g_ViewMatrix * vec4(g_LightDirection.xyz, 0.0) );
+// spotVec.w = floor(g_LightDirection.w) * 0.001;
+// lightVec.w = fract(g_LightDirection.w);
+
+ lightColor.w = 1.0;
+ #ifdef MATERIAL_COLORS
+ AmbientSum = (m_Ambient * g_AmbientLightColor).rgb;
+ DiffuseSum = m_Diffuse * lightColor;
+ SpecularSum = (m_Specular * lightColor).rgb;
+ #else
+ AmbientSum = vec3(0.2, 0.2, 0.2) * g_AmbientLightColor.rgb; // Default: ambient color is dark gray
+ DiffuseSum = lightColor;
+ SpecularSum = vec3(0.0);
+ #endif
+
+ #ifdef VERTEX_COLOR
+ AmbientSum *= inColor.rgb;
+ DiffuseSum *= inColor;
+ #endif
+
+ #ifdef VERTEX_LIGHTING
+ vertexLightValues = computeLighting(wvPosition, wvNormal, viewDir, wvLightPos);
+ #endif
+
+ #ifdef USE_REFLECTION
+ computeRef();
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag
new file mode 100644
index 0000000..272f100
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag
@@ -0,0 +1,9 @@
+varying vec2 texCoord;
+
+uniform sampler2D m_ColorMap;
+uniform vec4 m_Color;
+
+void main(){
+ vec4 texColor = texture2D(m_ColorMap, texCoord);
+ gl_FragColor = vec4(mix(m_Color.rgb, texColor.rgb, texColor.a), 1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md
new file mode 100644
index 0000000..dde8ea8
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md
@@ -0,0 +1,20 @@
+MaterialDef Colored Textured {
+
+ MaterialParameters {
+ Texture2D ColorMap
+ Color Color
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/ColoredTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/ColoredTextured.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert
new file mode 100644
index 0000000..572d841
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.frag b/engine/src/core-data/Common/MatDefs/Misc/Particle.frag
new file mode 100644
index 0000000..08cd2a3
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.frag
@@ -0,0 +1,22 @@
+#ifdef USE_TEXTURE
+uniform sampler2D m_Texture;
+varying vec4 texCoord;
+#endif
+
+varying vec4 color;
+
+void main(){
+ if (color.a <= 0.01)
+ discard;
+
+ #ifdef USE_TEXTURE
+ #ifdef POINT_SPRITE
+ vec2 uv = mix(texCoord.xy, texCoord.zw, gl_PointCoord.xy);
+ #else
+ vec2 uv = texCoord.xy;
+ #endif
+ gl_FragColor = texture2D(m_Texture, uv) * color;
+ #else
+ gl_FragColor = color;
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
new file mode 100644
index 0000000..e28d41a
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
@@ -0,0 +1,89 @@
+MaterialDef Point Sprite {
+
+ MaterialParameters {
+ Texture2D Texture
+ Float Quadratic
+ Boolean PointSprite
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+
+ VertexShader GLSL100 : Common/MatDefs/Misc/Particle.vert
+ FragmentShader GLSL120 : Common/MatDefs/Misc/Particle.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ WorldMatrix
+ CameraPosition
+ }
+
+ RenderState {
+ Blend AlphaAdditive
+ DepthWrite Off
+ PointSprite On
+ // AlphaTestFalloff 0.01
+ }
+
+ Defines {
+ USE_TEXTURE : Texture
+ POINT_SPRITE : PointSprite
+ }
+ }
+
+ Technique {
+
+ VertexShader GLSL100 : Common/MatDefs/Misc/Particle.vert
+ FragmentShader GLSL100 : Common/MatDefs/Misc/Particle.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ WorldMatrix
+ CameraPosition
+ }
+
+ RenderState {
+ Blend AlphaAdditive
+ DepthWrite Off
+ }
+
+ Defines {
+ USE_TEXTURE : Texture
+ }
+ }
+
+ Technique FixedFunc {
+ RenderState {
+ Blend AlphaAdditive
+ // DepthWrite Off
+ // AlphaTestFalloff 0.01
+ }
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+
+ RenderState {
+ PointSprite On
+ Blend AlphaAdditive
+ DepthWrite Off
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.vert b/engine/src/core-data/Common/MatDefs/Misc/Particle.vert
new file mode 100644
index 0000000..9c27336
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.vert
@@ -0,0 +1,42 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec4 inColor;
+attribute vec4 inTexCoord;
+
+varying vec4 color;
+
+#ifdef USE_TEXTURE
+varying vec4 texCoord;
+#endif
+
+#ifdef POINT_SPRITE
+uniform mat4 g_WorldViewMatrix;
+uniform mat4 g_WorldMatrix;
+uniform vec3 g_CameraPosition;
+uniform float m_Quadratic;
+const float SIZE_MULTIPLIER = 4.0;
+attribute float inSize;
+#endif
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ color = inColor;
+
+ #ifdef USE_TEXTURE
+ texCoord = inTexCoord;
+ #endif
+
+ #ifdef POINT_SPRITE
+ vec4 worldPos = g_WorldMatrix * pos;
+ float d = distance(g_CameraPosition.xyz, worldPos.xyz);
+ gl_PointSize = max(1.0, (inSize * SIZE_MULTIPLIER * m_Quadratic) / d);
+
+ //vec4 worldViewPos = g_WorldViewMatrix * pos;
+ //gl_PointSize = (inSize * SIZE_MULTIPLIER * m_Quadratic)*100.0 / worldViewPos.z;
+
+ color.a *= min(gl_PointSize, 1.0);
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag
new file mode 100644
index 0000000..93e4882
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag
@@ -0,0 +1,5 @@
+varying vec3 normal;
+
+void main(){
+ gl_FragColor = vec4((normal * vec3(0.5)) + vec3(0.5), 1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md
new file mode 100644
index 0000000..db480b7
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md
@@ -0,0 +1,10 @@
+MaterialDef Debug Normals {
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/ShowNormals.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/ShowNormals.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert
new file mode 100644
index 0000000..3813043
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+
+varying vec3 normal;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
+ normal = inNormal;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag
new file mode 100644
index 0000000..395f3d1
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag
@@ -0,0 +1,27 @@
+#import "Common/ShaderLib/Texture.glsllib"
+
+varying vec2 texCoord;
+
+uniform sampler2D m_ColorMap;
+
+void main(){
+ //Texture_GetColor(m_ColorMap, texCoord)
+ //vec4 color = texture2D(m_ColorMap, texCoord);
+ //color.rgb *= color.a;
+ //gl_FragColor = vec4(color.a);
+
+ #ifdef NORMAL_LATC
+ vec3 newNorm = vec3(texture2D(m_ColorMap, texCoord).ag, 0.0);
+ newNorm = Common_UnpackNormal(newNorm);
+ newNorm.b = sqrt(1.0 - (newNorm.x * newNorm.x) - (newNorm.y * newNorm.y));
+ newNorm = Common_PackNormal(newNorm);
+ gl_FragColor = vec4(newNorm, 1.0);
+ #elif defined(SHOW_ALPHA)
+ gl_FragColor = vec4(texture2D(m_ColorMap, texCoord).a);
+ #else
+ gl_FragColor = Texture_GetColor(m_ColorMap, texCoord);
+ #endif
+ #ifdef NORMALIZE
+ gl_FragColor = vec4(normalize(gl_FragColor.xyz), gl_FragColor.a);
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md
new file mode 100644
index 0000000..53469e2
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md
@@ -0,0 +1,32 @@
+Exception SimpleTextured.j3md has been marked as obsolete. Please use Unshaded.j3md instead.
+
+MaterialDef Plain Texture {
+
+ MaterialParameters {
+ Texture2D ColorMap
+ Boolean YCoCg
+ Boolean LATC
+ Boolean Normalize
+ Boolean ShowAlpha
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/SimpleTextured.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ DXT_YCOCG : YCoCg
+ NORMAL_LATC : LATC
+ NORMALIZE : Normalize
+ SHOW_ALPHA : ShowAlpha
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert
new file mode 100644
index 0000000..572d841
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Sky.frag b/engine/src/core-data/Common/MatDefs/Misc/Sky.frag
new file mode 100644
index 0000000..3e36e0a
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Sky.frag
@@ -0,0 +1,11 @@
+#import "Common/ShaderLib/Optics.glsllib"
+
+uniform ENVMAP m_Texture;
+
+varying vec3 direction;
+
+void main() {
+ vec3 dir = normalize(direction);
+ gl_FragColor = Optics_GetEnvColor(m_Texture, direction);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Sky.j3md b/engine/src/core-data/Common/MatDefs/Misc/Sky.j3md
new file mode 100644
index 0000000..919309b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Sky.j3md
@@ -0,0 +1,27 @@
+MaterialDef Sky Plane {
+ MaterialParameters {
+ TextureCubeMap Texture
+ Boolean SphereMap
+ Vector3 NormalScale
+ }
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Sky.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Sky.frag
+
+ RenderState {
+ FaceCull Off
+ }
+
+ WorldParameters {
+ ViewMatrix
+ ProjectionMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ SPHERE_MAP : SphereMap
+ }
+ }
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Sky.vert b/engine/src/core-data/Common/MatDefs/Misc/Sky.vert
new file mode 100644
index 0000000..0426f87
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Sky.vert
@@ -0,0 +1,25 @@
+uniform mat4 g_ViewMatrix;
+uniform mat4 g_ProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+
+uniform vec3 m_NormalScale;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+
+varying vec3 direction;
+
+void main(){
+ // set w coordinate to 0
+ vec4 pos = vec4(inPosition, 0.0);
+
+ // compute rotation only for view matrix
+ pos = g_ViewMatrix * pos;
+
+ // now find projection
+ pos.w = 1.0;
+ gl_Position = g_ProjectionMatrix * pos;
+
+ vec4 normal = vec4(inNormal * m_NormalScale, 0.0);
+ direction = normalize( (g_WorldMatrix * normal).xyz );
+}
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md b/engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md
new file mode 100644
index 0000000..15cdff2
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md
@@ -0,0 +1,44 @@
+Exception SolidColor.j3md has been marked as obsolete. Please use Unshaded.j3md instead.
+
+MaterialDef Solid Color {
+
+ MaterialParameters {
+ Vector4 Color
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ Defines {
+ HAS_COLOR : Color
+ }
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag
new file mode 100644
index 0000000..ab28778
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag
@@ -0,0 +1,50 @@
+uniform vec4 m_Color;
+
+#if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
+ #define NEED_TEXCOORD1
+#endif
+
+#ifdef HAS_COLORMAP
+ uniform sampler2D m_ColorMap;
+#endif
+
+#ifdef NEED_TEXCOORD1
+ varying vec2 texCoord1;
+#endif
+
+#ifdef HAS_LIGHTMAP
+ uniform sampler2D m_LightMap;
+ #ifdef SEPARATE_TEXCOORD
+ varying vec2 texCoord2;
+ #endif
+#endif
+
+#ifdef HAS_VERTEXCOLOR
+ varying vec4 vertColor;
+#endif
+
+void main(){
+ vec4 color = vec4(1.0);
+
+ #ifdef HAS_COLORMAP
+ color *= texture2D(m_ColorMap, texCoord1);
+ #endif
+
+ #ifdef HAS_VERTEXCOLOR
+ color *= vertColor;
+ #endif
+
+ #ifdef HAS_COLOR
+ color *= m_Color;
+ #endif
+
+ #ifdef HAS_LIGHTMAP
+ #ifdef SEPARATE_TEXCOORD
+ color.rgb *= texture2D(m_LightMap, texCoord2).rgb;
+ #else
+ color.rgb *= texture2D(m_LightMap, texCoord1).rgb;
+ #endif
+ #endif
+
+ gl_FragColor = color;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md
new file mode 100644
index 0000000..5a33d44
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md
@@ -0,0 +1,70 @@
+MaterialDef Unshaded {
+
+ MaterialParameters {
+ Texture2D ColorMap
+ Texture2D LightMap
+ Color Color ( Color )
+ Boolean VertexColor
+ Boolean SeparateTexCoord
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ SEPARATE_TEXCOORD : SeparateTexCoord
+ HAS_COLORMAP : ColorMap
+ HAS_LIGHTMAP : LightMap
+ HAS_VERTEXCOLOR : VertexColor
+ HAS_COLOR : Color
+ }
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ }
+
+ RenderState {
+
+ }
+
+ }
+
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ HAS_COLORMAP // Must be passed so that Unshaded.vert exports texCoord.
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert
new file mode 100644
index 0000000..7bf9f7e
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert
@@ -0,0 +1,37 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+attribute vec3 inPosition;
+
+#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
+ #define NEED_TEXCOORD1
+#endif
+
+#ifdef NEED_TEXCOORD1
+ attribute vec2 inTexCoord;
+ varying vec2 texCoord1;
+#endif
+
+#ifdef SEPARATE_TEXCOORD
+ attribute vec2 inTexCoord2;
+ varying vec2 texCoord2;
+#endif
+
+#ifdef HAS_VERTEXCOLOR
+ attribute vec4 inColor;
+ varying vec4 vertColor;
+#endif
+
+void main(){
+ #ifdef NEED_TEXCOORD1
+ texCoord1 = inTexCoord;
+ #endif
+
+ #ifdef SEPARATE_TEXCOORD
+ texCoord2 = inTexCoord2;
+ #endif
+
+ #ifdef HAS_VERTEXCOLOR
+ vertColor = inColor;
+ #endif
+
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md b/engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md
new file mode 100644
index 0000000..d5b3eba
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md
@@ -0,0 +1,18 @@
+Exception VertexColor.j3md has been marked as obsolete. Please use Unshaded.j3md instead.
+
+MaterialDef Vertex Color {
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ Defines {
+ HAS_VERTEXCOLOR
+ }
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md b/engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md
new file mode 100644
index 0000000..51a4733
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md
@@ -0,0 +1,38 @@
+Exception WireColor.j3md has been marked as obsolete. Please use Unshaded.j3md instead.
+
+MaterialDef Wire Color {
+
+ MaterialParameters {
+ Vector4 Color : Color
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ RenderState {
+ FaceCull Off
+ Blend Alpha
+ AlphaTestFalloff 0.01
+ Wireframe On
+ }
+
+ Defines {
+ HAS_COLOR : Color
+ }
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ RenderState {
+ FaceCull Off
+ Blend Alpha
+ AlphaTestFalloff 0.01
+ Wireframe On
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag
new file mode 100644
index 0000000..8c3d133
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag
@@ -0,0 +1,12 @@
+#import "Common/ShaderLib/Shadow.glsllib"
+
+uniform SHADOWMAP m_ShadowMap;
+varying vec4 projCoord;
+
+void main() {
+ vec4 coord = projCoord;
+ coord.xyz /= coord.w;
+ float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3;
+ gl_FragColor = vec4(shad,shad,shad,1.0);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md
new file mode 100644
index 0000000..0fc8bfc
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md
@@ -0,0 +1,26 @@
+MaterialDef Post Shadow {
+
+ MaterialParameters {
+ Texture2D ShadowMap
+ Matrix4 LightViewProjectionMatrix
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert
+ FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ NO_SHADOW2DPROJ
+ }
+
+ RenderState {
+ Blend Modulate
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert
new file mode 100644
index 0000000..3f09753
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert
@@ -0,0 +1,31 @@
+uniform mat4 m_LightViewProjectionMatrix;
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+
+varying vec4 projCoord;
+
+attribute vec3 inPosition;
+
+const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
+ 0.0, 0.5, 0.0, 0.0,
+ 0.0, 0.0, 0.5, 0.0,
+ 0.5, 0.5, 0.5, 1.0);
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+
+ // get the vertex in world space
+ vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
+
+ // convert vertex to light viewProj space
+ //projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos);
+ vec4 coord = m_LightViewProjectionMatrix * worldPos;
+ projCoord = biasMat * coord;
+ //projCoord.z /= gl_DepthRange.far;
+ //projCoord = (m_LightViewProjectionMatrix * worldPos);
+ //projCoord /= projCoord.w;
+ //projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5);
+
+ // bias from [-1, 1] to [0, 1] for sampling shadow map
+ //projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag
new file mode 100644
index 0000000..ab5993b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag
@@ -0,0 +1,119 @@
+#ifdef HARDWARE_SHADOWS
+ #define SHADOWMAP sampler2DShadow
+ #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
+#else
+ #define SHADOWMAP sampler2D
+ #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
+#endif
+
+#if FILTER_MODE == 0
+ #define GETSHADOW Shadow_DoShadowCompare
+ #define KERNEL 1.0
+#elif FILTER_MODE == 1
+ #ifdef HARDWARE_SHADOWS
+ #define GETSHADOW Shadow_DoShadowCompare
+ #else
+ #define GETSHADOW Shadow_DoBilinear_2x2
+ #endif
+ #define KERNEL 1.0
+#elif FILTER_MODE == 2
+ #define GETSHADOW Shadow_DoDither_2x2
+ #define KERNEL 1.0
+#elif FILTER_MODE == 3
+ #define GETSHADOW Shadow_DoPCF
+ #define KERNEL 4.0
+#elif FILTER_MODE == 4
+ #define GETSHADOW Shadow_DoPCF
+ #define KERNEL 8.0
+#endif
+
+uniform SHADOWMAP m_ShadowMap0;
+uniform SHADOWMAP m_ShadowMap1;
+uniform SHADOWMAP m_ShadowMap2;
+uniform SHADOWMAP m_ShadowMap3;
+
+uniform vec4 m_Splits;
+
+uniform float m_ShadowIntensity;
+
+varying vec4 projCoord0;
+varying vec4 projCoord1;
+varying vec4 projCoord2;
+varying vec4 projCoord3;
+
+varying float shadowPosition;
+
+const float texSize = 1024.0;
+const float pixSize = 1.0 / texSize;
+const vec2 pixSize2 = vec2(pixSize);
+
+float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){
+ vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2, projCoord.zw);
+ return SHADOWCOMPARE(tex, coord);
+}
+
+float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
+ return SHADOWCOMPARE(tex, projCoord);
+}
+
+float Shadow_BorderCheck(in vec2 coord){
+ // Fastest, "hack" method (uses 4-5 instructions)
+ vec4 t = vec4(coord.xy, 0.0, 1.0);
+ t = step(t.wwxy, t.xyzz);
+ return dot(t,t);
+}
+
+float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ float shadow = 0.0;
+ vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o);
+ shadow *= 0.25 ;
+ return shadow;
+}
+
+float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ vec4 gather = vec4(0.0);
+ gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
+ gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
+ gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
+ gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
+
+ vec2 f = fract( projCoord.xy * texSize );
+ vec2 mx = mix( gather.xz, gather.yw, f.x );
+ return mix( mx.x, mx.y, f.y );
+}
+
+float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
+ float shadow = 0.0;
+ float bound = KERNEL * 0.5 - 0.5;
+ bound *= PCFEDGE;
+ for (float y = -bound; y <= bound; y += PCFEDGE){
+ for (float x = -bound; x <= bound; x += PCFEDGE){
+ shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
+ Shadow_BorderCheck(projCoord.xy),
+ 0.0, 1.0);
+ }
+ }
+
+ shadow = shadow / (KERNEL * KERNEL);
+ return shadow;
+}
+
+void main(){
+ vec4 shadowPerSplit = vec4(0.0);
+ shadowPerSplit.x = GETSHADOW(m_ShadowMap0, projCoord0);
+ shadowPerSplit.y = GETSHADOW(m_ShadowMap1, projCoord1);
+ shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2);
+ shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3);
+
+ vec4 less = step( shadowPosition, m_Splits );
+ vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) );
+ float shadow = dot(shadowPerSplit, less * more );
+
+ shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
+ gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md
new file mode 100644
index 0000000..8b75748
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md
@@ -0,0 +1,63 @@
+MaterialDef Post Shadow {
+
+ MaterialParameters {
+ Int FilterMode
+ Boolean HardwareShadows
+
+ Texture2D ShadowMap0
+ Texture2D ShadowMap1
+ Texture2D ShadowMap2
+ Texture2D ShadowMap3
+
+ Float ShadowIntensity
+ Vector4 Splits
+
+ Matrix4 LightViewProjectionMatrix0
+ Matrix4 LightViewProjectionMatrix1
+ Matrix4 LightViewProjectionMatrix2
+ Matrix4 LightViewProjectionMatrix3
+
+ Float PCFEdge
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert
+ FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ HARDWARE_SHADOWS : HardwareShadows
+ FILTER_MODE : FilterMode
+ PCFEDGE : PCFEdge
+ }
+
+ RenderState {
+ Blend Modulate
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert
+ FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ HARDWARE_SHADOWS : HardwareShadows
+ FILTER_MODE : FilterMode
+ PCFEDGE : PCFEdge
+ }
+
+ RenderState {
+ Blend Modulate
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert
new file mode 100644
index 0000000..b1c1690
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert
@@ -0,0 +1,37 @@
+uniform mat4 m_LightViewProjectionMatrix0;
+uniform mat4 m_LightViewProjectionMatrix1;
+uniform mat4 m_LightViewProjectionMatrix2;
+uniform mat4 m_LightViewProjectionMatrix3;
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+
+varying vec4 projCoord0;
+varying vec4 projCoord1;
+varying vec4 projCoord2;
+varying vec4 projCoord3;
+
+varying float shadowPosition;
+
+attribute vec3 inPosition;
+
+const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
+ 0.0, 0.5, 0.0, 0.0,
+ 0.0, 0.0, 0.5, 0.0,
+ 0.5, 0.5, 0.5, 1.0);
+
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+
+ shadowPosition = gl_Position.z;
+ // get the vertex in world space
+ vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
+
+
+ // populate the light view matrices array and convert vertex to light viewProj space
+ projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
+ projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
+ projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
+ projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag
new file mode 100644
index 0000000..0dc1ddf
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag
@@ -0,0 +1,139 @@
+// Because gpu_shader5 is actually where those
+// gather functions are declared to work on shadowmaps
+#extension GL_ARB_gpu_shader5 : enable
+
+#ifdef HARDWARE_SHADOWS
+ #define SHADOWMAP sampler2DShadow
+ #define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset)
+ #define SHADOWCOMPARE(tex,coord) textureProj(tex, coord)
+ #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
+#else
+ #define SHADOWMAP sampler2D
+ #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
+ #define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
+ #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
+#endif
+
+
+#if FILTER_MODE == 0
+ #define GETSHADOW SHADOWCOMPARE
+ #define KERNEL 1
+#elif FILTER_MODE == 1
+ #ifdef HARDWARE_SHADOWS
+ #define GETSHADOW SHADOWCOMPARE
+ #else
+ #define GETSHADOW Shadow_DoBilinear_2x2
+ #endif
+ #define KERNEL 1
+#elif FILTER_MODE == 2
+ #define GETSHADOW Shadow_DoDither_2x2
+ #define KERNEL 1
+#elif FILTER_MODE == 3
+ #define GETSHADOW Shadow_DoPCF
+ #define KERNEL 4
+#elif FILTER_MODE == 4
+ #define GETSHADOW Shadow_DoPCF
+ #define KERNEL 8
+#endif
+
+out vec4 outFragColor;
+
+uniform SHADOWMAP m_ShadowMap0;
+uniform SHADOWMAP m_ShadowMap1;
+uniform SHADOWMAP m_ShadowMap2;
+uniform SHADOWMAP m_ShadowMap3;
+
+uniform vec4 m_Splits;
+uniform float m_ShadowIntensity;
+
+in vec4 projCoord0;
+in vec4 projCoord1;
+in vec4 projCoord2;
+in vec4 projCoord3;
+in float shadowPosition;
+
+float Shadow_BorderCheck(in vec2 coord){
+ // Fastest, "hack" method (uses 4-5 instructions)
+ vec4 t = vec4(coord.xy, 0.0, 1.0);
+ t = step(t.wwxy, t.xyzz);
+ return dot(t,t);
+}
+
+float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ float border = Shadow_BorderCheck(projCoord.xy);
+ if (border > 0.0)
+ return 1.0;
+
+ ivec2 texSize = textureSize(tex, 0);
+ vec2 pixSize = 1.0 / vec2(texSize);
+
+ float shadow = 0.0;
+ ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw));
+ shadow *= 0.25;
+ return shadow;
+}
+
+float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ float border = Shadow_BorderCheck(projCoord.xy);
+ if (border > 0.0)
+ return 1.0;
+
+ ivec2 texSize = textureSize(tex, 0);
+ #ifdef GL_ARB_gpu_shader5
+ vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
+ vec4 gather = SHADOWGATHER(tex, coord);
+ #else
+ vec4 gather = vec4(0.0);
+ gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
+ gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
+ gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
+ gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
+ #endif
+
+ vec2 f = fract( projCoord.xy * texSize );
+ vec2 mx = mix( gather.xz, gather.yw, f.x );
+ return mix( mx.x, mx.y, f.y );
+}
+
+float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
+ float pixSize = 1.0 / textureSize(tex,0).x;
+
+ float shadow = 0.0;
+ float border = Shadow_BorderCheck(projCoord.xy);
+ if (border > 0.0)
+ return 1.0;
+
+ float bound = KERNEL * 0.5 - 0.5;
+ bound *= PCFEDGE;
+ for (float y = -bound; y <= bound; y += PCFEDGE){
+ for (float x = -bound; x <= bound; x += PCFEDGE){
+ vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw);
+ shadow += SHADOWCOMPARE(tex, coord);
+ }
+ }
+
+ shadow = shadow / (KERNEL * KERNEL);
+ return shadow;
+}
+
+void main(){
+ float shadow = 0.0;
+
+ if(shadowPosition < m_Splits.x){
+ shadow = GETSHADOW(m_ShadowMap0, projCoord0);
+ }else if( shadowPosition < m_Splits.y){
+ shadow = GETSHADOW(m_ShadowMap1, projCoord1);
+ }else if( shadowPosition < m_Splits.z){
+ shadow = GETSHADOW(m_ShadowMap2, projCoord2);
+ }else if( shadowPosition < m_Splits.w){
+ shadow = GETSHADOW(m_ShadowMap3, projCoord3);
+ }
+
+ shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
+ outFragColor = vec4(shadow, shadow, shadow, 1.0);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag
new file mode 100644
index 0000000..5d957e9
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag
@@ -0,0 +1,15 @@
+varying vec2 texCoord;
+
+#ifdef DIFFUSEMAP_ALPHA
+uniform sampler2D m_DiffuseMap;
+#endif
+
+
+void main(){
+ #ifdef DIFFUSEMAP_ALPHA
+ if (texture2D(m_DiffuseMap, texCoord).a <= 0.50)
+ discard;
+ #endif
+
+ gl_FragColor = vec4(1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md
new file mode 100644
index 0000000..070949f
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md
@@ -0,0 +1,19 @@
+MaterialDef Pre Shadow {
+ Technique {
+ VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
+ FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ }
+
+ RenderState {
+ FaceCull Off
+ DepthTest On
+ DepthWrite On
+ PolyOffset 5 0
+ ColorWrite Off
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert
new file mode 100644
index 0000000..fdd3a25
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert
@@ -0,0 +1,12 @@
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * inPosition;
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/Materials/RedColor.j3m b/engine/src/core-data/Common/Materials/RedColor.j3m
new file mode 100644
index 0000000..c7c8e77
--- /dev/null
+++ b/engine/src/core-data/Common/Materials/RedColor.j3m
@@ -0,0 +1,5 @@
+Material Red Color : Common/MatDefs/Misc/Unshaded.j3md {
+ MaterialParameters {
+ Color : 1 0 0 1
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/Materials/VertexColor.j3m b/engine/src/core-data/Common/Materials/VertexColor.j3m
new file mode 100644
index 0000000..ae72092
--- /dev/null
+++ b/engine/src/core-data/Common/Materials/VertexColor.j3m
@@ -0,0 +1,5 @@
+Material Vertex Color Ext : Common/MatDefs/Misc/Unshaded.j3md {
+ MaterialParameters {
+ VertexColor : true
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/Materials/WhiteColor.j3m b/engine/src/core-data/Common/Materials/WhiteColor.j3m
new file mode 100644
index 0000000..1a5d78e
--- /dev/null
+++ b/engine/src/core-data/Common/Materials/WhiteColor.j3m
@@ -0,0 +1,5 @@
+Material White Color : Common/MatDefs/Misc/Unshaded.j3md {
+ MaterialParameters {
+ Color : 1 1 1 1
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Bump.glsllib b/engine/src/core-data/Common/ShaderLib/Bump.glsllib
new file mode 100644
index 0000000..6b9149b
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Bump.glsllib
@@ -0,0 +1,44 @@
+#define SCALE 0.12
+#define BIAS -0.04
+#define BIN_ITER 5
+
+#ifndef BUMP_HQ
+ #define LIN_ITER 5
+#endif
+
+vec2 Bump_DoOcclusionParallax(in sampler2D heightMap, in vec2 texCoord, in vec3 tanViewDir){
+ float size = 1.0 / float(BIN_ITER);
+
+ // depth
+ float d = 1.0;
+ // best depth
+ float bd = 0.0;
+
+ #ifdef BUMP_HQ
+ const int N = 8;
+ int LIN_ITER = mix(2 * N, N, tanViewDir.z);
+ #endif
+
+ // search from front to back
+ for (int i = 0; i < LIN_ITER; i++){
+ d -= dstep;
+ float h = texture2D(heightMap, dp + ds * (1.0 - d)).a;
+ if (bd < 0.005) // if no depth found yet
+ if (d <= h) bd = depth; // best depth
+ }
+
+ for (int i = 0; i < BIN_ITER; i++) {
+ size *= 0.5;
+ float t = texture2D(heightMap, dp + ds * (1.0 - d)).a;
+ if (d <= t) {
+ bd = depth;
+ d += 2 * size;
+ }
+ d -= size;
+ }
+}
+
+vec2 Bump_DoParallax(in sampler2D heightMap, in vec2 texCoord, in vec3 tanViewDir){
+ float h = texture2D(heightMap, texCoord).a * SCALE + BIAS;
+ return texCoord + h * tanViewDir.xy;
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Common.glsllib b/engine/src/core-data/Common/ShaderLib/Common.glsllib
new file mode 100644
index 0000000..8dce15b
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Common.glsllib
@@ -0,0 +1,13 @@
+vec3 Common_UnpackNormal(in vec3 norm){
+ return (norm * vec3(2.0)) - vec3(1.0);
+}
+
+vec3 Common_UnpackNormalLA(in vec4 norm){
+ vec3 newNorm = norm.agb;
+ newNorm.b = sqrt(1.0 - (newNorm.x * newNorm.x) - (newNorm.y * newNorm.y));
+ return (newNorm * vec3(2.0)) - vec3(1.0);
+}
+
+vec3 Common_PackNormal(in vec3 norm){
+ return (norm * vec3(0.5)) + vec3(0.5);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Fog.glsllib b/engine/src/core-data/Common/ShaderLib/Fog.glsllib
new file mode 100644
index 0000000..0a28362
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Fog.glsllib
@@ -0,0 +1,41 @@
+#ifdef FOG
+
+#ifdef FOG_TEXTURE
+uniform sampler2D m_FogTexture;
+#endif
+
+uniform vec3 m_FogColor;
+
+// x == density
+// y == factor
+// z == ystart
+// w == yend
+uniform vec4 m_FogParams;
+
+varying vec3 fogCoord;
+
+void Fog_PerVertex(inout vec4 color, in vec3 wvPosition){
+ float density = g_FogParams.x;
+ float factor = g_FogParams.y;
+ float dist = length(wvPosition.xyz);
+
+ float yf = wvPosition.y;
+ float y0 = g_FogParams.z;
+ float y1 = g_FogParams.w;
+ float yh = (y1 - y0) * 0.5;
+
+ float fogAmt1 = max(step(yh, 0.0), smoothstep(0, yh, max(y1-yf, yf-y0)));
+ float fogAmt2 = exp(-density * density * dist * dist);
+
+ color.rgb = mix(color.rgb, m_FogColor, fogAmt1 * fogAmt2);
+}
+
+void Fog_PerPixel(inout vec4 color){
+ Fog_PerVertex(color, fogCoord);
+}
+
+void Fog_WVPos(in vec4 wvPosition){
+ fogCoord = wvPosition.xyz;
+}
+
+#endif \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Hdr.glsllib b/engine/src/core-data/Common/ShaderLib/Hdr.glsllib
new file mode 100644
index 0000000..5db1423
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Hdr.glsllib
@@ -0,0 +1,65 @@
+const float epsilon = 0.0001;
+const vec3 lumConv = vec3(0.27, 0.67, 0.06);
+
+float HDR_GetLum(in vec3 color){
+ return dot(color, lumConv);
+}
+
+vec4 HDR_EncodeLum(in float lum){
+ float Le = 2.0 * log2(lum + epsilon) + 127.0;
+ vec4 result = vec4(0.0);
+ result.a = fract(Le);
+ result.rgb = vec3((Le - (floor(result.a * 255.0)) / 255.0) / 255.0);
+ return result;
+}
+
+float HDR_DecodeLum(in vec4 logLum){
+ float Le = logLum.r * 255.0 + logLum.a;
+ return exp2((Le - 127.0) / 2.0);
+}
+
+const mat3 rgbToXyz = mat3(
+ 0.2209, 0.3390, 0.4184,
+ 0.1138, 0.6780, 0.7319,
+ 0.0102, 0.1130, 0.2969);
+
+const mat3 xyzToRgb = mat3(
+ 6.0013, -2.700, -1.7995,
+ -1.332, 3.1029, -5.7720,
+ .3007, -1.088, 5.6268);
+
+vec4 HDR_LogLuvEncode(in vec3 rgb){
+ vec4 result;
+ vec3 Xp_Y_XYZp = rgb * rgbToXyz;
+ Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));
+ result.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
+ float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;
+ result.w = fract(Le);
+ result.z = (Le - (floor(result.w * 255.0)) / 255.0) / 255.0;
+ return result;
+}
+
+vec3 HDR_LogLuvDecode(in vec4 logLuv){
+ float Le = logLuv.z * 255.0 + logLuv.w;
+ vec3 Xp_Y_XYZp;
+ Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);
+ Xp_Y_XYZp.z = Xp_Y_XYZp.y / logLuv.y;
+ Xp_Y_XYZp.x = logLuv.x * Xp_Y_XYZp.z;
+ vec3 rgb = Xp_Y_XYZp * xyzToRgb;
+ return max(rgb, 0.0);
+}
+
+vec3 HDR_ToneMap(in vec3 color, in float lumAvg, in float a, in float white){
+ white *= white;
+ float lumHDR = HDR_GetLum(color);
+ float L = (a / lumAvg) * lumHDR;
+ float Ld = 1.0 + (L / white);
+ Ld = (Ld * L) / (1.0 + L);
+ return (color / lumHDR) * Ld;
+ //return color * vec3(Ld);
+}
+
+vec3 HDR_ToneMap2(in vec3 color, in float lumAvg, in float a, in float white){
+ float scale = a / (lumAvg + 0.001);
+ return (vec3(scale) * color) / (color + vec3(1.0));
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Lighting.glsllib b/engine/src/core-data/Common/ShaderLib/Lighting.glsllib
new file mode 100644
index 0000000..4d1b404
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Lighting.glsllib
@@ -0,0 +1,48 @@
+#ifndef NUM_LIGHTS
+ #define NUM_LIGHTS 4
+#endif
+
+uniform mat4 g_ViewMatrix;
+uniform vec4 g_LightPosition[NUM_LIGHTS];
+uniform vec4 g_g_LightColor[NUM_LIGHTS];
+uniform float m_Shininess;
+
+float Lighting_Diffuse(vec3 norm, vec3 lightdir){
+ return max(0.0, dot(norm, lightdir));
+}
+
+float Lighting_Specular(vec3 norm, vec3 viewdir, vec3 lightdir, float shiny){
+ vec3 refdir = reflect(-lightdir, norm);
+ return pow(max(dot(refdir, viewdir), 0.0), shiny);
+}
+
+void Lighting_Direction(vec3 worldPos, vec4 color, vec4 position, out vec4 lightDir){
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ float dist = length(tempVec);
+
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / dist;
+}
+
+void Lighting_ComputePS(vec3 tanNormal, mat3 tbnMat,
+ int lightCount, out vec3 outDiffuse, out vec3 outSpecular){
+ // find tangent view dir & vert pos
+ vec3 tanViewDir = viewDir * tbnMat;
+
+ for (int i = 0; i < lightCount; i++){
+ // find light dir in tangent space, works for point & directional lights
+ vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition[i].xyz, g_LightColor[i].w));
+ wvLightPos.w = g_LightPosition[i].w;
+
+ vec4 tanLightDir;
+ Lighting_Direction(wvPosition, g_LightColor[i], wvLightPos, tanLightDir);
+ tanLightDir.xyz = tanLightDir.xyz * tbnMat;
+
+ vec3 lightScale = g_LightColor[i].rgb * tanLightDir.w;
+ float specular = Lighting_Specular(tanNormal, tanViewDir, tanLightDir.xyz, m_Shininess);
+ float diffuse = Lighting_Diffuse(tanNormal, tanLightDir.xyz);
+ outSpecular += specular * lightScale * step(0.01, diffuse) * g_LightColor[i].rgb;
+ outDiffuse += diffuse * lightScale * g_LightColor[i].rgb;
+ }
+}
diff --git a/engine/src/core-data/Common/ShaderLib/Math.glsllib b/engine/src/core-data/Common/ShaderLib/Math.glsllib
new file mode 100644
index 0000000..6f4cc90
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Math.glsllib
@@ -0,0 +1,4 @@
+/// Multiplies the vector by the quaternion, then returns the resultant vector.
+vec3 Math_QuaternionMult(in vec4 quat, in vec3 vec){
+ return vec + 2.0 * cross(quat.xyz, cross(quat.xyz, vec) + quat.w * vec);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib b/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib
new file mode 100644
index 0000000..0e5355c
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib
@@ -0,0 +1,62 @@
+#extension GL_ARB_texture_multisample : enable
+
+uniform int m_NumSamples;
+uniform int m_NumSamplesDepth;
+
+#ifdef RESOLVE_MS
+ #define COLORTEXTURE sampler2DMS
+#else
+ #define COLORTEXTURE sampler2D
+#endif
+
+#ifdef RESOLVE_DEPTH_MS
+ #define DEPTHTEXTURE sampler2DMS
+#else
+ #define DEPTHTEXTURE sampler2D
+#endif
+
+// NOTE: Only define multisample functions if multisample is available and is being used!
+#if defined(GL_ARB_texture_multisample) && (defined(RESOLVE_MS) || defined(RESOLVE_DEPTH_MS))
+vec4 textureFetch(in sampler2DMS tex,in vec2 texC, in int numSamples){
+ ivec2 iTexC = ivec2(texC * textureSize(tex));
+ vec4 color = vec4(0.0);
+ for (int i = 0; i < numSamples; i++){
+ color += texelFetch(tex, iTexC, i);
+ }
+ return color / numSamples;
+}
+
+vec4 fetchTextureSample(in sampler2DMS tex,in vec2 texC,in int sample){
+ ivec2 iTexC = ivec2(texC * textureSize(tex));
+ return texelFetch(tex, iTexC, sample);
+}
+
+vec4 getColor(in sampler2DMS tex, in vec2 texC){
+ return textureFetch(tex, texC, m_NumSamples);
+}
+
+vec4 getColorSingle(in sampler2DMS tex, in vec2 texC){
+ ivec2 iTexC = ivec2(texC * textureSize(tex));
+ return texelFetch(tex, iTexC, 0);
+}
+
+vec4 getDepth(in sampler2DMS tex,in vec2 texC){
+ return textureFetch(tex,texC,m_NumSamplesDepth);
+}
+#endif
+
+vec4 fetchTextureSample(in sampler2D tex,in vec2 texC,in int sample){
+ return texture2D(tex,texC);
+}
+
+vec4 getColor(in sampler2D tex, in vec2 texC){
+ return texture2D(tex,texC);
+}
+
+vec4 getColorSingle(in sampler2D tex, in vec2 texC){
+ return texture2D(tex, texC);
+}
+
+vec4 getDepth(in sampler2D tex,in vec2 texC){
+ return texture2D(tex,texC);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Optics.glsllib b/engine/src/core-data/Common/ShaderLib/Optics.glsllib
new file mode 100644
index 0000000..5f76243
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Optics.glsllib
@@ -0,0 +1,32 @@
+#ifdef SPHERE_MAP
+#define ENVMAP sampler2D
+#define TEXENV texture2D
+#else
+#define ENVMAP samplerCube
+#define TEXENV textureCube
+#endif
+
+// converts a normalized direction vector
+// into a texture coordinate for fetching
+// texel from a sphere map
+vec2 Optics_SphereCoord(in vec3 dir){
+ float dzplus1 = dir.z + 1.0;
+
+ // compute 1/2p
+ // NOTE: this simplification only works if dir is normalized.
+ float inv_two_p = 1.414 * sqrt(dzplus1);
+ //float inv_two_p = sqrt(dir.x * dir.x + dir.y * dir.y + dzplus1 * dzplus1);
+ inv_two_p *= 2.0;
+ inv_two_p = 1.0 / inv_two_p;
+
+ // compute texcoord
+ return (dir.xy * vec2(inv_two_p)) + vec2(0.5);
+}
+
+vec4 Optics_GetEnvColor(in ENVMAP envMap, in vec3 dir){
+ #ifdef SPHERE_MAP
+ return texture2D(envMap, Optics_SphereCoord(dir));
+ #else
+ return textureCube(envMap, dir);
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Parallax.glsllib b/engine/src/core-data/Common/ShaderLib/Parallax.glsllib
new file mode 100644
index 0000000..61b9d4b
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Parallax.glsllib
@@ -0,0 +1,78 @@
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
+ vec2 steepParallaxOffset(sampler2D parallaxMap, vec3 vViewDir,vec2 texCoord,float parallaxScale){
+ vec2 vParallaxDirection = normalize( vViewDir.xy );
+
+ // The length of this vector determines the furthest amount of displacement: (Ati's comment)
+ float fLength = length( vViewDir );
+ float fParallaxLength = sqrt( fLength * fLength - vViewDir.z * vViewDir.z ) / vViewDir.z;
+
+ // Compute the actual reverse parallax displacement vector: (Ati's comment)
+ vec2 vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
+
+ // Need to scale the amount of displacement to account for different height ranges
+ // in height maps. This is controlled by an artist-editable parameter: (Ati's comment)
+ parallaxScale *=0.3;
+ vParallaxOffsetTS *= parallaxScale;
+
+ vec3 eyeDir = normalize(vViewDir).xyz;
+
+ float nMinSamples = 6.0;
+ float nMaxSamples = 1000.0 * parallaxScale;
+ float nNumSamples = mix( nMinSamples, nMaxSamples, 1.0 - eyeDir.z ); //In reference shader: int nNumSamples = (int)(lerp( nMinSamples, nMaxSamples, dot( eyeDirWS, N ) ));
+ float fStepSize = 1.0 / nNumSamples;
+ float fCurrHeight = 0.0;
+ float fPrevHeight = 1.0;
+ float fNextHeight = 0.0;
+ float nStepIndex = 0.0;
+ vec2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
+ vec2 vTexCurrentOffset = texCoord;
+ float fCurrentBound = 1.0;
+ float fParallaxAmount = 0.0;
+
+ while ( nStepIndex < nNumSamples && fCurrHeight <= fCurrentBound ) {
+ vTexCurrentOffset -= vTexOffsetPerStep;
+ fPrevHeight = fCurrHeight;
+
+
+ #ifdef NORMALMAP_PARALLAX
+ //parallax map is stored in the alpha channel of the normal map
+ fCurrHeight = texture2DLod( parallaxMap, vTexCurrentOffset,1.0).a;
+ #else
+ //parallax map is a texture
+ fCurrHeight = texture2DLod( parallaxMap, vTexCurrentOffset,1.0).r;
+ #endif
+
+ fCurrentBound -= fStepSize;
+ nStepIndex+=1.0;
+ }
+ vec2 pt1 = vec2( fCurrentBound, fCurrHeight );
+ vec2 pt2 = vec2( fCurrentBound + fStepSize, fPrevHeight );
+
+ float fDelta2 = pt2.x - pt2.y;
+ float fDelta1 = pt1.x - pt1.y;
+
+ float fDenominator = fDelta2 - fDelta1;
+
+ fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;
+
+ vec2 vParallaxOffset = vParallaxOffsetTS * (1.0 - fParallaxAmount );
+ return texCoord - vParallaxOffset;
+ }
+
+ vec2 classicParallaxOffset(sampler2D parallaxMap, vec3 vViewDir,vec2 texCoord,float parallaxScale){
+ float h;
+ h = texture2D(parallaxMap, texCoord).a;
+ #ifdef NORMALMAP_PARALLAX
+ //parallax map is stored in the alpha channel of the normal map
+ h = texture2D(parallaxMap, texCoord).a;
+ #else
+ //parallax map is a texture
+ h = texture2D(parallaxMap, texCoord).r;
+ #endif
+ float heightScale = parallaxScale;
+ float heightBias = heightScale* -0.6;
+ vec3 normView = normalize(vViewDir);
+ h = (h * heightScale + heightBias) * normView.z;
+ return texCoord + (h * normView.xy);
+ }
+#endif \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Shadow.glsllib b/engine/src/core-data/Common/ShaderLib/Shadow.glsllib
new file mode 100644
index 0000000..81a5361
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Shadow.glsllib
@@ -0,0 +1,105 @@
+#ifdef NO_SHADOW2DPROJ
+#define SHADOWMAP sampler2D
+#define SHADOWTEX texture2D
+#define SHADCOORD(coord) coord.xy
+#else
+#define SHADOWMAP sampler2DShadow
+#define SHADOWTEX shadow2D
+#define SHADCOORD(coord) vec3(coord.xy,0.0)
+#endif
+
+//float shadowDepth = texture2DProj(tex, projCoord);
+
+const float texSize = 1024.0;
+const float pixSize = 1.0 / texSize;
+const vec2 pixSize2 = vec2(pixSize);
+
+float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, vec4 projCoord, vec2 offset){
+ return step(projCoord.z, SHADOWTEX(tex, SHADCOORD(projCoord.xy + offset * pixSize2)).r);
+}
+
+float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
+ return step(projCoord.z, SHADOWTEX(tex, SHADCOORD(projCoord.xy)).r);
+}
+
+float Shadow_BorderCheck(in vec2 coord){
+ // Very slow method (uses 24 instructions)
+ //if (coord.x >= 1.0)
+ // return 1.0;
+ //else if (coord.x <= 0.0)
+ // return 1.0;
+ //else if (coord.y >= 1.0)
+ // return 1.0;
+ //else if (coord.y <= 0.0)
+ // return 1.0;
+ //else
+ // return 0.0;
+
+ // Fastest, "hack" method (uses 4-5 instructions)
+ vec4 t = vec4(coord.xy, 0.0, 1.0);
+ t = step(t.wwxy, t.xyzz);
+ return dot(t,t);
+}
+
+float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ float shadow = 0.0;
+ vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o);
+ shadow *= 0.25 ;
+ return shadow;
+}
+
+float Shadow_DoBilinear(in SHADOWMAP tex, in vec4 projCoord){
+ const vec2 size = vec2(256.0);
+ const vec2 pixel = vec2(1.0) / vec2(256.0);
+
+ vec2 tc = projCoord.xy * size;
+ vec2 bl = fract(tc);
+ vec2 dn = floor(tc) * pixel;
+ vec2 up = dn + pixel;
+
+ vec4 coord = vec4(dn.xy, projCoord.zw);
+ float s_00 = Shadow_DoShadowCompare(tex, coord);
+ s_00 = clamp(s_00, 0.0, 1.0);
+
+ coord = vec4(up.x, dn.y, projCoord.zw);
+ float s_10 = Shadow_DoShadowCompare(tex, coord);
+ s_10 = clamp(s_10, 0.0, 1.0);
+
+ coord = vec4(dn.x, up.y, projCoord.zw);
+ float s_01 = Shadow_DoShadowCompare(tex, coord);
+ s_01 = clamp(s_01, 0.0, 1.0);
+
+ coord = vec4(up.xy, projCoord.zw);
+ float s_11 = Shadow_DoShadowCompare(tex, coord);
+ s_11 = clamp(s_11, 0.0, 1.0);
+
+ float xb0 = mix(s_00, s_10, clamp(bl.x, 0.0, 1.0));
+ float xb1 = mix(s_01, s_11, clamp(bl.x, 0.0, 1.0));
+ float yb = mix(xb0, xb1, clamp(bl.y, 0.0, 1.0));
+ return yb;
+}
+
+float Shadow_DoPCF_2x2(in SHADOWMAP tex, in vec4 projCoord){
+
+ float shadow = 0.0;
+ float x,y;
+ for (y = -1.5 ; y <=1.5 ; y+=1.0)
+ for (x = -1.5 ; x <=1.5 ; x+=1.0)
+ shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
+ Shadow_BorderCheck(projCoord.xy),
+ 0.0, 1.0);
+
+ shadow /= 16.0 ;
+ return shadow;
+}
+
+
+float Shadow_GetShadow(in SHADOWMAP tex, in vec4 projCoord){
+ return clamp(Shadow_DoDither_2x2(tex, projCoord) + Shadow_BorderCheck(projCoord.xy), 0.0, 1.0);
+}
+
+
diff --git a/engine/src/core-data/Common/ShaderLib/Skinning.glsllib b/engine/src/core-data/Common/ShaderLib/Skinning.glsllib
new file mode 100644
index 0000000..f0641b6
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Skinning.glsllib
@@ -0,0 +1,36 @@
+#ifdef USE_HWSKINNING
+
+#ifndef NUM_BONES
+#error A required pre-processor define "NUM_BONES" is not set!
+#endif
+
+attribute vec4 inBoneWeight;
+attribute vec4 inBoneIndices;
+uniform mat4 m_BoneMatrices[NUM_BONES];
+
+void Skinning_Compute(inout vec4 position, inout vec4 normal){
+ vec4 index = inBoneIndices;
+ vec4 weight = inBoneWeight;
+
+ vec4 newPos = vec4(0.0);
+ vec4 newNormal = vec4(0.0);
+
+ for (float i = 0.0; i < 4.0; i += 1.0){
+ mat4 skinMat = m_BoneMatrices[int(index.x)];
+ newPos += weight.x * (skinMat * position);
+ newNormal += weight.x * (skinMat * normal);
+ index = index.yzwx;
+ weight = weight.yzwx;
+ }
+
+ position = newPos;
+ normal = newNormal;
+}
+
+#else
+
+void Skinning_Compute(inout vec4 position, inout vec4 normal){
+ // skinning disabled, leave position and normal unaltered
+}
+
+#endif \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Splatting.glsllib b/engine/src/core-data/Common/ShaderLib/Splatting.glsllib
new file mode 100644
index 0000000..044fee3
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Splatting.glsllib
@@ -0,0 +1,10 @@
+void Splatting_Base(in sampler2D baseMap, in vec2 tc, in float scale, out vec3 outColor){
+ outColor = texture2D(baseMap, tc * vec2(scale)).rgb;
+}
+
+void Splatting_AlphaDetail(in sampler2D alphaMap, in sampler2D detailMap, in vec2 tc, in float scale, out vec3 outColor){
+ float alpha = sampler2D(alphaMap, tc).r;
+ vec3 color = sampler2D(detailMap, tc * vec2(scale)).rgb;
+ //outColor = mix(outColor, color, alpha);
+ outColor = outColor + color * vec3(alpha);
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Tangent.glsllib b/engine/src/core-data/Common/ShaderLib/Tangent.glsllib
new file mode 100644
index 0000000..308c13d
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Tangent.glsllib
@@ -0,0 +1,11 @@
+uniform mat3 g_NormalMatrix;
+
+void Tangent_ComputeVS(out vec3 outNormal, out vec3 outTangent){
+ outNormal = normalize(g_NormalMatrix * inNormal);
+ outTangent = normalize(g_NormalMatrix * inTangent);
+}
+
+mat3 Tangent_GetBasis(){
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+ return mat3(wvTangent, wvBinormal, wvNormal);
+}
diff --git a/engine/src/core-data/Common/ShaderLib/Texture.glsllib b/engine/src/core-data/Common/ShaderLib/Texture.glsllib
new file mode 100644
index 0000000..829f51b
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Texture.glsllib
@@ -0,0 +1,41 @@
+#import "Common/ShaderLib/Common.glsllib"
+
+vec3 Texture_GetNormal(in sampler2D normalMap, in vec2 texCoord){
+ #ifdef NORMAL_LATC
+ return Common_UnpackNormalLA( texture2D(normalMap, texCoord) );
+ #else
+ return Common_UnpackNormal( texture2D(normalMap, texCoord).rgb );
+ #endif
+}
+
+#ifdef DXT_YCOCG
+const mat4 ycocg_mat = mat4( 1.0, -1.0, 0.0, 1.0,
+ 0.0, 1.0, -0.5 * 256.0 / 255.0, 1.0,
+ -1.0, -1.0, 256.0 / 255.0, 1.0,
+ 0.0, 0.0, 0.0, 0.0 );
+#endif
+
+vec4 Texture_GetColor(in sampler2D colorMap, in vec2 texCoord){
+ #ifdef DXT_YCOCG
+ vec4 color = texture2D(colorMap, texCoord);
+ // fast YCoCg decode:
+ color.z = 1.0 / ((color.z * ( 255.0 / 8.0 )) + 1.0);
+ color.xy *= color.z;
+ return color * ycocg_mat;
+
+ // slow decode:
+ //float Y = color.a;
+ //float scale = 1.0 / ((255.0 / 8.0) * color.b + 1.0);
+ //const float offset = 128.0 / 255.0;
+ //float Co = (color.r - offset) * scale;
+ //float Cg = (color.g - offset) * scale;
+
+ //float R = Y + Co - Cg;
+ //float G = Y + Cg;
+ //float B = Y - Co - Cg;
+
+ //return vec4(R, G, B, 1.0);
+ #else
+ return texture2D(colorMap, texCoord);
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Ubo.glsllib b/engine/src/core-data/Common/ShaderLib/Ubo.glsllib
new file mode 100644
index 0000000..5dbb5b9
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Ubo.glsllib
@@ -0,0 +1,15 @@
+
+
+#ifdef ENABLE_UBO
+ // #version 140
+ #extension GL_ARB_uniform_buffer_object : enable
+
+ #define START_MATPARAMS layout(std140) uniform matparams {
+ #define END_MATPARAMS }
+ #define MATPARAM
+ #define attribute in
+#else
+ #define START_MATPARAMS
+ #define END_MATPARAMS
+ #define MATPARAM uniform
+#endif
diff --git a/engine/src/core-data/Interface/Fonts/Console.fnt b/engine/src/core-data/Interface/Fonts/Console.fnt
new file mode 100644
index 0000000..15ea60c
--- /dev/null
+++ b/engine/src/core-data/Interface/Fonts/Console.fnt
@@ -0,0 +1,99 @@
+info face="Lucida Console" size=11 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0
+common lineHeight=11 base=9 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0
+page id=0 file="Console.png"
+chars count=95
+char id=32 x=61 y=18 width=1 height=0 xoffset=0 yoffset=11 xadvance=7 page=0 chnl=15
+char id=33 x=147 y=8 width=1 height=7 xoffset=3 yoffset=2 xadvance=7 page=0 chnl=15
+char id=34 x=26 y=19 width=4 height=3 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=35 x=214 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=36 x=59 y=0 width=5 height=9 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=37 x=142 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=38 x=150 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=39 x=31 y=19 width=1 height=3 xoffset=3 yoffset=1 xadvance=7 page=0 chnl=15
+char id=40 x=26 y=0 width=4 height=10 xoffset=2 yoffset=1 xadvance=7 page=0 chnl=15
+char id=41 x=16 y=0 width=4 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=42 x=9 y=19 width=5 height=4 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=43 x=245 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=44 x=15 y=19 width=2 height=4 xoffset=3 yoffset=7 xadvance=7 page=0 chnl=15
+char id=45 x=55 y=18 width=5 height=1 xoffset=1 yoffset=5 xadvance=7 page=0 chnl=15
+char id=46 x=41 y=19 width=2 height=2 xoffset=2 yoffset=7 xadvance=7 page=0 chnl=15
+char id=47 x=0 y=0 width=7 height=10 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15
+char id=48 x=31 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=49 x=37 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=50 x=132 y=9 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=51 x=127 y=9 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=52 x=43 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=53 x=142 y=8 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=54 x=103 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=55 x=49 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=56 x=55 y=10 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=57 x=61 y=10 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=58 x=6 y=19 width=2 height=6 xoffset=2 yoffset=3 xadvance=7 page=0 chnl=15
+char id=59 x=131 y=0 width=2 height=8 xoffset=2 yoffset=3 xadvance=7 page=0 chnl=15
+char id=60 x=188 y=8 width=6 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=61 x=18 y=19 width=7 height=3 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
+char id=62 x=202 y=8 width=6 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=63 x=79 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=64 x=190 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=65 x=166 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=66 x=91 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=67 x=242 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=68 x=13 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=69 x=67 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=70 x=109 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=71 x=235 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=72 x=115 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=73 x=121 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=74 x=137 y=8 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=75 x=228 y=0 width=6 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=76 x=7 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=77 x=221 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=78 x=85 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=79 x=0 y=11 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=80 x=73 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=81 x=51 y=0 width=7 height=9 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=82 x=174 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=83 x=25 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=84 x=182 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=85 x=19 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=86 x=198 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=87 x=206 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=88 x=134 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=89 x=158 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=90 x=249 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=91 x=41 y=0 width=3 height=10 xoffset=2 yoffset=1 xadvance=7 page=0 chnl=15
+char id=92 x=8 y=0 width=7 height=10 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15
+char id=93 x=45 y=0 width=3 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=94 x=149 y=8 width=7 height=6 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15
+char id=95 x=47 y=19 width=7 height=1 xoffset=0 yoffset=9 xadvance=7 page=0 chnl=15
+char id=96 x=44 y=19 width=2 height=2 xoffset=2 yoffset=0 xadvance=7 page=0 chnl=15
+char id=97 x=181 y=8 width=6 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=98 x=87 y=0 width=5 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=99 x=227 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=100 x=111 y=0 width=5 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=101 x=221 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=102 x=73 y=0 width=6 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=103 x=93 y=0 width=5 height=8 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=104 x=99 y=0 width=5 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=105 x=123 y=0 width=3 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=106 x=36 y=0 width=4 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=107 x=80 y=0 width=6 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=108 x=127 y=0 width=3 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=109 x=157 y=8 width=7 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=110 x=209 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=111 x=215 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=112 x=117 y=0 width=5 height=8 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=113 x=105 y=0 width=5 height=8 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=114 x=239 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=115 x=251 y=8 width=4 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=116 x=97 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=117 x=0 y=19 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=118 x=165 y=8 width=7 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=119 x=173 y=8 width=7 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=120 x=195 y=8 width=6 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=121 x=65 y=0 width=7 height=8 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=122 x=233 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=123 x=31 y=0 width=4 height=10 xoffset=2 yoffset=1 xadvance=7 page=0 chnl=15
+char id=124 x=49 y=0 width=1 height=10 xoffset=3 yoffset=1 xadvance=7 page=0 chnl=15
+char id=125 x=21 y=0 width=4 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=126 x=33 y=19 width=7 height=2 xoffset=0 yoffset=5 xadvance=7 page=0 chnl=15
diff --git a/engine/src/core-data/Interface/Fonts/Console.png b/engine/src/core-data/Interface/Fonts/Console.png
new file mode 100644
index 0000000..821f92a
--- /dev/null
+++ b/engine/src/core-data/Interface/Fonts/Console.png
Binary files differ
diff --git a/engine/src/core-data/Interface/Fonts/Default.fnt b/engine/src/core-data/Interface/Fonts/Default.fnt
new file mode 100644
index 0000000..97230e8
--- /dev/null
+++ b/engine/src/core-data/Interface/Fonts/Default.fnt
@@ -0,0 +1,229 @@
+info face="null" size=17 bold=0 italic=0 charset="ASCII" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
+common lineHeight=21 base=26 scaleW=256 scaleH=256 pages=1 packed=0
+page id=0 file="Default.png"
+chars count=224
+char id=32 x=30 y=110 width=5 height=3 xoffset=0 yoffset=16 xadvance=4 page=0 chnl=0
+char id=33 x=110 y=60 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=34 x=185 y=95 width=6 height=8 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=0
+char id=35 x=116 y=60 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=36 x=242 y=0 width=10 height=19 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=37 x=189 y=22 width=14 height=17 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
+char id=38 x=204 y=22 width=11 height=17 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=39 x=181 y=95 width=3 height=9 xoffset=0 yoffset=2 xadvance=2 page=0 chnl=0
+char id=40 x=0 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=41 x=6 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=42 x=161 y=95 width=9 height=11 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=43 x=150 y=95 width=10 height=12 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=44 x=192 y=95 width=5 height=8 xoffset=0 yoffset=14 xadvance=4 page=0 chnl=0
+char id=45 x=245 y=95 width=6 height=5 xoffset=0 yoffset=10 xadvance=5 page=0 chnl=0
+char id=46 x=0 y=110 width=5 height=5 xoffset=0 yoffset=14 xadvance=4 page=0 chnl=0
+char id=47 x=12 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=48 x=216 y=22 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=49 x=128 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=50 x=139 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=51 x=227 y=22 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=52 x=150 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=53 x=238 y=22 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=54 x=0 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=55 x=161 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=56 x=11 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=57 x=22 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=58 x=43 y=95 width=5 height=13 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=0
+char id=59 x=172 y=60 width=5 height=16 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=0
+char id=60 x=49 y=95 width=10 height=13 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=61 x=198 y=95 width=10 height=7 xoffset=0 yoffset=9 xadvance=9 page=0 chnl=0
+char id=62 x=60 y=95 width=10 height=13 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=63 x=178 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=64 x=18 y=22 width=13 height=19 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=65 x=189 y=60 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=66 x=202 y=60 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=67 x=33 y=42 width=11 height=17 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=68 x=214 y=60 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=69 x=227 y=60 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=70 x=239 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=71 x=45 y=42 width=12 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=72 x=0 y=78 width=13 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=73 x=14 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=74 x=58 y=42 width=8 height=17 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=0
+char id=75 x=20 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=76 x=32 y=78 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=77 x=43 y=78 width=15 height=16 xoffset=0 yoffset=3 xadvance=14 page=0 chnl=0
+char id=78 x=59 y=78 width=13 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=79 x=67 y=42 width=14 height=17 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
+char id=80 x=73 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=81 x=32 y=22 width=14 height=19 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
+char id=82 x=85 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=83 x=82 y=42 width=11 height=17 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=84 x=97 y=78 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=85 x=94 y=42 width=13 height=17 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=86 x=108 y=78 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=87 x=121 y=78 width=16 height=16 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=0
+char id=88 x=138 y=78 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=89 x=151 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=90 x=163 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=91 x=47 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=92 x=53 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=93 x=59 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=94 x=171 y=95 width=9 height=10 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=95 x=6 y=110 width=9 height=5 xoffset=0 yoffset=15 xadvance=8 page=0 chnl=0
+char id=96 x=209 y=95 width=4 height=7 xoffset=0 yoffset=2 xadvance=3 page=0 chnl=0
+char id=97 x=237 y=78 width=9 height=14 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=98 x=145 y=22 width=10 height=18 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=99 x=247 y=78 width=9 height=14 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=100 x=156 y=22 width=10 height=18 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=101 x=0 y=95 width=10 height=14 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=102 x=108 y=42 width=6 height=17 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=0
+char id=103 x=115 y=42 width=9 height=17 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=104 x=125 y=42 width=10 height=17 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=105 x=175 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=106 x=69 y=0 width=7 height=20 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=107 x=136 y=42 width=9 height=17 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
+char id=108 x=146 y=42 width=5 height=17 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=0
+char id=109 x=71 y=95 width=15 height=13 xoffset=0 yoffset=6 xadvance=14 page=0 chnl=0
+char id=110 x=87 y=95 width=10 height=13 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=111 x=11 y=95 width=10 height=14 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=112 x=152 y=42 width=10 height=17 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=113 x=163 y=42 width=10 height=17 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=114 x=98 y=95 width=7 height=13 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=0
+char id=115 x=22 y=95 width=9 height=14 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=116 x=181 y=78 width=6 height=16 xoffset=0 yoffset=4 xadvance=5 page=0 chnl=0
+char id=117 x=32 y=95 width=10 height=14 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=118 x=106 y=95 width=9 height=13 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=119 x=116 y=95 width=13 height=13 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
+char id=120 x=130 y=95 width=9 height=13 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=121 x=174 y=42 width=9 height=17 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=122 x=140 y=95 width=9 height=13 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=123 x=65 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=124 x=71 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=125 x=77 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=126 x=214 y=95 width=10 height=7 xoffset=0 yoffset=9 xadvance=9 page=0 chnl=0
+char id=127 x=36 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=128 x=46 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=129 x=56 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=130 x=66 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=131 x=76 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=132 x=86 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=133 x=96 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=134 x=106 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=135 x=116 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=136 x=126 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=137 x=136 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=138 x=146 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=139 x=156 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=140 x=166 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=141 x=176 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=142 x=186 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=143 x=196 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=144 x=206 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=145 x=216 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=146 x=226 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=147 x=236 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=148 x=246 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=149 x=0 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=150 x=10 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=151 x=20 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=152 x=30 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=153 x=40 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=154 x=50 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=155 x=60 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=156 x=70 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=157 x=80 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=158 x=90 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=159 x=100 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=160 x=110 y=116 width=5 height=3 xoffset=0 yoffset=16 xadvance=4 page=0 chnl=0
+char id=161 x=116 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=162 x=126 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=163 x=136 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=164 x=146 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=165 x=156 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=166 x=166 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=167 x=176 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=168 x=225 y=95 width=13 height=6 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=169 x=186 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=170 x=196 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=171 x=206 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=172 x=216 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=173 x=226 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=174 x=236 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=175 x=16 y=110 width=13 height=5 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=0
+char id=176 x=246 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=177 x=0 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=178 x=10 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=179 x=20 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=180 x=239 y=95 width=5 height=6 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=181 x=30 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=182 x=40 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=183 x=50 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=184 x=60 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=185 x=70 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=186 x=80 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=187 x=90 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=188 x=100 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=189 x=110 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=190 x=120 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=191 x=130 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=192 x=77 y=0 width=12 height=20 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=0
+char id=193 x=90 y=0 width=12 height=20 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=0
+char id=194 x=83 y=22 width=12 height=19 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0
+char id=195 x=140 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=196 x=96 y=22 width=12 height=19 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0
+char id=197 x=103 y=0 width=12 height=20 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=0
+char id=198 x=150 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=199 x=160 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=200 x=116 y=0 width=11 height=20 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=0
+char id=201 x=128 y=0 width=11 height=20 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=0
+char id=202 x=109 y=22 width=11 height=19 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0
+char id=203 x=121 y=22 width=11 height=19 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0
+char id=204 x=140 y=0 width=5 height=20 xoffset=0 yoffset=-1 xadvance=4 page=0 chnl=0
+char id=205 x=146 y=0 width=5 height=20 xoffset=0 yoffset=-1 xadvance=4 page=0 chnl=0
+char id=206 x=133 y=22 width=5 height=19 xoffset=0 yoffset=0 xadvance=4 page=0 chnl=0
+char id=207 x=139 y=22 width=5 height=19 xoffset=0 yoffset=0 xadvance=4 page=0 chnl=0
+char id=208 x=188 y=78 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=209 x=170 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=210 x=0 y=0 width=14 height=21 xoffset=0 yoffset=-1 xadvance=13 page=0 chnl=0
+char id=211 x=15 y=0 width=14 height=21 xoffset=0 yoffset=-1 xadvance=13 page=0 chnl=0
+char id=212 x=152 y=0 width=14 height=20 xoffset=0 yoffset=0 xadvance=13 page=0 chnl=0
+char id=213 x=180 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=214 x=167 y=0 width=14 height=20 xoffset=0 yoffset=0 xadvance=13 page=0 chnl=0
+char id=215 x=190 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=216 x=200 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=217 x=30 y=0 width=13 height=21 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=0
+char id=218 x=44 y=0 width=13 height=21 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=0
+char id=219 x=182 y=0 width=13 height=20 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=0
+char id=220 x=196 y=0 width=13 height=20 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=0
+char id=221 x=210 y=0 width=11 height=20 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=0
+char id=222 x=201 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=223 x=167 y=22 width=11 height=18 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=0
+char id=224 x=184 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=225 x=194 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=226 x=204 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=227 x=210 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=228 x=214 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=229 x=179 y=22 width=9 height=18 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
+char id=230 x=220 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=231 x=230 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=232 x=224 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=233 x=235 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=234 x=246 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=235 x=0 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=236 x=213 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=237 x=219 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=238 x=225 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=239 x=231 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=240 x=11 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=241 x=240 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=242 x=22 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=243 x=33 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=244 x=44 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=245 x=0 y=124 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=246 x=55 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=247 x=10 y=124 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=248 x=20 y=124 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=249 x=66 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=250 x=77 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=251 x=88 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=252 x=99 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=253 x=222 y=0 width=9 height=20 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=254 x=58 y=0 width=10 height=21 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=255 x=232 y=0 width=9 height=20 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+kernings count=0
diff --git a/engine/src/core-data/Interface/Fonts/Default.png b/engine/src/core-data/Interface/Fonts/Default.png
new file mode 100644
index 0000000..3c2dee2
--- /dev/null
+++ b/engine/src/core-data/Interface/Fonts/Default.png
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Post/BloomExtract.j3md b/engine/src/core-effects/Common/MatDefs/Post/BloomExtract.j3md
new file mode 100644
index 0000000..76614cc
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/BloomExtract.j3md
@@ -0,0 +1,43 @@
+MaterialDef Bloom {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float ExposurePow
+ Float ExposureCutoff
+ Boolean Extract
+ Texture2D GlowMap
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/bloomExtract15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ DO_EXTRACT : Extract
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/bloomExtract.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ DO_EXTRACT : Extract
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md b/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md
new file mode 100644
index 0000000..68f8c28
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md
@@ -0,0 +1,36 @@
+MaterialDef Bloom Final {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Texture2D BloomTex
+ Float BloomIntensity
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/bloomFinal15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/bloomFinal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.frag b/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.frag
new file mode 100644
index 0000000..7ee7f78
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.frag
@@ -0,0 +1,55 @@
+uniform vec4 m_EdgeColor;
+
+uniform float m_EdgeWidth;
+uniform float m_EdgeIntensity;
+
+uniform float m_NormalThreshold;
+uniform float m_DepthThreshold;
+
+uniform float m_NormalSensitivity;
+uniform float m_DepthSensitivity;
+
+varying vec2 texCoord;
+
+uniform sampler2D m_Texture;
+uniform sampler2D m_NormalsTexture;
+uniform sampler2D m_DepthTexture;
+
+uniform vec2 g_Resolution;
+
+vec4 fetchNormalDepth(vec2 tc){
+ vec4 nd;
+ nd.xyz = texture2D(m_NormalsTexture, tc).rgb;
+ nd.w = texture2D(m_DepthTexture, tc).r;
+ return nd;
+}
+
+void main(){
+ vec3 color = texture2D(m_Texture, texCoord).rgb;
+
+ vec2 edgeOffset = vec2(m_EdgeWidth) / g_Resolution;
+
+ vec4 n1 = fetchNormalDepth(texCoord + vec2(-1.0, -1.0) * edgeOffset);
+ vec4 n2 = fetchNormalDepth(texCoord + vec2( 1.0, 1.0) * edgeOffset);
+ vec4 n3 = fetchNormalDepth(texCoord + vec2(-1.0, 1.0) * edgeOffset);
+ vec4 n4 = fetchNormalDepth(texCoord + vec2( 1.0, -1.0) * edgeOffset);
+
+ // Work out how much the normal and depth values are changing.
+ vec4 diagonalDelta = abs(n1 - n2) + abs(n3 - n4);
+
+ float normalDelta = dot(diagonalDelta.xyz, vec3(1.0));
+ float depthDelta = diagonalDelta.w;
+
+ // Filter out very small changes, in order to produce nice clean results.
+ normalDelta = clamp((normalDelta - m_NormalThreshold) * m_NormalSensitivity, 0.0, 1.0);
+ depthDelta = clamp((depthDelta - m_DepthThreshold) * m_DepthSensitivity, 0.0, 1.0);
+
+ // Does this pixel lie on an edge?
+ float edgeAmount = clamp(normalDelta + depthDelta, 0.0, 1.0) * m_EdgeIntensity;
+
+ // Apply the edge detection result to the main scene color.
+ //color *= (1.0 - edgeAmount);
+ color = mix (color,m_EdgeColor.rgb,edgeAmount);
+
+ gl_FragColor = vec4(color, 1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.j3md b/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.j3md
new file mode 100644
index 0000000..687193e
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge.j3md
@@ -0,0 +1,48 @@
+MaterialDef Cartoon Edge {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D NormalsTexture
+ Texture2D DepthTexture
+ Color EdgeColor
+ Float EdgeWidth
+ Float EdgeIntensity
+ Float NormalThreshold
+ Float DepthThreshold
+ Float NormalSensitivity
+ Float DepthSensitivity
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/CartoonEdge15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/CartoonEdge.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge15.frag b/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge15.frag
new file mode 100644
index 0000000..3c3921a
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/CartoonEdge15.frag
@@ -0,0 +1,57 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+uniform sampler2D m_NormalsTexture;
+uniform vec2 g_Resolution;
+
+uniform vec4 m_EdgeColor;
+
+uniform float m_EdgeWidth;
+uniform float m_EdgeIntensity;
+
+uniform float m_NormalThreshold;
+uniform float m_DepthThreshold;
+
+uniform float m_NormalSensitivity;
+uniform float m_DepthSensitivity;
+
+in vec2 texCoord;
+out vec4 outFragColor;
+
+vec4 fetchNormalDepth(vec2 tc){
+ vec4 nd;
+ nd.xyz = texture2D(m_NormalsTexture, tc).rgb;
+ nd.w = fetchTextureSample(m_DepthTexture, tc,0).r;
+ return nd;
+}
+
+void main(){
+ vec3 color = getColor(m_Texture, texCoord).rgb;
+
+ vec2 edgeOffset = vec2(m_EdgeWidth) / textureSize(m_NormalsTexture, 0);
+ vec4 n1 = fetchNormalDepth(texCoord + vec2(-1.0, -1.0) * edgeOffset);
+ vec4 n2 = fetchNormalDepth(texCoord + vec2( 1.0, 1.0) * edgeOffset);
+ vec4 n3 = fetchNormalDepth(texCoord + vec2(-1.0, 1.0) * edgeOffset);
+ vec4 n4 = fetchNormalDepth(texCoord + vec2( 1.0, -1.0) * edgeOffset);
+
+ // Work out how much the normal and depth values are changing.
+ vec4 diagonalDelta = abs(n1 - n2) + abs(n3 - n4);
+
+ float normalDelta = dot(diagonalDelta.xyz, vec3(1.0));
+ float depthDelta = diagonalDelta.w;
+
+ // Filter out very small changes, in order to produce nice clean results.
+ normalDelta = clamp((normalDelta - m_NormalThreshold) * m_NormalSensitivity, 0.0, 1.0);
+ depthDelta = clamp((depthDelta - m_DepthThreshold) * m_DepthSensitivity, 0.0, 1.0);
+
+ // Does this pixel lie on an edge?
+ float edgeAmount = clamp(normalDelta + depthDelta, 0.0, 1.0) * m_EdgeIntensity;
+
+ // Apply the edge detection result to the main scene color.
+ //color *= (1.0 - edgeAmount);
+ color = mix (color,m_EdgeColor.rgb,edgeAmount);
+
+ outFragColor = vec4(color, 1.0);
+}
diff --git a/engine/src/core-effects/Common/MatDefs/Post/CrossHatch.frag b/engine/src/core-effects/Common/MatDefs/Post/CrossHatch.frag
new file mode 100644
index 0000000..fc700f8
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/CrossHatch.frag
@@ -0,0 +1,51 @@
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+uniform vec4 m_LineColor;
+uniform vec4 m_PaperColor;
+uniform float m_ColorInfluenceLine;
+uniform float m_ColorInfluencePaper;
+
+uniform float m_FillValue;
+uniform float m_Luminance1;
+uniform float m_Luminance2;
+uniform float m_Luminance3;
+uniform float m_Luminance4;
+uniform float m_Luminance5;
+
+uniform float m_LineDistance;
+uniform float m_LineThickness;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ float linePixel = 0.0;
+
+ float lum = texVal.r*0.2126 + texVal.g*0.7152 + texVal.b*0.0722;
+
+ if (lum < m_Luminance1){
+ if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2.0) < m_LineThickness)
+ linePixel = 1.0;
+ }
+ if (lum < m_Luminance2){
+ if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2.0) < m_LineThickness)
+ linePixel = 1.0;
+ }
+ if (lum < m_Luminance3){
+ if (mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness)
+ linePixel = 1.0;
+ }
+ if (lum < m_Luminance4){
+ if (mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness)
+ linePixel = 1.0;
+ }
+ if (lum < m_Luminance5){ // No line, make a blob instead
+ linePixel = m_FillValue;
+ }
+
+ // Mix line color with existing color information
+ vec4 lineColor = mix(m_LineColor, texVal, m_ColorInfluenceLine);
+ // Mix paper color with existing color information
+ vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper);
+
+ gl_FragColor = mix(paperColor, lineColor, linePixel);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/CrossHatch.j3md b/engine/src/core-effects/Common/MatDefs/Post/CrossHatch.j3md
new file mode 100644
index 0000000..bf850dc
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/CrossHatch.j3md
@@ -0,0 +1,41 @@
+MaterialDef CrossHatch {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture;
+ Vector4 LineColor;
+ Vector4 PaperColor;
+ Float ColorInfluenceLine;
+ Float ColorInfluencePaper;
+ Float FillValue;
+ Float Luminance1;
+ Float Luminance2;
+ Float Luminance3;
+ Float Luminance4;
+ Float Luminance5;
+ Float LineThickness;
+ Float LineDistance;
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/CrossHatch15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/CrossHatch.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/CrossHatch15.frag b/engine/src/core-effects/Common/MatDefs/Post/CrossHatch15.frag
new file mode 100644
index 0000000..8daa4f7
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/CrossHatch15.frag
@@ -0,0 +1,53 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+in vec2 texCoord;
+
+uniform vec4 m_LineColor;
+uniform vec4 m_PaperColor;
+uniform float m_ColorInfluenceLine;
+uniform float m_ColorInfluencePaper;
+
+uniform float m_FillValue;
+uniform float m_Luminance1;
+uniform float m_Luminance2;
+uniform float m_Luminance3;
+uniform float m_Luminance4;
+uniform float m_Luminance5;
+
+uniform float m_LineDistance;
+uniform float m_LineThickness;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+ float linePixel = 0;
+
+ float lum = texVal.r*0.2126 + texVal.g*0.7152 + texVal.b*0.0722;
+
+ if (lum < m_Luminance1){
+ if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2.0) < m_LineThickness)
+ linePixel = 1;
+ }
+ if (lum < m_Luminance2){
+ if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2.0) < m_LineThickness)
+ linePixel = 1;
+ }
+ if (lum < m_Luminance3){
+ if (mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness)
+ linePixel = 1;
+ }
+ if (lum < m_Luminance4){
+ if (mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness)
+ linePixel = 1;
+ }
+ if (lum < m_Luminance5){ // No line, make a blob instead
+ linePixel = m_FillValue;
+ }
+
+ // Mix line color with existing color information
+ vec4 lineColor = mix(m_LineColor, texVal, m_ColorInfluenceLine);
+ // Mix paper color with existing color information
+ vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper);
+
+ gl_FragColor = mix(paperColor, lineColor, linePixel);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/DepthOfField.frag b/engine/src/core-effects/Common/MatDefs/Post/DepthOfField.frag
new file mode 100644
index 0000000..658da54
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/DepthOfField.frag
@@ -0,0 +1,89 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+varying vec2 texCoord;
+
+uniform float m_FocusRange;
+uniform float m_FocusDistance;
+uniform float m_XScale;
+uniform float m_YScale;
+
+vec2 m_NearFar = vec2( 0.1, 1000.0 );
+
+void main() {
+
+ vec4 texVal = texture2D( m_Texture, texCoord );
+
+ float zBuffer = texture2D( m_DepthTexture, texCoord ).r;
+
+ //
+ // z_buffer_value = a + b / z;
+ //
+ // Where:
+ // a = zFar / ( zFar - zNear )
+ // b = zFar * zNear / ( zNear - zFar )
+ // z = distance from the eye to the object
+ //
+ // Which means:
+ // zb - a = b / z;
+ // z * (zb - a) = b
+ // z = b / (zb - a)
+ //
+ float a = m_NearFar.y / (m_NearFar.y - m_NearFar.x);
+ float b = m_NearFar.y * m_NearFar.x / (m_NearFar.x - m_NearFar.y);
+ float z = b / (zBuffer - a);
+
+ // Above could be the same for any depth-based filter
+
+ // We want to be purely focused right at
+ // m_FocusDistance and be purely unfocused
+ // at +/- m_FocusRange to either side of that.
+ float unfocus = min( 1.0, abs( z - m_FocusDistance ) / m_FocusRange );
+
+ if( unfocus < 0.2 ) {
+ // If we are mostly in focus then don't bother with the
+ // convolution filter
+ gl_FragColor = texVal;
+ } else {
+ // Perform a wide convolution filter and we scatter it
+ // a bit to avoid some texture look-ups. Instead of
+ // a full 5x5 (25-1 lookups) we'll skip every other one
+ // to only perform 12.
+ // 1 0 1 0 1
+ // 0 1 0 1 0
+ // 1 0 x 0 1
+ // 0 1 0 1 0
+ // 1 0 1 0 1
+ //
+ // You can get away with 8 just around the outside but
+ // it looks more jittery to me.
+
+ vec4 sum = vec4(0.0);
+
+ float x = texCoord.x;
+ float y = texCoord.y;
+
+ float xScale = m_XScale;
+ float yScale = m_YScale;
+
+ // In order from lower left to right, depending on how you look at it
+ sum += texture2D( m_Texture, vec2(x - 2.0 * xScale, y - 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 0.0 * xScale, y - 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 2.0 * xScale, y - 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 1.0 * xScale, y - 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 1.0 * xScale, y - 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 2.0 * xScale, y - 0.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 2.0 * xScale, y - 0.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 1.0 * xScale, y + 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 1.0 * xScale, y + 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 2.0 * xScale, y + 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 0.0 * xScale, y + 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 2.0 * xScale, y + 2.0 * yScale) );
+
+ sum = sum / 12.0;
+
+ gl_FragColor = mix( texVal, sum, unfocus );
+
+ // I used this for debugging the range
+ // gl_FragColor.r = unfocus;
+}
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/DepthOfField.j3md b/engine/src/core-effects/Common/MatDefs/Post/DepthOfField.j3md
new file mode 100644
index 0000000..db30a09
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/DepthOfField.j3md
@@ -0,0 +1,25 @@
+MaterialDef Depth Of Field {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D DepthTexture
+ Float FocusRange;
+ Float FocusDistance;
+ Float XScale;
+ Float YScale;
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/DepthOfField.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/FXAA.frag b/engine/src/core-effects/Common/MatDefs/Post/FXAA.frag
new file mode 100644
index 0000000..d630b35
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/FXAA.frag
@@ -0,0 +1,88 @@
+#extension GL_EXT_gpu_shader4 : enable
+
+uniform sampler2D m_Texture;
+uniform vec2 g_Resolution;
+
+uniform float m_VxOffset;
+uniform float m_SpanMax;
+uniform float m_ReduceMul;
+
+varying vec2 texCoord;
+varying vec4 posPos;
+
+#define FxaaTex(t, p) texture2D(t, p)
+
+#if __VERSION__ >= 130
+ #define OffsetVec(a, b) ivec2(a, b)
+ #define FxaaTexOff(t, p, o, r) textureOffset(t, p, o)
+#elif defined(GL_EXT_gpu_shader4)
+ #define OffsetVec(a, b) ivec2(a, b)
+ #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o)
+#else
+ #define OffsetVec(a, b) vec2(a, b)
+ #define FxaaTexOff(t, p, o, r) texture2D(t, p + o * r)
+#endif
+
+vec3 FxaaPixelShader(
+ vec4 posPos, // Output of FxaaVertexShader interpolated across screen.
+ sampler2D tex, // Input texture.
+ vec2 rcpFrame) // Constant {1.0/frameWidth, 1.0/frameHeight}.
+{
+
+ #define FXAA_REDUCE_MIN (1.0/128.0)
+ //#define FXAA_REDUCE_MUL (1.0/8.0)
+ //#define FXAA_SPAN_MAX 8.0
+
+ vec3 rgbNW = FxaaTex(tex, posPos.zw).xyz;
+ vec3 rgbNE = FxaaTexOff(tex, posPos.zw, OffsetVec(1,0), rcpFrame.xy).xyz;
+ vec3 rgbSW = FxaaTexOff(tex, posPos.zw, OffsetVec(0,1), rcpFrame.xy).xyz;
+ vec3 rgbSE = FxaaTexOff(tex, posPos.zw, OffsetVec(1,1), rcpFrame.xy).xyz;
+
+ vec3 rgbM = FxaaTex(tex, posPos.xy).xyz;
+
+ vec3 luma = vec3(0.299, 0.587, 0.114);
+ float lumaNW = dot(rgbNW, luma);
+ float lumaNE = dot(rgbNE, luma);
+ float lumaSW = dot(rgbSW, luma);
+ float lumaSE = dot(rgbSE, luma);
+ float lumaM = dot(rgbM, luma);
+
+ float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
+ float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
+
+ vec2 dir;
+ dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
+ dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
+
+ float dirReduce = max(
+ (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * m_ReduceMul),
+ FXAA_REDUCE_MIN);
+ float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
+ dir = min(vec2( m_SpanMax, m_SpanMax),
+ max(vec2(-m_SpanMax, -m_SpanMax),
+ dir * rcpDirMin)) * rcpFrame.xy;
+
+ vec3 rgbA = (1.0/2.0) * (
+ FxaaTex(tex, posPos.xy + dir * vec2(1.0/3.0 - 0.5)).xyz +
+ FxaaTex(tex, posPos.xy + dir * vec2(2.0/3.0 - 0.5)).xyz);
+ vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
+ FxaaTex(tex, posPos.xy + dir * vec2(0.0/3.0 - 0.5)).xyz +
+ FxaaTex(tex, posPos.xy + dir * vec2(3.0/3.0 - 0.5)).xyz);
+
+ float lumaB = dot(rgbB, luma);
+
+ if ((lumaB < lumaMin) || (lumaB > lumaMax))
+ {
+ return rgbA;
+ }
+ else
+ {
+ return rgbB;
+ }
+}
+
+void main()
+{
+ vec2 rcpFrame = vec2(1.0) / g_Resolution;
+ gl_FragColor = vec4(FxaaPixelShader(posPos, m_Texture, rcpFrame), 1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/FXAA.j3md b/engine/src/core-effects/Common/MatDefs/Post/FXAA.j3md
new file mode 100644
index 0000000..6685585
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/FXAA.j3md
@@ -0,0 +1,20 @@
+MaterialDef FXAA {
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float SubPixelShift
+ Float VxOffset
+ Float SpanMax
+ Float ReduceMul
+ }
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/FXAA.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/FXAA.frag
+ WorldParameters {
+ WorldViewProjectionMatrix
+ Resolution
+ }
+ }
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/FXAA.vert b/engine/src/core-effects/Common/MatDefs/Post/FXAA.vert
new file mode 100644
index 0000000..2390384
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/FXAA.vert
@@ -0,0 +1,18 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform vec2 g_Resolution;
+
+uniform float m_SubPixelShift;
+
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+varying vec4 posPos;
+
+void main() {
+ gl_Position = inPosition * 2.0 - 1.0; //vec4(pos, 0.0, 1.0);
+ texCoord = inTexCoord;
+ vec2 rcpFrame = vec2(1.0) / g_Resolution;
+ posPos.xy = inTexCoord.xy;
+ posPos.zw = inTexCoord.xy - (rcpFrame * vec2(0.5 + m_SubPixelShift));
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Fade.frag b/engine/src/core-effects/Common/MatDefs/Post/Fade.frag
new file mode 100644
index 0000000..b616239
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Fade.frag
@@ -0,0 +1,11 @@
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+uniform float m_Value;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+
+ gl_FragColor = texVal * m_Value;
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Fade.j3md b/engine/src/core-effects/Common/MatDefs/Post/Fade.j3md
new file mode 100644
index 0000000..e93b276
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Fade.j3md
@@ -0,0 +1,34 @@
+MaterialDef Fade {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float Value
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/Fade15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/Fade.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Fade15.frag b/engine/src/core-effects/Common/MatDefs/Post/Fade15.frag
new file mode 100644
index 0000000..c99de34
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Fade15.frag
@@ -0,0 +1,11 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform float m_Value;
+
+in vec2 texCoord;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+ gl_FragColor = texVal * m_Value;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Fog.frag b/engine/src/core-effects/Common/MatDefs/Post/Fog.frag
new file mode 100644
index 0000000..7321b15
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Fog.frag
@@ -0,0 +1,21 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+varying vec2 texCoord;
+
+uniform vec4 m_FogColor;
+uniform float m_FogDensity;
+uniform float m_FogDistance;
+
+vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance);
+const float LOG2 = 1.442695;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ float fogVal =texture2D(m_DepthTexture,texCoord).r;
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - fogVal* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ gl_FragColor =mix(m_FogColor,texVal,fogFactor);
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Fog.j3md b/engine/src/core-effects/Common/MatDefs/Post/Fog.j3md
new file mode 100644
index 0000000..e5d6c8d
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Fog.j3md
@@ -0,0 +1,39 @@
+MaterialDef Fade {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D DepthTexture
+ Vector4 FogColor;
+ Float FogDensity;
+ Float FogDistance;
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/Fog15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/Fog.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Fog15.frag b/engine/src/core-effects/Common/MatDefs/Post/Fog15.frag
new file mode 100644
index 0000000..65a3407
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Fog15.frag
@@ -0,0 +1,24 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+uniform vec4 m_FogColor;
+uniform float m_FogDensity;
+uniform float m_FogDistance;
+
+in vec2 texCoord;
+
+vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance);
+const float LOG2 = 1.442695;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+ float fogVal = getDepth(m_DepthTexture,texCoord).r;
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - fogVal* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ gl_FragColor =mix(m_FogColor,texVal,fogFactor);
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.frag b/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.frag
new file mode 100644
index 0000000..90b2ade
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.frag
@@ -0,0 +1,23 @@
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+uniform float m_gamma;
+
+vec3 gamma(vec3 L,float gamma)
+{
+ return pow(L, vec3(1.0 / gamma));
+}
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+
+ if(m_gamma > 0.0)
+ {
+ texVal.rgb = gamma(texVal.rgb , m_gamma);
+ }
+ #ifdef COMPUTE_LUMA
+ texVal.a = dot(texVal.rgb, vec3(0.299, 0.587, 0.114));
+ #endif
+
+ gl_FragColor = texVal;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.j3md b/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.j3md
new file mode 100644
index 0000000..b9c94c0
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection.j3md
@@ -0,0 +1,39 @@
+MaterialDef GammaCorrection {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float gamma
+ Boolean computeLuma
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/GammaCorrection15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ COMPUTE_LUMA : computeLuma
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/GammaCorrection.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ COMPUTE_LUMA : computeLuma
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection15.frag b/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection15.frag
new file mode 100644
index 0000000..b786190
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/GammaCorrection15.frag
@@ -0,0 +1,26 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+in vec2 texCoord;
+
+uniform float m_gamma;
+
+vec3 gamma(vec3 L,float gamma)
+{
+ return pow(L, vec3(1.0 / gamma));
+}
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+
+ if(m_gamma > 0.0)
+ {
+ texVal.rgb = gamma(texVal.rgb , m_gamma);
+ }
+
+ #ifdef COMPUTE_LUMA
+ texVal.a = dot(texVal.rgb, vec3(0.299, 0.587, 0.114));
+ #endif
+
+ gl_FragColor = texVal;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/LightScattering.frag b/engine/src/core-effects/Common/MatDefs/Post/LightScattering.frag
new file mode 100644
index 0000000..683bbea
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/LightScattering.frag
@@ -0,0 +1,36 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+uniform int m_NbSamples;
+uniform float m_BlurStart;
+uniform float m_BlurWidth;
+uniform float m_LightDensity;
+uniform bool m_Display;
+
+varying vec2 lightPos;
+varying vec2 texCoord;
+
+void main(void)
+{
+ if(m_Display){
+
+ vec4 colorRes= texture2D(m_Texture,texCoord);
+ float factor=(m_BlurWidth/float(m_NbSamples-1.0));
+ float scale;
+ vec2 texCoo=texCoord-lightPos;
+ vec2 scaledCoord;
+ vec4 res = vec4(0.0);
+ for(int i=0; i<m_NbSamples; i++) {
+ scale = i * factor + m_BlurStart ;
+ scaledCoord=texCoo*scale+lightPos;
+ if(texture2D(m_DepthTexture,scaledCoord).r==1.0){
+ res += texture2D(m_Texture,scaledCoord);
+ }
+ }
+ res /= m_NbSamples;
+
+ //Blend the original color with the averaged pixels
+ gl_FragColor =mix( colorRes, res, m_LightDensity);
+ }else{
+ gl_FragColor= texture2D(m_Texture,texCoord);
+ }
+}
diff --git a/engine/src/core-effects/Common/MatDefs/Post/LightScattering.j3md b/engine/src/core-effects/Common/MatDefs/Post/LightScattering.j3md
new file mode 100644
index 0000000..37e0e85
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/LightScattering.j3md
@@ -0,0 +1,41 @@
+MaterialDef Light Scattering {
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D DepthTexture
+ Vector3 LightPosition
+ Int NbSamples
+ Float BlurStart
+ Float BlurWidth
+ Float LightDensity
+ Boolean Display
+ Boolean multiSampledDepth
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/LightScattering15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/LightScattering15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+ Technique {
+ VertexShader GLSL120: Common/MatDefs/Post/LightScattering.vert
+ FragmentShader GLSL120: Common/MatDefs/Post/LightScattering.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/LightScattering.vert b/engine/src/core-effects/Common/MatDefs/Post/LightScattering.vert
new file mode 100644
index 0000000..f58f66e
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/LightScattering.vert
@@ -0,0 +1,14 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform vec3 m_LightPosition;
+
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+varying vec2 texCoord;
+varying vec2 lightPos;
+
+void main() {
+ vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ gl_Position = vec4(pos, 0.0, 1.0);
+ lightPos=m_LightPosition.xy;
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/LightScattering15.frag b/engine/src/core-effects/Common/MatDefs/Post/LightScattering15.frag
new file mode 100644
index 0000000..2a5ae26
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/LightScattering15.frag
@@ -0,0 +1,39 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+uniform int m_NbSamples;
+uniform float m_BlurStart;
+uniform float m_BlurWidth;
+uniform float m_LightDensity;
+uniform bool m_Display;
+
+in vec2 lightPos;
+in vec2 texCoord;
+
+void main(void)
+{
+ if(m_Display){
+
+ vec4 colorRes= getColor(m_Texture,texCoord);
+ float factor=(m_BlurWidth/float(m_NbSamples-1.0));
+ float scale;
+ vec2 texCoo=texCoord-lightPos;
+ vec2 scaledCoord;
+ vec4 res = vec4(0.0);
+ for(int i=0; i<m_NbSamples; i++) {
+ scale = i * factor + m_BlurStart ;
+ scaledCoord=texCoo*scale+lightPos;
+ if(fetchTextureSample(m_DepthTexture, scaledCoord,0).r==1.0){
+ res += fetchTextureSample(m_Texture,scaledCoord,0);
+ }
+ }
+ res /= m_NbSamples;
+
+ //Blend the original color with the averaged pixels
+ gl_FragColor =mix( colorRes, res, m_LightDensity);
+ }else{
+ gl_FragColor= getColor(m_Texture,texCoord);
+ }
+}
diff --git a/engine/src/core-effects/Common/MatDefs/Post/LightScattering15.vert b/engine/src/core-effects/Common/MatDefs/Post/LightScattering15.vert
new file mode 100644
index 0000000..990951b
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/LightScattering15.vert
@@ -0,0 +1,14 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform vec3 m_LightPosition;
+
+in vec4 inPosition;
+in vec2 inTexCoord;
+out vec2 texCoord;
+out vec2 lightPos;
+
+void main() {
+ vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ gl_Position = vec4(pos, 0.0, 1.0);
+ lightPos=m_LightPosition.xy;
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Overlay.frag b/engine/src/core-effects/Common/MatDefs/Post/Overlay.frag
new file mode 100644
index 0000000..15d4bcb
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Overlay.frag
@@ -0,0 +1,9 @@
+uniform sampler2D m_Texture;
+uniform vec4 m_Color;
+varying vec2 texCoord;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ gl_FragColor = texVal * m_Color;
+}
+
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Overlay.j3md b/engine/src/core-effects/Common/MatDefs/Post/Overlay.j3md
new file mode 100644
index 0000000..fbf2d24
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Overlay.j3md
@@ -0,0 +1,36 @@
+MaterialDef Default GUI {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Color Color
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/Overlay15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/Overlay.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Overlay15.frag b/engine/src/core-effects/Common/MatDefs/Post/Overlay15.frag
new file mode 100644
index 0000000..7dc4d1e
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Overlay15.frag
@@ -0,0 +1,11 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform vec4 m_Color;
+in vec2 texCoord;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+ gl_FragColor = texVal * m_Color;
+}
+
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Post.vert b/engine/src/core-effects/Common/MatDefs/Post/Post.vert
new file mode 100644
index 0000000..6d80905
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Post.vert
@@ -0,0 +1,10 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+varying vec2 texCoord;
+
+void main() {
+ gl_Position = inPosition * 2.0 - 1.0; //vec4(pos, 0.0, 1.0);
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Post15.vert b/engine/src/core-effects/Common/MatDefs/Post/Post15.vert
new file mode 100644
index 0000000..367c330
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Post15.vert
@@ -0,0 +1,12 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+in vec4 inPosition;
+in vec2 inTexCoord;
+
+out vec2 texCoord;
+
+void main() {
+ vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ gl_Position = vec4(pos, 0.0, 1.0);
+ texCoord = inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Posterization.frag b/engine/src/core-effects/Common/MatDefs/Post/Posterization.frag
new file mode 100644
index 0000000..d184bc6
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Posterization.frag
@@ -0,0 +1,18 @@
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+uniform int m_NumColors;
+uniform float m_Gamma;
+uniform float m_Strength;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+
+ texVal = pow(texVal, vec4(m_Gamma));
+ texVal = texVal * vec4(m_NumColors);
+ texVal = floor(texVal);
+ texVal = texVal / vec4(m_NumColors);
+ texVal = pow(texVal, vec4(1.0/m_Gamma));
+
+ gl_FragColor = mix(texture2D(m_Texture, texCoord), texVal, m_Strength);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Posterization.j3md b/engine/src/core-effects/Common/MatDefs/Post/Posterization.j3md
new file mode 100644
index 0000000..244dcbc
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Posterization.j3md
@@ -0,0 +1,32 @@
+MaterialDef Posterization {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture;
+ Int NumColors;
+ Float Gamma;
+ Float Strength;
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/Posterization15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/Posterization.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/Posterization15.frag b/engine/src/core-effects/Common/MatDefs/Post/Posterization15.frag
new file mode 100644
index 0000000..f55ac5b
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/Posterization15.frag
@@ -0,0 +1,20 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+in vec2 texCoord;
+
+uniform int m_NumColors;
+uniform float m_Gamma;
+uniform float m_Strength;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+
+ texVal = pow(texVal, vec4(m_Gamma));
+ texVal = texVal * m_NumColors;
+ texVal = floor(texVal);
+ texVal = texVal / m_NumColors;
+ texVal = pow(texVal, vec4(1.0/m_Gamma));
+
+ gl_FragColor = mix(getColor(m_Texture, texCoord), texVal, m_Strength);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Post/bloomExtract.frag b/engine/src/core-effects/Common/MatDefs/Post/bloomExtract.frag
new file mode 100644
index 0000000..23aa45e
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/bloomExtract.frag
@@ -0,0 +1,29 @@
+uniform float m_ExposurePow;
+uniform float m_ExposureCutoff;
+uniform sampler2D m_Texture;
+
+varying vec2 texCoord;
+
+#ifdef HAS_GLOWMAP
+ uniform sampler2D m_GlowMap;
+#endif
+
+void main(){
+ vec4 color = vec4(0.0);
+ #ifdef DO_EXTRACT
+ color = texture2D( m_Texture, texCoord );
+ if ( (color.r+color.g+color.b)/3.0 < m_ExposureCutoff ) {
+ color = vec4(0.0);
+ }else{
+ color = pow(color,vec4(m_ExposurePow));
+ }
+ #endif
+
+ #ifdef HAS_GLOWMAP
+ vec4 glowColor = texture2D(m_GlowMap, texCoord);
+ glowColor = pow(glowColor, vec4(m_ExposurePow));
+ color += glowColor;
+ #endif
+
+ gl_FragColor = color;
+}
diff --git a/engine/src/core-effects/Common/MatDefs/Post/bloomExtract15.frag b/engine/src/core-effects/Common/MatDefs/Post/bloomExtract15.frag
new file mode 100644
index 0000000..7194f9c
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/bloomExtract15.frag
@@ -0,0 +1,33 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+
+uniform float m_ExposurePow;
+uniform float m_ExposureCutoff;
+
+in vec2 texCoord;
+out vec4 outFragColor;
+
+#ifdef HAS_GLOWMAP
+ uniform sampler2D m_GlowMap;
+#endif
+
+void main(){
+ vec4 color = vec4(0.0);
+ #ifdef DO_EXTRACT
+ color = getColorSingle(m_Texture, texCoord);
+ if ( (color.r + color.g + color.b) / 3.0 >= m_ExposureCutoff ) {
+ color = pow(color, vec4(m_ExposurePow));
+ }else{
+ color = vec4(0.0);
+ }
+ #endif
+
+ #ifdef HAS_GLOWMAP
+ vec4 glowColor = texture2D( m_GlowMap, texCoord );
+ glowColor = pow(glowColor, vec4(m_ExposurePow));
+ color += glowColor;
+ #endif
+
+ outFragColor = color;
+}
diff --git a/engine/src/core-effects/Common/MatDefs/Post/bloomFinal.frag b/engine/src/core-effects/Common/MatDefs/Post/bloomFinal.frag
new file mode 100644
index 0000000..9499e58
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/bloomFinal.frag
@@ -0,0 +1,12 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_BloomTex;
+uniform float m_BloomIntensity;
+
+varying vec2 texCoord;
+
+void main(){
+ vec4 colorRes = texture2D(m_Texture, texCoord);
+ vec4 bloom = texture2D(m_BloomTex, texCoord);
+ gl_FragColor = bloom * m_BloomIntensity + colorRes;
+}
+
diff --git a/engine/src/core-effects/Common/MatDefs/Post/bloomFinal15.frag b/engine/src/core-effects/Common/MatDefs/Post/bloomFinal15.frag
new file mode 100644
index 0000000..34268c5
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Post/bloomFinal15.frag
@@ -0,0 +1,15 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+
+uniform sampler2D m_BloomTex;
+uniform float m_BloomIntensity;
+
+in vec2 texCoord;
+
+void main(){
+ vec4 colorRes = getColor(m_Texture,texCoord);
+ vec4 bloom = texture2D(m_BloomTex, texCoord);
+ gl_FragColor = bloom * m_BloomIntensity + colorRes;
+}
+
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/Textures/random.png b/engine/src/core-effects/Common/MatDefs/SSAO/Textures/random.png
new file mode 100644
index 0000000..e19b7f5
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/Textures/random.png
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/normal.frag b/engine/src/core-effects/Common/MatDefs/SSAO/normal.frag
new file mode 100644
index 0000000..289c4c0
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/normal.frag
@@ -0,0 +1,21 @@
+varying vec3 normal;
+varying vec2 texCoord;
+
+
+#ifdef DIFFUSEMAP_ALPHA
+ uniform sampler2D m_DiffuseMap;
+ uniform float m_AlphaDiscardThreshold;
+#endif
+
+void main(void)
+{
+
+ #ifdef DIFFUSEMAP_ALPHA
+ if(texture2D(m_DiffuseMap,texCoord).a<m_AlphaDiscardThreshold){
+ discard;
+ }
+ #endif
+ gl_FragColor = vec4(normal.xy* 0.5 + 0.5,-normal.z* 0.5 + 0.5, 1.0);
+
+}
+
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/normal.vert b/engine/src/core-effects/Common/MatDefs/SSAO/normal.vert
new file mode 100644
index 0000000..24a76cb
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/normal.vert
@@ -0,0 +1,16 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat3 g_NormalMatrix;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+attribute vec4 inTexCoord;
+
+varying vec3 normal;
+varying vec2 texCoord;
+
+void main(void)
+{
+ texCoord=inTexCoord.xy;
+ normal = normalize(g_NormalMatrix * inNormal);
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/ssao.frag b/engine/src/core-effects/Common/MatDefs/SSAO/ssao.frag
new file mode 100644
index 0000000..3cd860d
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/ssao.frag
@@ -0,0 +1,104 @@
+uniform vec2 g_Resolution;
+uniform vec2 m_FrustumNearFar;
+uniform sampler2D m_Texture;
+uniform sampler2D m_Normals;
+uniform sampler2D m_DepthTexture;
+uniform vec3 m_FrustumCorner;
+uniform float m_SampleRadius;
+uniform float m_Intensity;
+uniform float m_Scale;
+uniform float m_Bias;
+uniform bool m_UseOnlyAo;
+uniform bool m_UseAo;
+uniform vec2[4] m_Samples;
+
+varying vec2 texCoord;
+
+float depthv;
+
+vec3 getPosition(in vec2 uv){
+ //Reconstruction from depth
+ depthv =texture2D(m_DepthTexture,uv).r;
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ //one frustum corner method
+ float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, uv.x);
+ float y = mix(-m_FrustumCorner.y, m_FrustumCorner.y, uv.y);
+
+ return depth* vec3(x, y, m_FrustumCorner.z);
+}
+
+vec3 getNormal(in vec2 uv){
+ return normalize(texture2D(m_Normals, uv).xyz * 2.0 - 1.0);
+}
+
+vec2 getRandom(in vec2 uv){
+ float rand=(fract(uv.x*(g_Resolution.x/2.0))*0.25)+(fract(uv.y*(g_Resolution.y/2.0))*0.5);
+ return normalize(vec2(rand,rand));
+}
+
+float doAmbientOcclusion(in vec2 tc, in vec3 pos, in vec3 norm){
+ vec3 diff = getPosition(tc)- pos;
+ vec3 v = normalize(diff);
+ float d = length(diff) * m_Scale;
+
+ return max(0.0, dot(norm, v) - m_Bias) * ( 1.0/(1.0 + d) ) * m_Intensity;
+}
+
+vec4 getColor(in float result){
+
+ if(m_UseOnlyAo){
+ return vec4(result,result,result, 1.0);
+ }
+ if(m_UseAo){
+ return texture2D(m_Texture,texCoord)* vec4(result,result,result, 1.0);
+ }else{
+ return texture2D(m_Texture,texCoord);
+ }
+
+}
+
+vec2 reflection(in vec2 v1,in vec2 v2){
+ vec2 result= 2.0 * dot(v2, v1) * v2;
+ result=v1-result;
+ return result;
+}
+
+
+//const vec2 vec[4] = vec2[4](vec2(1.0,0.0), vec2(-1.0,0.0), vec2(0.0,1.0), vec2(0.0,-1.0));
+void main(){
+
+ float result;
+
+ //vec2 vec[4] = { vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0) };
+ vec3 position = getPosition(texCoord);
+ //optimization, do not calculate AO if depth is 1
+ if(depthv==1.0){
+ gl_FragColor=getColor(1.0);
+ return;
+ }
+ vec3 normal = getNormal(texCoord);
+ vec2 rand = getRandom(texCoord);
+
+ float ao = 0.0;
+ float rad =m_SampleRadius / position.z;
+
+
+ int iterations = 4;
+ for (int j = 0; j < iterations; ++j){
+ vec2 coord1 = reflection(vec2(m_Samples[j]), vec2(rand)) * vec2(rad,rad);
+ vec2 coord2 = vec2(coord1.x* 0.707 - coord1.y* 0.707, coord1.x* 0.707 + coord1.y* 0.707) ;
+
+ ao += doAmbientOcclusion(texCoord + coord1.xy * 0.25, position, normal);
+ ao += doAmbientOcclusion(texCoord + coord2 * 0.50, position, normal);
+ ao += doAmbientOcclusion(texCoord + coord1.xy * 0.75, position, normal);
+ ao += doAmbientOcclusion(texCoord + coord2 * 1.00, position, normal);
+
+ }
+ ao /= float(iterations) * 4.0;
+ result = 1.0-ao;
+
+ gl_FragColor=getColor(result);
+
+//gl_FragColor=vec4(normal,1.0);
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/ssao.j3md b/engine/src/core-effects/Common/MatDefs/SSAO/ssao.j3md
new file mode 100644
index 0000000..762c1e5
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/ssao.j3md
@@ -0,0 +1,51 @@
+MaterialDef SSAO {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D RandomMap
+ Texture2D Normals
+ Texture2D DepthTexture
+ Vector3 FrustumCorner
+ Float SampleRadius
+ Float Intensity
+ Float Scale
+ Float Bias
+ Vector2 FrustumNearFar
+ Vector2Array Samples
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/SSAO/ssao15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+ Technique {
+ VertexShader GLSL120: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL120: Common/MatDefs/SSAO/ssao.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+
+ }
+ }
+
+
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/ssao15.frag b/engine/src/core-effects/Common/MatDefs/SSAO/ssao15.frag
new file mode 100644
index 0000000..de3b846
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/ssao15.frag
@@ -0,0 +1,96 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+uniform vec2 g_Resolution;
+uniform vec2 m_FrustumNearFar;
+uniform sampler2D m_Normals;
+uniform sampler2D m_RandomMap;
+uniform vec3 m_FrustumCorner;
+uniform float m_SampleRadius;
+uniform float m_Intensity;
+uniform float m_Scale;
+uniform float m_Bias;
+uniform vec2[4] m_Samples;
+
+in vec2 texCoord;
+
+float depthv;
+
+vec3 getPosition(in vec2 uv){
+ //Reconstruction from depth
+ depthv =getDepth(m_DepthTexture,uv).r;
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ //one frustum corner method
+ float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, uv.x);
+ float y = mix(-m_FrustumCorner.y, m_FrustumCorner.y, uv.y);
+
+ return depth* vec3(x, y, m_FrustumCorner.z);
+}
+
+vec3 getNormal(in vec2 uv){
+ return normalize(texture2D(m_Normals, uv).xyz * 2.0 - 1.0);
+}
+
+vec2 getRandom(in vec2 uv){
+ //float rand=(fract(uv.x*(g_Resolution.x/2.0))*0.25)+(fract(uv.y*(g_Resolution.y/2.0))*0.5);
+ vec4 rand=texture2D(m_RandomMap,g_Resolution * uv / 128.0 * 3.0)*2.0 -1.0;
+
+ return normalize(rand.xy);
+}
+
+float doAmbientOcclusion(in vec2 tc, in vec3 pos, in vec3 norm){
+ vec3 diff = getPosition(tc)- pos;
+ vec3 v = normalize(diff);
+ float d = length(diff) * m_Scale;
+
+ return max(0.0, dot(norm, v) - m_Bias) * ( 1.0/(1.0 + d) ) * m_Intensity;
+}
+
+
+vec2 reflection(in vec2 v1,in vec2 v2){
+ vec2 result= 2.0 * dot(v2, v1) * v2;
+ result=v1-result;
+ return result;
+}
+
+
+//const vec2 vec[4] = vec2[4](vec2(1.0,0.0), vec2(-1.0,0.0), vec2(0.0,1.0), vec2(0.0,-1.0));
+void main(){
+
+ float result;
+
+ //vec2 vec[4] = { vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0) };
+ vec3 position = getPosition(texCoord);
+ //optimization, do not calculate AO if depth is 1
+ if(depthv==1.0){
+ gl_FragColor=vec4(1.0);
+ return;
+ }
+ vec3 normal = getNormal(texCoord);
+ vec2 rand = getRandom(texCoord);
+
+ float ao = 0.0;
+ float rad =m_SampleRadius / position.z;
+
+
+ int iterations = 4;
+ for (int j = 0; j < iterations; ++j){
+ vec2 coord1 = reflection(vec2(m_Samples[j]), rand) * vec2(rad,rad);
+ vec2 coord2 = vec2(coord1.x* 0.707 - coord1.y* 0.707, coord1.x* 0.707 + coord1.y* 0.707) ;
+
+ ao += doAmbientOcclusion(texCoord + coord1.xy * 0.25, position, normal);
+ ao += doAmbientOcclusion(texCoord + coord2 * 0.50, position, normal);
+ ao += doAmbientOcclusion(texCoord + coord1.xy * 0.75, position, normal);
+ ao += doAmbientOcclusion(texCoord + coord2 * 1.00, position, normal);
+
+ }
+ ao /= float(iterations) * 4.0;
+ result = 1.0-ao;
+
+ gl_FragColor=vec4(result,result,result, 1.0);
+//gl_FragColor=vec4(depthv,depthv,depthv, 1.0);
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.frag b/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.frag
new file mode 100644
index 0000000..48f4a2f
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.frag
@@ -0,0 +1,159 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+uniform sampler2D m_SSAOMap;
+uniform vec2 g_Resolution;
+uniform bool m_UseOnlyAo;
+uniform bool m_UseAo;
+uniform float m_XScale;
+uniform float m_YScale;
+uniform vec2 m_FrustumNearFar;
+
+varying vec2 texCoord;
+
+vec4 getColor(vec4 color){
+
+
+ #ifdef USE_ONLY_AO
+ return color;
+ #endif
+ #ifdef USE_AO
+ return texture2D(m_Texture,texCoord)* color;
+ #endif
+
+ return texture2D(m_Texture,texCoord);
+
+}
+
+float readDepth(in vec2 uv){
+ float depthv =texture2D(m_DepthTexture,uv).r;
+ return (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+}
+
+ const float epsilon = 0.005;
+
+
+/*
+ const int kernelSize=7;
+
+ vec4 bilateralFilter() {
+ vec4 color = vec4(0.0);
+
+ vec2 sample;
+ float sum = 0.0;
+ float coefZ;
+ float Zp = readDepth(texCoord);
+
+ for(int i = -(kernelSize-1); i <= (kernelSize-1); i+=2) {
+ for(int j = -(kernelSize-1); j <= (kernelSize-1); j+=2) {
+ sample = texCoord + vec2(i,j) / g_Resolution;
+ float zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ sum += coefZ;
+
+ color += coefZ * texture2D(m_SSAOMap,sample);
+
+ }
+ }
+
+ return color / sum;
+ }
+*/
+
+ vec4 convolutionFilter(){
+ vec4 sum = vec4(0.0);
+
+ float x = texCoord.x;
+ float y = texCoord.y;
+
+ float xScale = m_XScale;
+ float yScale = m_YScale;
+
+ float zsum = 1.0;
+ float Zp =readDepth(texCoord);
+
+
+ vec2 sample = vec2(x - 2.0 * xScale, y - 2.0 * yScale);
+ float zTmp =readDepth(sample);
+ float coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 0.0 * xScale, y - 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 2.0 * xScale, y - 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 1.0 * xScale, y - 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 1.0 * xScale, y - 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 2.0 * xScale, y - 0.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 2.0 * xScale, y - 0.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 1.0 * xScale, y + 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 1.0 * xScale, y + 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 2.0 * xScale, y + 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 0.0 * xScale, y + 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 2.0 * xScale, y + 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+
+ return sum / zsum;
+ }
+
+
+ void main(){
+ // float depth =texture2D(m_DepthTexture,uv).r;
+
+ gl_FragColor=getColor(convolutionFilter());
+ // gl_FragColor=getColor(bilateralFilter());
+ // gl_FragColor=texture2D(m_SSAOMap,texCoord);
+
+ } \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.j3md b/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.j3md
new file mode 100644
index 0000000..dcc5e49
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur.j3md
@@ -0,0 +1,57 @@
+MaterialDef SSAOBlur {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D SSAOMap
+ Texture2D DepthTexture
+ Vector2 FrustumNearFar
+ Boolean UseAo
+ Boolean UseOnlyAo
+ Float XScale
+ Float YScale
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/SSAO/ssaoBlur15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ }
+
+ Defines {
+ USE_AO : UseAo
+ USE_ONLY_AO : UseOnlyAo
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+ Technique {
+ VertexShader GLSL120: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL120: Common/MatDefs/SSAO/ssaoBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+
+ }
+
+ Defines {
+ USE_AO : UseAo
+ USE_ONLY_AO : UseOnlyAo
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur15.frag b/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur15.frag
new file mode 100644
index 0000000..e90a76f
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/SSAO/ssaoBlur15.frag
@@ -0,0 +1,160 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+uniform sampler2D m_SSAOMap;
+uniform vec2 g_Resolution;
+uniform bool m_UseOnlyAo;
+uniform bool m_UseAo;
+uniform float m_XScale;
+uniform float m_YScale;
+uniform vec2 m_FrustumNearFar;
+
+in vec2 texCoord;
+
+vec4 getResult(vec4 color){
+
+ #ifdef USE_ONLY_AO
+ return color;
+ #endif
+ #ifdef USE_AO
+ return getColor(m_Texture,texCoord)* color;
+ #endif
+
+ return getColor(m_Texture,texCoord);
+
+}
+
+float readDepth(in vec2 uv){
+ float depthv =fetchTextureSample(m_DepthTexture,uv,0).r;
+ return (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+}
+
+ const float epsilon = 0.005;
+
+
+/*
+ const int kernelSize=7;
+
+ vec4 bilateralFilter() {
+ vec4 color = vec4(0.0);
+
+ vec2 sample;
+ float sum = 0.0;
+ float coefZ;
+ float Zp = readDepth(texCoord);
+
+ for(int i = -(kernelSize-1); i <= (kernelSize-1); i+=2) {
+ for(int j = -(kernelSize-1); j <= (kernelSize-1); j+=2) {
+ sample = texCoord + vec2(i,j) / g_Resolution;
+ float zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ sum += coefZ;
+
+ color += coefZ * texture2D(m_SSAOMap,sample);
+
+ }
+ }
+
+ return color / sum;
+ }
+*/
+
+ vec4 convolutionFilter(){
+ vec4 sum = vec4(0.0);
+
+ float x = texCoord.x;
+ float y = texCoord.y;
+
+ float xScale = m_XScale;
+ float yScale = m_YScale;
+
+ float zsum = 1.0;
+ float Zp =readDepth(texCoord);
+
+
+ vec2 sample = vec2(x - 2.0 * xScale, y - 2.0 * yScale);
+ float zTmp =readDepth(sample);
+ float coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 0.0 * xScale, y - 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 2.0 * xScale, y - 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 1.0 * xScale, y - 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 1.0 * xScale, y - 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 2.0 * xScale, y - 0.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 2.0 * xScale, y - 0.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 1.0 * xScale, y + 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 1.0 * xScale, y + 1.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 2.0 * xScale, y + 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x - 0.0 * xScale, y + 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+ sample = vec2(x + 2.0 * xScale, y + 2.0 * yScale);
+ zTmp =readDepth(sample);
+ coefZ = 1.0 / (epsilon + abs(Zp - zTmp));
+ zsum += coefZ;
+ sum += coefZ* texture2D( m_SSAOMap, sample);
+
+
+ return sum / zsum;
+ }
+
+
+ void main(){
+ // float depth =texture2D(m_DepthTexture,uv).r;
+
+ gl_FragColor=getResult(convolutionFilter());
+ // gl_FragColor=getResult(bilateralFilter());
+ // gl_FragColor=getColor(m_SSAOMap,texCoord);
+
+ } \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Water/SimpleWater.j3md b/engine/src/core-effects/Common/MatDefs/Water/SimpleWater.j3md
new file mode 100644
index 0000000..1288324
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/SimpleWater.j3md
@@ -0,0 +1,34 @@
+MaterialDef Simple Water {
+
+ MaterialParameters {
+ Texture2D water_reflection
+ Texture2D water_refraction
+ Texture2D water_depthmap
+ Texture2D water_normalmap
+ Texture2D water_dudvmap
+ Vector4 waterColor
+ Vector3 lightPos
+ Float time
+ Float waterDepth
+ Vector4 distortionScale
+ Vector4 distortionMix
+ Vector4 texScale
+ Vector2 FrustumNearFar
+ Float waterTransparency
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Water/simple_water.vert
+ FragmentShader GLSL100: Common/MatDefs/Water/simple_water.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ CameraPosition
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/caustics.jpg b/engine/src/core-effects/Common/MatDefs/Water/Textures/caustics.jpg
new file mode 100644
index 0000000..fb4f21c
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/caustics.jpg
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/dudv_map.jpg b/engine/src/core-effects/Common/MatDefs/Water/Textures/dudv_map.jpg
new file mode 100644
index 0000000..a2734bb
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/dudv_map.jpg
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/foam.jpg b/engine/src/core-effects/Common/MatDefs/Water/Textures/foam.jpg
new file mode 100644
index 0000000..fc17ac2
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/foam.jpg
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/foam2.jpg b/engine/src/core-effects/Common/MatDefs/Water/Textures/foam2.jpg
new file mode 100644
index 0000000..ef19b87
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/foam2.jpg
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/foam3.jpg b/engine/src/core-effects/Common/MatDefs/Water/Textures/foam3.jpg
new file mode 100644
index 0000000..8295b3f
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/foam3.jpg
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/heightmap.jpg b/engine/src/core-effects/Common/MatDefs/Water/Textures/heightmap.jpg
new file mode 100644
index 0000000..cfb5846
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/heightmap.jpg
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Textures/water_normalmap.dds b/engine/src/core-effects/Common/MatDefs/Water/Textures/water_normalmap.dds
new file mode 100644
index 0000000..13582e1
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Textures/water_normalmap.dds
Binary files differ
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Water.frag b/engine/src/core-effects/Common/MatDefs/Water/Water.frag
new file mode 100644
index 0000000..928ee22
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Water.frag
@@ -0,0 +1,402 @@
+// Water pixel shader
+// Copyright (C) JMonkeyEngine 3.0
+// by Remy Bouquet (nehon) for JMonkeyEngine 3.0
+// original HLSL version by Wojciech Toman 2009
+
+uniform sampler2D m_HeightMap;
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+uniform sampler2D m_NormalMap;
+uniform sampler2D m_FoamMap;
+uniform sampler2D m_CausticsMap;
+uniform sampler2D m_ReflectionMap;
+
+uniform mat4 m_ViewProjectionMatrixInverse;
+uniform mat4 m_TextureProjMatrix;
+uniform vec3 m_CameraPosition;
+
+uniform float m_WaterHeight;
+uniform float m_Time;
+uniform float m_WaterTransparency;
+uniform float m_NormalScale;
+uniform float m_R0;
+uniform float m_MaxAmplitude;
+uniform vec3 m_LightDir;
+uniform vec4 m_LightColor;
+uniform float m_ShoreHardness;
+uniform float m_FoamHardness;
+uniform float m_RefractionStrength;
+uniform vec3 m_FoamExistence;
+uniform vec3 m_ColorExtinction;
+uniform float m_Shininess;
+uniform vec4 m_WaterColor;
+uniform vec4 m_DeepWaterColor;
+uniform vec2 m_WindDirection;
+uniform float m_SunScale;
+uniform float m_WaveScale;
+uniform float m_UnderWaterFogDistance;
+uniform float m_CausticsIntensity;
+
+vec2 scale = vec2(m_WaveScale, m_WaveScale);
+float refractionScale = m_WaveScale;
+
+// Modifies 4 sampled normals. Increase first values to have more
+// smaller "waves" or last to have more bigger "waves"
+const vec4 normalModifier = vec4(3.0, 2.0, 4.0, 10.0);
+// Strength of displacement along normal.
+// Strength of displacement along normal.
+uniform float m_ReflectionDisplace;
+// Water transparency along eye vector.
+const float visibility = 3.0;
+// foam intensity
+uniform float m_FoamIntensity ;
+
+varying vec2 texCoord;
+
+mat3 MatrixInverse(in mat3 inMatrix){
+ float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]);
+ mat3 T = transpose(inMatrix);
+ return mat3(cross(T[1], T[2]),
+ cross(T[2], T[0]),
+ cross(T[0], T[1])) / det;
+}
+
+
+mat3 computeTangentFrame(in vec3 N, in vec3 P, in vec2 UV) {
+ vec3 dp1 = dFdx(P);
+ vec3 dp2 = dFdy(P);
+ vec2 duv1 = dFdx(UV);
+ vec2 duv2 = dFdy(UV);
+
+ // solve the linear system
+ mat3 M = mat3(dp1, dp2, cross(dp1, dp2));
+ //vec3 dp1xdp2 = cross(dp1, dp2);
+ mat3 inverseM = MatrixInverse(M);
+ //mat2x3 inverseM = mat2x3(cross(dp2, dp1xdp2), cross(dp1xdp2, dp1));
+
+ vec3 T = inverseM * vec3(duv1.x, duv2.x, 0.0);
+ vec3 B = inverseM * vec3(duv1.y, duv2.y, 0.0);
+
+ //vec3 T = inverseM * vec2(duv1.x, duv2.x);
+ //vec3 B = inverseM * vec2(duv1.y, duv2.y);
+
+ // construct tangent frame
+ float maxLength = max(length(T), length(B));
+ T = T / maxLength;
+ B = B / maxLength;
+
+ //vec3 tangent = normalize(T);
+ //vec3 binormal = normalize(B);
+
+ return mat3(T, B, N);
+}
+
+float saturate(in float val){
+ return clamp(val,0.0,1.0);
+}
+
+vec3 saturate(in vec3 val){
+ return clamp(val,vec3(0.0),vec3(1.0));
+}
+
+
+vec3 getPosition(in float depth, in vec2 uv){
+ vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
+ pos = m_ViewProjectionMatrixInverse * pos;
+ return pos.xyz / pos.w;
+}
+
+// Function calculating fresnel term.
+// - normal - normalized normal vector
+// - eyeVec - normalized eye vector
+float fresnelTerm(in vec3 normal,in vec3 eyeVec){
+ float angle = 1.0 - saturate(dot(normal, eyeVec));
+ float fresnel = angle * angle;
+ fresnel = fresnel * fresnel;
+ fresnel = fresnel * angle;
+ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength);
+}
+
+vec2 m_FrustumNearFar=vec2(1.0,m_UnderWaterFogDistance);
+const float LOG2 = 1.442695;
+
+vec4 underWater(){
+
+
+ float sceneDepth = texture2D(m_DepthTexture, texCoord).r;
+ vec3 color2 = texture2D(m_Texture, texCoord).rgb;
+
+ vec3 position = getPosition(sceneDepth, texCoord);
+ float level = m_WaterHeight;
+
+ vec3 eyeVec = position - m_CameraPosition;
+
+ // Find intersection with water surface
+ vec3 eyeVecNorm = normalize(eyeVec);
+ float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
+
+ vec2 texC = vec2(0.0);
+
+ float cameraDepth = length(m_CameraPosition - surfacePoint);
+ texC = (surfacePoint.xz + eyeVecNorm.xz) * scale + m_Time * 0.03 * m_WindDirection;
+ float bias = texture2D(m_HeightMap, texC).r;
+ level += bias * m_MaxAmplitude;
+ t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ surfacePoint = m_CameraPosition + eyeVecNorm * t;
+ eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
+
+ // Find normal of water surface
+ float normal1 = texture2D(m_HeightMap, (texC + vec2(-1.0, 0.0) / 256.0)).r;
+ float normal2 = texture2D(m_HeightMap, (texC + vec2(1.0, 0.0) / 256.0)).r;
+ float normal3 = texture2D(m_HeightMap, (texC + vec2(0.0, -1.0) / 256.0)).r;
+ float normal4 = texture2D(m_HeightMap, (texC + vec2(0.0, 1.0) / 256.0)).r;
+
+ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
+ vec3 normal = myNormal*-1.0;
+ float fresnel = fresnelTerm(normal, eyeVecNorm);
+
+ vec3 refraction = color2;
+ #ifdef ENABLE_REFRACTION
+ texC = texCoord.xy *sin (fresnel+1.0);
+ refraction = texture2D(m_Texture, texC).rgb;
+ #endif
+
+ float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
+ refraction = mix(mix(refraction, m_DeepWaterColor.rgb * waterCol, m_WaterTransparency), m_WaterColor.rgb* waterCol,m_WaterTransparency);
+
+ vec3 foam = vec3(0.0);
+ #ifdef ENABLE_FOAM
+ texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
+ vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
+
+ if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){
+ foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
+ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
+ }
+ foam *= m_LightColor.rgb;
+ #endif
+
+
+
+ vec3 specular = vec3(0.0);
+ vec3 color ;
+ float fogFactor;
+
+ if(position.y>level){
+ #ifdef ENABLE_SPECULAR
+ if(step(0.9999,sceneDepth)==1.0){
+ vec3 lightDir=normalize(m_LightDir);
+ vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
+ float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
+ specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
+ specular += specular * 25.0 * saturate(m_Shininess - 0.05);
+ specular=specular * m_LightColor.rgb * 100.0;
+ }
+ #endif
+ float fogIntensity= 8.0 * m_WaterTransparency;
+ fogFactor = exp2( -fogIntensity * fogIntensity * cameraDepth * 0.03 * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ color =mix(m_DeepWaterColor.rgb,refraction,fogFactor);
+ specular=specular*fogFactor;
+ color = saturate(color + max(specular, foam ));
+ }else{
+ vec3 caustics = vec3(0.0);
+ #ifdef ENABLE_CAUSTICS
+ vec2 windDirection=m_WindDirection;
+ texC = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.x) * 0.01;
+ vec2 texCoord2 = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.z) * 0.01;
+ caustics += (texture2D(m_CausticsMap, texC)+ texture2D(m_CausticsMap, texCoord2)).rgb;
+ caustics=saturate(mix(m_WaterColor.rgb,caustics,m_CausticsIntensity));
+ color=mix(color2,caustics,m_CausticsIntensity);
+ #else
+ color=color2;
+ #endif
+
+ float fogDepth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - sceneDepth* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+ float fogIntensity= 18 * m_WaterTransparency;
+ fogFactor = exp2( -fogIntensity * fogIntensity * fogDepth * fogDepth * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ color =mix(m_DeepWaterColor.rgb,color,fogFactor);
+ }
+
+ return vec4(color, 1.0);
+}
+
+void main(){
+ float sceneDepth = texture2D(m_DepthTexture, texCoord).r;
+ float isAtFarPlane = step(0.99998, sceneDepth);
+
+ vec3 color2 = texture2D(m_Texture, texCoord).rgb;
+ vec3 color = color2;
+
+ vec3 position = getPosition(sceneDepth,texCoord);
+
+ float level = m_WaterHeight;
+
+ // If we are underwater let's go to under water function
+ if(level >= m_CameraPosition.y){
+ gl_FragColor = underWater();
+ return ;
+ }
+
+ //#ifndef ENABLE_RIPPLES
+ // This optimization won't work on NVIDIA cards if ripples are enabled
+ if(position.y > level + m_MaxAmplitude + isAtFarPlane * 100.0){
+ gl_FragColor = vec4(color2, 1.0);
+ return;
+ }
+ //#endif
+
+ vec3 eyeVec = position - m_CameraPosition;
+ float diff = level - position.y;
+ float cameraDepth = m_CameraPosition.y - position.y;
+
+ // Find intersection with water surface
+ vec3 eyeVecNorm = normalize(eyeVec);
+ float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
+
+ vec2 texC;
+ int samples = 1;
+ #ifdef ENABLE_HQ_SHORELINE
+ samples = 10;
+ #endif
+ float biasFactor = 1.0/samples;
+ for (int i = 0; i < samples; i++){
+ texC = (surfacePoint.xz + eyeVecNorm.xz * biasFactor) * scale + m_Time * 0.03 * m_WindDirection;
+
+ float bias = texture2D(m_HeightMap, texC).r;
+
+ bias *= biasFactor;
+ level += bias * m_MaxAmplitude;
+ t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ surfacePoint = m_CameraPosition + eyeVecNorm * t;
+ }
+
+ float depth = length(position - surfacePoint);
+ float depth2 = surfacePoint.y - position.y;
+
+ // XXX: HACK ALERT: Increase water depth to infinity if at far plane
+ // Prevents "foam on horizon" issue
+ // For best results, replace the "100.0" below with the
+ // highest value in the m_ColorExtinction vec3
+ depth += isAtFarPlane * 100.0;
+ depth2 += isAtFarPlane * 100.0;
+
+ eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
+
+ // Find normal of water surface
+ float normal1 = texture2D(m_HeightMap, (texC + vec2(-1.0, 0.0) / 256.0)).r;
+ float normal2 = texture2D(m_HeightMap, (texC + vec2(1.0, 0.0) / 256.0)).r;
+ float normal3 = texture2D(m_HeightMap, (texC + vec2(0.0, -1.0) / 256.0)).r;
+ float normal4 = texture2D(m_HeightMap, (texC + vec2(0.0, 1.0) / 256.0)).r;
+
+ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
+ vec3 normal = vec3(0.0);
+
+ #ifdef ENABLE_RIPPLES
+ texC = surfacePoint.xz * 0.8 + m_WindDirection * m_Time* 1.6;
+ mat3 tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal0a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.4 + m_WindDirection * m_Time* 0.8;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal1a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.2 + m_WindDirection * m_Time * 0.4;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal2a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.1 + m_WindDirection * m_Time * 0.2;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal3a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ normal = normalize(normal0a * normalModifier.x + normal1a * normalModifier.y +normal2a * normalModifier.z + normal3a * normalModifier.w);
+ // XXX: Here's another way to fix the terrain edge issue,
+ // But it requires GLSL 1.3 and still looks kinda incorrect
+ // around edges
+ // To make the shader 1.2 compatible we use a trick :
+ // we clamp the x value of the normal and compare it to it's former value instead of using isnan.
+ normal = clamp(normal.x,0.0,1.0)!=normal.x ? myNormal : normal;
+ //if (position.y > level){
+ // gl_FragColor = vec4(color2 + normal*0.0001, 1.0);
+ // return;
+ //}
+ #else
+ normal = myNormal;
+ #endif
+
+ vec3 refraction = color2;
+ #ifdef ENABLE_REFRACTION
+ texC = texCoord.xy;
+ texC += sin(m_Time*1.8 + 3.0 * abs(position.y)) * (refractionScale * min(depth2, 1.0));
+ refraction = texture2D(m_Texture, texC).rgb;
+ #endif
+
+ vec3 waterPosition = surfacePoint.xyz;
+ waterPosition.y -= (level - m_WaterHeight);
+ vec4 texCoordProj = m_TextureProjMatrix * vec4(waterPosition, 1.0);
+
+ texCoordProj.x = texCoordProj.x + m_ReflectionDisplace * normal.x;
+ texCoordProj.z = texCoordProj.z + m_ReflectionDisplace * normal.z;
+ texCoordProj /= texCoordProj.w;
+ texCoordProj.y = 1.0 - texCoordProj.y;
+
+ vec3 reflection = texture2D(m_ReflectionMap, texCoordProj.xy).rgb;
+
+ float fresnel = fresnelTerm(normal, eyeVecNorm);
+
+ float depthN = depth * m_WaterTransparency;
+ float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
+ refraction = mix(mix(refraction, m_WaterColor.rgb * waterCol, saturate(depthN / visibility)),
+ m_DeepWaterColor.rgb * waterCol, saturate(depth2 / m_ColorExtinction));
+
+ vec3 foam = vec3(0.0);
+ #ifdef ENABLE_FOAM
+ texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
+ vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
+
+ if(depth2 < m_FoamExistence.x){
+ foam = (texture2D(m_FoamMap, texC).r + texture2D(m_FoamMap, texCoord2)).rgb * m_FoamIntensity;
+ }else if(depth2 < m_FoamExistence.y){
+ foam = mix((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity, vec4(0.0),
+ (depth2 - m_FoamExistence.x) / (m_FoamExistence.y - m_FoamExistence.x)).rgb;
+ }
+
+ if(m_MaxAmplitude - m_FoamExistence.z > 0.0001){
+ foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
+ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
+ }
+ foam *= m_LightColor.rgb;
+ #endif
+
+ vec3 specular =vec3(0.0);
+ #ifdef ENABLE_SPECULAR
+ vec3 lightDir=normalize(m_LightDir);
+ vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
+ float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
+ specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
+ specular += specular * 25.0 * saturate(m_Shininess - 0.05);
+ //foam does not shine
+ specular=specular * m_LightColor.rgb - (5.0 * foam);
+ #endif
+
+ color = mix(refraction, reflection, fresnel);
+ color = mix(refraction, color, saturate(depth * m_ShoreHardness));
+ color = saturate(color + max(specular, foam ));
+ color = mix(refraction, color, saturate(depth* m_FoamHardness));
+
+
+ // XXX: HACK ALERT:
+ // We trick the GeForces to think they have
+ // to calculate the derivatives for all these pixels by using step()!
+ // That way we won't get pixels around the edges of the terrain,
+ // Where the derivatives are undefined
+ if(position.y > level){
+ color = color2;
+ }
+
+ gl_FragColor = vec4(color,1.0);
+
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Water.j3md b/engine/src/core-effects/Common/MatDefs/Water/Water.j3md
new file mode 100644
index 0000000..160cadb
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Water.j3md
@@ -0,0 +1,90 @@
+MaterialDef Advanced Water {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D FoamMap
+ Texture2D CausticsMap
+ Texture2D NormalMap
+ Texture2D ReflectionMap
+ Texture2D HeightMap
+ Texture2D Texture
+ Texture2D DepthTexture
+ Vector3 CameraPosition
+ Float Time
+ Vector3 frustumCorner
+ Matrix4 TextureProjMatrix
+ Matrix4 ViewProjectionMatrixInverse
+ Float WaterHeight
+ Vector3 LightDir
+ Float WaterTransparency
+ Float NormalScale
+ Float R0
+ Float MaxAmplitude
+ Color LightColor
+ Float ShoreHardness
+ Float FoamHardness
+ Float RefractionStrength
+ Float WaveScale
+ Vector3 FoamExistence
+ Float SunScale
+ Vector3 ColorExtinction
+ Float Shininess
+ Color WaterColor
+ Color DeepWaterColor
+ Vector2 WindDirection
+ Float ReflectionDisplace
+ Float FoamIntensity
+ Float CausticsIntensity
+ Float UnderWaterFogDistance
+
+ Boolean UseRipples
+ Boolean UseHQShoreline
+ Boolean UseSpecular
+ Boolean UseFoam
+ Boolean UseCaustics
+ Boolean UseRefraction
+
+ }
+
+ Technique {
+ VertexShader GLSL150 : Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150 : Common/MatDefs/Water/Water15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ ENABLE_RIPPLES : UseRipples
+ ENABLE_HQ_SHORELINE : UseHQShoreline
+ ENABLE_SPECULAR : UseSpecular
+ ENABLE_FOAM : UseFoam
+ ENABLE_CAUSTICS : UseCaustics
+ ENABLE_REFRACTION : UseRefraction
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100 : Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL120 : Common/MatDefs/Water/Water.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ Defines {
+ ENABLE_RIPPLES : UseRipples
+ ENABLE_HQ_SHORELINE : UseHQShoreline
+ ENABLE_SPECULAR : UseSpecular
+ ENABLE_FOAM : UseFoam
+ ENABLE_CAUSTICS : UseCaustics
+ ENABLE_REFRACTION : UseRefraction
+
+ }
+ }
+
+ Technique FixedFunc {
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Water/Water15.frag b/engine/src/core-effects/Common/MatDefs/Water/Water15.frag
new file mode 100644
index 0000000..64cf280
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/Water15.frag
@@ -0,0 +1,419 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+// Water pixel shader
+// Copyright (C) JMonkeyEngine 3.0
+// by Remy Bouquet (nehon) for JMonkeyEngine 3.0
+// original HLSL version by Wojciech Toman 2009
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+
+uniform sampler2D m_HeightMap;
+uniform sampler2D m_NormalMap;
+uniform sampler2D m_FoamMap;
+uniform sampler2D m_CausticsMap;
+uniform sampler2D m_ReflectionMap;
+
+uniform mat4 m_ViewProjectionMatrixInverse;
+uniform mat4 m_TextureProjMatrix;
+uniform vec3 m_CameraPosition;
+
+uniform float m_WaterHeight;
+uniform float m_Time;
+uniform float m_WaterTransparency;
+uniform float m_NormalScale;
+uniform float m_R0;
+uniform float m_MaxAmplitude;
+uniform vec3 m_LightDir;
+uniform vec4 m_LightColor;
+uniform float m_ShoreHardness;
+uniform float m_FoamHardness;
+uniform float m_RefractionStrength;
+uniform vec3 m_FoamExistence;
+uniform vec3 m_ColorExtinction;
+uniform float m_Shininess;
+uniform vec4 m_WaterColor;
+uniform vec4 m_DeepWaterColor;
+uniform vec2 m_WindDirection;
+uniform float m_SunScale;
+uniform float m_WaveScale;
+uniform float m_UnderWaterFogDistance;
+uniform float m_CausticsIntensity;
+
+
+vec2 scale = vec2(m_WaveScale, m_WaveScale);
+float refractionScale = m_WaveScale;
+
+// Modifies 4 sampled normals. Increase first values to have more
+// smaller "waves" or last to have more bigger "waves"
+const vec4 normalModifier = vec4(3.0, 2.0, 4.0, 10.0);
+// Strength of displacement along normal.
+uniform float m_ReflectionDisplace;
+// Water transparency along eye vector.
+const float visibility = 3.0;
+// foam intensity
+uniform float m_FoamIntensity ;
+
+in vec2 texCoord;
+out vec4 outFragColor;
+
+mat3 MatrixInverse(in mat3 inMatrix){
+ float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]);
+ mat3 T = transpose(inMatrix);
+ return mat3(cross(T[1], T[2]),
+ cross(T[2], T[0]),
+ cross(T[0], T[1])) / det;
+}
+
+
+mat3 computeTangentFrame(in vec3 N, in vec3 P, in vec2 UV) {
+ vec3 dp1 = dFdx(P);
+ vec3 dp2 = dFdy(P);
+ vec2 duv1 = dFdx(UV);
+ vec2 duv2 = dFdy(UV);
+
+ // solve the linear system
+ vec3 dp1xdp2 = cross(dp1, dp2);
+ mat2x3 inverseM = mat2x3(cross(dp2, dp1xdp2), cross(dp1xdp2, dp1));
+
+ vec3 T = inverseM * vec2(duv1.x, duv2.x);
+ vec3 B = inverseM * vec2(duv1.y, duv2.y);
+
+ // construct tangent frame
+ float maxLength = max(length(T), length(B));
+ T = T / maxLength;
+ B = B / maxLength;
+
+ return mat3(T, B, N);
+}
+
+float saturate(in float val){
+ return clamp(val,0.0,1.0);
+}
+
+vec3 saturate(in vec3 val){
+ return clamp(val,vec3(0.0),vec3(1.0));
+}
+
+vec3 getPosition(in float depth, in vec2 uv){
+ vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
+ pos = m_ViewProjectionMatrixInverse * pos;
+ return pos.xyz / pos.w;
+}
+
+// Function calculating fresnel term.
+// - normal - normalized normal vector
+// - eyeVec - normalized eye vector
+float fresnelTerm(in vec3 normal,in vec3 eyeVec){
+ float angle = 1.0 - max(0.0, dot(normal, eyeVec));
+ float fresnel = angle * angle;
+ fresnel = fresnel * fresnel;
+ fresnel = fresnel * angle;
+ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength);
+}
+
+vec2 m_FrustumNearFar=vec2(1.0,m_UnderWaterFogDistance);
+const float LOG2 = 1.442695;
+
+vec4 underWater(int sampleNum){
+
+
+ float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r;
+ vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb;
+
+ vec3 position = getPosition(sceneDepth, texCoord);
+ float level = m_WaterHeight;
+
+ vec3 eyeVec = position - m_CameraPosition;
+
+ // Find intersection with water surface
+ vec3 eyeVecNorm = normalize(eyeVec);
+ float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
+
+ vec2 texC = vec2(0.0);
+
+ float cameraDepth = length(m_CameraPosition - surfacePoint);
+ texC = (surfacePoint.xz + eyeVecNorm.xz) * scale + m_Time * 0.03 * m_WindDirection;
+ float bias = texture(m_HeightMap, texC).r;
+ level += bias * m_MaxAmplitude;
+ t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ surfacePoint = m_CameraPosition + eyeVecNorm * t;
+ eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
+
+ // Find normal of water surface
+ float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1.0, 0.0)).r;
+ float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1.0, 0.0)).r;
+ float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0.0, -1.0)).r;
+ float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0.0, 1.0)).r;
+
+ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
+ vec3 normal = myNormal*-1.0;
+ float fresnel = fresnelTerm(normal, eyeVecNorm);
+
+ vec3 refraction = color2;
+ #ifdef ENABLE_REFRACTION
+ texC = texCoord.xy *sin (fresnel+1.0);
+ #ifdef RESOLVE_MS
+ ivec2 iTexC = ivec2(texC * textureSize(m_Texture));
+ refraction = texelFetch(m_Texture, iTexC, sampleNum).rgb;
+ #else
+ ivec2 iTexC = ivec2(texC * textureSize(m_Texture, 0));
+ refraction = texelFetch(m_Texture, iTexC, 0).rgb;
+ #endif
+ #endif
+
+ float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
+ refraction = mix(mix(refraction, m_DeepWaterColor.rgb * waterCol, m_WaterTransparency), m_WaterColor.rgb* waterCol,m_WaterTransparency);
+
+ vec3 foam = vec3(0.0);
+ #ifdef ENABLE_FOAM
+ texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
+ vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
+
+ if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){
+ foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
+ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
+ }
+ foam *= m_LightColor.rgb;
+ #endif
+
+
+
+ vec3 specular = vec3(0.0);
+ vec3 color ;
+ float fogFactor;
+
+ if(position.y>level){
+ #ifdef ENABLE_SPECULAR
+ if(step(0.9999,sceneDepth)==1.0){
+ vec3 lightDir=normalize(m_LightDir);
+ vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
+ float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
+ specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
+ specular += specular * 25.0 * saturate(m_Shininess - 0.05);
+ specular=specular * m_LightColor.rgb * 100.0;
+ }
+ #endif
+ float fogIntensity= 8.0 * m_WaterTransparency;
+ fogFactor = exp2( -fogIntensity * fogIntensity * cameraDepth * 0.03 * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ color =mix(m_DeepWaterColor.rgb,refraction,fogFactor);
+ specular=specular*fogFactor;
+ color = saturate(color + max(specular, foam ));
+ }else{
+ vec3 caustics = vec3(0.0);
+ #ifdef ENABLE_CAUSTICS
+ vec2 windDirection=m_WindDirection;
+ texC = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.x) * 0.01;
+ vec2 texCoord2 = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.z) * 0.01;
+ caustics += (texture2D(m_CausticsMap, texC)+ texture2D(m_CausticsMap, texCoord2)).rgb;
+ caustics=saturate(mix(m_WaterColor.rgb,caustics,m_CausticsIntensity));
+ color=mix(color2,caustics,m_CausticsIntensity);
+ #else
+ color=color2;
+ #endif
+
+ float fogDepth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - sceneDepth* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+ float fogIntensity= 18 * m_WaterTransparency;
+ fogFactor = exp2( -fogIntensity * fogIntensity * fogDepth * fogDepth * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ color =mix(m_DeepWaterColor.rgb,color,fogFactor);
+ }
+
+ return vec4(color, 1.0);
+}
+// NOTE: This will be called even for single-sampling
+vec4 main_multiSample(int sampleNum){
+ // If we are underwater let's call the underwater function
+ if(m_WaterHeight >= m_CameraPosition.y){
+
+ return underWater(sampleNum);
+ }
+
+ float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r;
+ vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb;
+
+ vec3 color = color2;
+ vec3 position = getPosition(sceneDepth, texCoord);
+
+ float level = m_WaterHeight;
+
+ float isAtFarPlane = step(0.99998, sceneDepth);
+ //#ifndef ENABLE_RIPPLES
+ // This optimization won't work on NVIDIA cards if ripples are enabled
+ if(position.y > level + m_MaxAmplitude + isAtFarPlane * 100.0){
+
+ return vec4(color2, 1.0);
+ }
+ //#endif
+
+ vec3 eyeVec = position - m_CameraPosition;
+ float cameraDepth = m_CameraPosition.y - position.y;
+
+ // Find intersection with water surface
+ vec3 eyeVecNorm = normalize(eyeVec);
+ float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
+
+ vec2 texC = vec2(0.0);
+ int samples = 1;
+ #ifdef ENABLE_HQ_SHORELINE
+ samples = 10;
+ #endif
+
+ float biasFactor = 1.0 / samples;
+ for (int i = 0; i < samples; i++){
+ texC = (surfacePoint.xz + eyeVecNorm.xz * biasFactor) * scale + m_Time * 0.03 * m_WindDirection;
+
+ float bias = texture(m_HeightMap, texC).r;
+
+ bias *= biasFactor;
+ level += bias * m_MaxAmplitude;
+ t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ surfacePoint = m_CameraPosition + eyeVecNorm * t;
+ }
+
+ float depth = length(position - surfacePoint);
+ float depth2 = surfacePoint.y - position.y;
+
+ // XXX: HACK ALERT: Increase water depth to infinity if at far plane
+ // Prevents "foam on horizon" issue
+ // For best results, replace the "100.0" below with the
+ // highest value in the m_ColorExtinction vec3
+ depth += isAtFarPlane * 100.0;
+ depth2 += isAtFarPlane * 100.0;
+
+ eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
+
+ // Find normal of water surface
+ float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1.0, 0.0)).r;
+ float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1.0, 0.0)).r;
+ float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0.0, -1.0)).r;
+ float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0.0, 1.0)).r;
+
+ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
+ vec3 normal = vec3(0.0);
+
+ #ifdef ENABLE_RIPPLES
+ texC = surfacePoint.xz * 0.8 + m_WindDirection * m_Time* 1.6;
+ mat3 tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal0a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.4 + m_WindDirection * m_Time* 0.8;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal1a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.2 + m_WindDirection * m_Time * 0.4;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal2a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.1 + m_WindDirection * m_Time * 0.2;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal3a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ normal = normalize(normal0a * normalModifier.x + normal1a * normalModifier.y +normal2a * normalModifier.z + normal3a * normalModifier.w);
+ // XXX: Here's another way to fix the terrain edge issue,
+ // But it requires GLSL 1.3 and still looks kinda incorrect
+ // around edges
+ normal = isnan(normal.x) ? myNormal : normal;
+ //if (position.y > level){
+ // gl_FragColor = vec4(color2 + normal*0.0001, 1.0);
+ // return;
+ //}
+ #else
+ normal = myNormal;
+ #endif
+
+ vec3 refraction = color2;
+ #ifdef ENABLE_REFRACTION
+ // texC = texCoord.xy+ m_ReflectionDisplace * normal.x;
+ texC = texCoord.xy;
+ texC += sin(m_Time*1.8 + 3.0 * abs(position.y)) * (refractionScale * min(depth2, 1.0));
+ #ifdef RESOLVE_MS
+ ivec2 iTexC = ivec2(texC * textureSize(m_Texture));
+ refraction = texelFetch(m_Texture, iTexC, sampleNum).rgb;
+ #else
+ ivec2 iTexC = ivec2(texC * textureSize(m_Texture, 0));
+ refraction = texelFetch(m_Texture, iTexC, 0).rgb;
+ #endif
+ #endif
+
+ vec3 waterPosition = surfacePoint.xyz;
+ waterPosition.y -= (level - m_WaterHeight);
+ vec4 texCoordProj = m_TextureProjMatrix * vec4(waterPosition, 1.0);
+
+ texCoordProj.x = texCoordProj.x + m_ReflectionDisplace * normal.x;
+ texCoordProj.z = texCoordProj.z + m_ReflectionDisplace * normal.z;
+ texCoordProj /= texCoordProj.w;
+ texCoordProj.y = 1.0 - texCoordProj.y;
+
+ vec3 reflection = texture(m_ReflectionMap, texCoordProj.xy).rgb;
+
+ float fresnel = fresnelTerm(normal, eyeVecNorm);
+
+ float depthN = depth * m_WaterTransparency;
+ float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
+ refraction = mix(mix(refraction, m_WaterColor.rgb * waterCol, saturate(depthN / visibility)),
+ m_DeepWaterColor.rgb * waterCol, saturate(depth2 / m_ColorExtinction));
+
+
+
+
+ vec3 foam = vec3(0.0);
+ #ifdef ENABLE_FOAM
+ texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
+ vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
+
+ if(depth2 < m_FoamExistence.x){
+ foam = (texture2D(m_FoamMap, texC).r + texture2D(m_FoamMap, texCoord2)).rgb * vec3(m_FoamIntensity);
+ }else if(depth2 < m_FoamExistence.y){
+ foam = mix((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity , vec4(0.0),
+ (depth2 - m_FoamExistence.x) / (m_FoamExistence.y - m_FoamExistence.x)).rgb;
+ }
+
+
+ if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){
+ foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
+ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
+ }
+ foam *= m_LightColor.rgb;
+ #endif
+
+ vec3 specular = vec3(0.0);
+ #ifdef ENABLE_SPECULAR
+ vec3 lightDir=normalize(m_LightDir);
+ vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
+ float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
+ specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
+ specular += specular * 25.0 * saturate(m_Shininess - 0.05);
+ //foam does not shine
+ specular=specular * m_LightColor.rgb - (5.0 * foam);
+ #endif
+
+ color = mix(refraction, reflection, fresnel);
+ color = mix(refraction, color, saturate(depth * m_ShoreHardness));
+ color = saturate(color + max(specular, foam ));
+ color = mix(refraction, color, saturate(depth* m_FoamHardness));
+
+
+ // XXX: HACK ALERT:
+ // We trick the GeForces to think they have
+ // to calculate the derivatives for all these pixels by using step()!
+ // That way we won't get pixels around the edges of the terrain,
+ // Where the derivatives are undefined
+ return vec4(mix(color, color2, step(level, position.y)), 1.0);
+}
+
+void main(){
+ #ifdef RESOLVE_MS
+ vec4 color = vec4(0.0);
+ for (int i = 0; i < m_NumSamples; i++){
+ color += main_multiSample(i);
+ }
+ outFragColor = color / m_NumSamples;
+ #else
+ outFragColor = main_multiSample(0);
+ #endif
+} \ No newline at end of file
diff --git a/engine/src/core-effects/Common/MatDefs/Water/simple_water.frag b/engine/src/core-effects/Common/MatDefs/Water/simple_water.frag
new file mode 100644
index 0000000..65a9c99
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/simple_water.frag
@@ -0,0 +1,126 @@
+/*
+GLSL conversion of Michael Horsch water demo
+http://www.bonzaisoftware.com/wfs.html
+Converted by Mars_999
+8/20/2005
+*/
+
+uniform sampler2D m_water_normalmap;
+uniform sampler2D m_water_reflection;
+uniform sampler2D m_water_refraction;
+uniform sampler2D m_water_dudvmap;
+uniform sampler2D m_water_depthmap;
+uniform vec4 m_waterColor;
+uniform float m_waterDepth;
+uniform vec4 m_distortionScale;
+uniform vec4 m_distortionMix;
+uniform vec4 m_texScale;
+uniform vec2 m_FrustumNearFar;
+uniform float m_waterTransparency;
+
+
+
+varying vec4 lightDir; //lightpos
+varying vec4 waterTex1; //moving texcoords
+varying vec4 waterTex2; //moving texcoords
+varying vec4 position; //for projection
+varying vec4 viewDir; //viewts
+varying vec4 viewLightDir;
+varying vec4 viewCamDir;
+
+//unit 0 = m_water_reflection
+//unit 1 = m_water_refraction
+//unit 2 = m_water_normalmap
+//unit 3 = m_water_dudvmap
+//unit 4 = m_water_depthmap
+
+ const vec4 two = vec4(2.0, 2.0, 2.0, 1.0);
+ const vec4 mone = vec4(-1.0, -1.0, -1.0, 1.0);
+
+ const vec4 ofive = vec4(0.5,0.5,0.5,1.0);
+
+ const float exponent = 64.0;
+
+float tangDot(in vec3 v1, in vec3 v2){
+ float d = dot(v1,v2);
+ #ifdef V_TANGENT
+ d = 1.0 - d*d;
+ return step(0.0, d) * sqrt(d);
+ #else
+ return d;
+ #endif
+}
+
+vec4 readDepth(vec2 uv){
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - texture2D(m_water_depthmap, uv).r* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+ return vec4( depth);
+}
+
+void main(void)
+{
+
+
+ vec4 lightTS = normalize(lightDir);
+ vec4 viewt = normalize(viewDir);
+ vec4 disdis = texture2D(m_water_dudvmap, vec2(waterTex2 * m_texScale));
+ vec4 fdist = texture2D(m_water_dudvmap, vec2(waterTex1 + disdis*m_distortionMix));
+ fdist =normalize( fdist * 2.0 - 1.0)* m_distortionScale;
+
+
+ //load normalmap
+ vec4 nmap = texture2D(m_water_normalmap, vec2(waterTex1 + disdis*m_distortionMix));
+ nmap = (nmap-ofive) * two;
+ // nmap = nmap*2.0-1.0;
+ vec4 vNorm = normalize(nmap);
+
+
+ vec4 projCoord = position / position.w;
+ projCoord =(projCoord+1.0)*0.5 + fdist;
+ projCoord = clamp(projCoord, 0.001, 0.999);
+
+ //load reflection,refraction and depth texture
+ vec4 refl = texture2D(m_water_reflection, vec2(projCoord.x,1.0-projCoord.y));
+ vec4 refr = texture2D(m_water_refraction, vec2(projCoord));
+ vec4 wdepth =readDepth(vec2(projCoord));
+
+ wdepth = vec4(pow(wdepth.x, m_waterDepth));
+ vec4 invdepth = 1.0 - wdepth;
+
+
+ // Blinn - Phong
+ // vec4 H = (viewt - lightTS);
+ // vec4 specular =vec4(pow(max(dot(H, vNorm), 0.0), exponent));
+
+// Standard Phong
+
+ // vec4 R =reflect(-L, vNorm);
+ // vec4 specular =vec4( pow(max(dot(R, E), 0.0),exponent));
+
+
+ //calculate specular highlight
+ vec4 L=normalize(viewLightDir);
+ vec4 E=normalize(viewCamDir);
+ vec4 vRef = normalize(reflect(-L,vNorm));
+ float stemp =max(0.0, dot( vRef,E) );
+ //initializing to 0 to avoid artifacts on old intel cards
+ vec4 specular = vec4(0.0,0.0,0.0,0.0);
+ if(stemp>0.0){
+ stemp = pow(stemp, exponent);
+ specular = vec4(stemp);
+ }
+
+
+
+ vec4 fresnelTerm = vec4(0.02+0.97*pow((1.0-dot(normalize(viewt), vNorm)),5.0));
+
+
+
+ fresnelTerm=fresnelTerm*invdepth*m_waterTransparency;
+ fresnelTerm=clamp(fresnelTerm,0.0,1.0);
+
+ refr*=(fresnelTerm);
+ refr *= invdepth;
+ refr= refr+ m_waterColor*wdepth*fresnelTerm;
+
+ gl_FragColor =(refr+ refl*(1.0-fresnelTerm))+specular;
+}
diff --git a/engine/src/core-effects/Common/MatDefs/Water/simple_water.vert b/engine/src/core-effects/Common/MatDefs/Water/simple_water.vert
new file mode 100644
index 0000000..e6052d8
--- /dev/null
+++ b/engine/src/core-effects/Common/MatDefs/Water/simple_water.vert
@@ -0,0 +1,87 @@
+/*
+GLSL conversion of Michael Horsch water demo
+http://www.bonzaisoftware.com/wfs.html
+Converted by Mars_999
+8/20/2005
+*/
+uniform vec3 m_lightPos;
+uniform float m_time;
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+uniform mat4 g_ViewMatrix;
+uniform vec3 g_CameraPosition;
+uniform mat3 g_NormalMatrix;
+
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inTangent;
+attribute vec3 inNormal;
+
+varying vec4 lightDir;
+varying vec4 waterTex1;
+varying vec4 waterTex2;
+varying vec4 position;
+varying vec4 viewDir;
+varying vec4 viewpos;
+varying vec4 viewLightDir;
+varying vec4 viewCamDir;
+
+
+//unit 0 = water_reflection
+//unit 1 = water_refraction
+//unit 2 = water_normalmap
+//unit 3 = water_dudvmap
+//unit 4 = water_depthmap
+
+void main(void)
+{
+ viewpos.x = g_CameraPosition.x;
+ viewpos.y = g_CameraPosition.y;
+ viewpos.z = g_CameraPosition.z;
+ viewpos.w = 1.0;
+
+ vec4 temp;
+ vec4 tangent = vec4(1.0, 0.0, 0.0, 0.0);
+ vec4 norm = vec4(0.0, 1.0, 0.0, 0.0);
+ vec4 binormal = vec4(0.0, 0.0, 1.0, 0.0);
+
+
+ temp = viewpos - inPosition;
+
+ viewDir.x = dot(temp, tangent);
+ viewDir.y = dot(temp, binormal);
+ viewDir.z = dot(temp, norm);
+ viewDir.w = 0.0;
+
+ temp = vec4(m_lightPos,1.0)- inPosition;
+ lightDir.x = dot(temp, tangent);
+ lightDir.y = dot(temp, binormal);
+ lightDir.z = dot(temp, norm);
+ lightDir.w = 0.0;
+
+ vec4 viewSpaceLightPos=g_ViewMatrix*vec4(m_lightPos,1.0);
+ vec4 viewSpacePos=g_WorldViewMatrix*inPosition;
+ vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
+ vec3 wvTangent = normalize(g_NormalMatrix * inTangent);
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+ mat3 tbnMat = mat3(wvTangent, wvBinormal, wvNormal);
+
+ temp = viewSpaceLightPos - viewSpacePos;
+ viewLightDir.xyz=temp.xyz*tbnMat;
+ viewLightDir.w = 0.0;
+
+ temp = -viewSpacePos;
+ viewCamDir.xyz =temp.xyz*tbnMat;
+ viewCamDir.w = 0.0;
+
+
+ vec4 t1 = vec4(0.0, -m_time, 0.0,0.0);
+ vec4 t2 = vec4(0.0, m_time, 0.0,0.0);
+
+ waterTex1 =vec4(inTexCoord,0.0,0.0) + t1;
+ waterTex2 =vec4(inTexCoord ,0.0,0.0)+ t2;
+
+ position = g_WorldViewProjectionMatrix * inPosition;
+ gl_Position = position;
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java b/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java
new file mode 100644
index 0000000..770e7f4
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.texture.Image.Format;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * BloomFilter is used to make objects in the scene have a glow effect.<br>
+ * There are 2 mode : Scene and Objects.<br>
+ * Scene mode extracts the bright parts of the scene to make them glow<br>
+ * Object mode make objects glow according to their material's glowMap or their GlowColor<br>
+ * @see <a href="http://jmonkeyengine.org/wiki/doku.php/jme3:advanced:bloom_and_glow">advanced:bloom_and_glow</a> for more details
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
+public class BloomFilter extends Filter {
+
+ /**
+ * GlowMode specifies if the glow will be applied to the whole scene,or to objects that have aglow color or a glow map
+ */
+ public enum GlowMode {
+
+ /**
+ * Apply bloom filter to bright areas in the scene.
+ */
+ Scene,
+ /**
+ * Apply bloom only to objects that have a glow map or a glow color.
+ */
+ Objects,
+ /**
+ * Apply bloom to both bright parts of the scene and objects with glow map.
+ */
+ SceneAndObjects;
+ }
+
+ private GlowMode glowMode = GlowMode.Scene;
+ //Bloom parameters
+ private float blurScale = 1.5f;
+ private float exposurePower = 5.0f;
+ private float exposureCutOff = 0.0f;
+ private float bloomIntensity = 2.0f;
+ private float downSamplingFactor = 1;
+ private Pass preGlowPass;
+ private Pass extractPass;
+ private Pass horizontalBlur = new Pass();
+ private Pass verticalalBlur = new Pass();
+ private Material extractMat;
+ private Material vBlurMat;
+ private Material hBlurMat;
+ private int screenWidth;
+ private int screenHeight;
+
+ /**
+ * Creates a Bloom filter
+ */
+ public BloomFilter() {
+ super("BloomFilter");
+ }
+
+ /**
+ * Creates the bloom filter with the specific glow mode
+ * @param glowMode
+ */
+ public BloomFilter(GlowMode glowMode) {
+ this();
+ this.glowMode = glowMode;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ screenWidth = (int) Math.max(1, (w / downSamplingFactor));
+ screenHeight = (int) Math.max(1, (h / downSamplingFactor));
+ // System.out.println(screenWidth + " " + screenHeight);
+ if (glowMode != GlowMode.Scene) {
+ preGlowPass = new Pass();
+ preGlowPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth);
+ }
+
+ postRenderPasses = new ArrayList<Pass>();
+ //configuring extractPass
+ extractMat = new Material(manager, "Common/MatDefs/Post/BloomExtract.j3md");
+ extractPass = new Pass() {
+
+ @Override
+ public boolean requiresSceneAsTexture() {
+ return true;
+ }
+
+ @Override
+ public void beforeRender() {
+ extractMat.setFloat("ExposurePow", exposurePower);
+ extractMat.setFloat("ExposureCutoff", exposureCutOff);
+ if (glowMode != GlowMode.Scene) {
+ extractMat.setTexture("GlowMap", preGlowPass.getRenderedTexture());
+ }
+ extractMat.setBoolean("Extract", glowMode != GlowMode.Objects);
+ }
+ };
+
+ extractPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, extractMat);
+ postRenderPasses.add(extractPass);
+
+ //configuring horizontal blur pass
+ hBlurMat = new Material(manager, "Common/MatDefs/Blur/HGaussianBlur.j3md");
+ horizontalBlur = new Pass() {
+
+ @Override
+ public void beforeRender() {
+ hBlurMat.setTexture("Texture", extractPass.getRenderedTexture());
+ hBlurMat.setFloat("Size", screenWidth);
+ hBlurMat.setFloat("Scale", blurScale);
+ }
+ };
+
+ horizontalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, hBlurMat);
+ postRenderPasses.add(horizontalBlur);
+
+ //configuring vertical blur pass
+ vBlurMat = new Material(manager, "Common/MatDefs/Blur/VGaussianBlur.j3md");
+ verticalalBlur = new Pass() {
+
+ @Override
+ public void beforeRender() {
+ vBlurMat.setTexture("Texture", horizontalBlur.getRenderedTexture());
+ vBlurMat.setFloat("Size", screenHeight);
+ vBlurMat.setFloat("Scale", blurScale);
+ }
+ };
+
+ verticalalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, vBlurMat);
+ postRenderPasses.add(verticalalBlur);
+
+
+ //final material
+ material = new Material(manager, "Common/MatDefs/Post/BloomFinal.j3md");
+ material.setTexture("BloomTex", verticalalBlur.getRenderedTexture());
+ }
+
+
+ @Override
+ protected Material getMaterial() {
+ material.setFloat("BloomIntensity", bloomIntensity);
+ return material;
+ }
+
+ @Override
+ protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
+ if (glowMode != GlowMode.Scene) {
+ renderManager.getRenderer().setBackgroundColor(ColorRGBA.BlackNoAlpha);
+ renderManager.getRenderer().setFrameBuffer(preGlowPass.getRenderFrameBuffer());
+ renderManager.getRenderer().clearBuffers(true, true, true);
+ renderManager.setForcedTechnique("Glow");
+ renderManager.renderViewPortQueues(viewPort, false);
+ renderManager.setForcedTechnique(null);
+ renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
+ }
+ }
+
+ /**
+ * returns the bloom intensity
+ * @return
+ */
+ public float getBloomIntensity() {
+ return bloomIntensity;
+ }
+
+ /**
+ * intensity of the bloom effect default is 2.0
+ * @param bloomIntensity
+ */
+ public void setBloomIntensity(float bloomIntensity) {
+ this.bloomIntensity = bloomIntensity;
+ }
+
+ /**
+ * returns the blur scale
+ * @return
+ */
+ public float getBlurScale() {
+ return blurScale;
+ }
+
+ /**
+ * sets The spread of the bloom default is 1.5f
+ * @param blurScale
+ */
+ public void setBlurScale(float blurScale) {
+ this.blurScale = blurScale;
+ }
+
+ /**
+ * returns the exposure cutoff<br>
+ * for more details see {@link setExposureCutOff(float exposureCutOff)}
+ * @return
+ */
+ public float getExposureCutOff() {
+ return exposureCutOff;
+ }
+
+ /**
+ * Define the color threshold on which the bloom will be applied (0.0 to 1.0)
+ * @param exposureCutOff
+ */
+ public void setExposureCutOff(float exposureCutOff) {
+ this.exposureCutOff = exposureCutOff;
+ }
+
+ /**
+ * returns the exposure power<br>
+ * form more details see {@link setExposurePower(float exposurePower)}
+ * @return
+ */
+ public float getExposurePower() {
+ return exposurePower;
+ }
+
+ /**
+ * defines how many time the bloom extracted color will be multiplied by itself. default id 5.0<br>
+ * a high value will reduce rough edges in the bloom and somhow the range of the bloom area *
+ * @param exposurePower
+ */
+ public void setExposurePower(float exposurePower) {
+ this.exposurePower = exposurePower;
+ }
+
+ /**
+ * returns the downSampling factor<br>
+ * form more details see {@link setDownSamplingFactor(float downSamplingFactor)}
+ * @return
+ */
+ public float getDownSamplingFactor() {
+ return downSamplingFactor;
+ }
+
+ /**
+ * Sets the downSampling factor : the size of the computed texture will be divided by this factor. default is 1 for no downsampling
+ * A 2 value is a good way of widening the blur
+ * @param downSamplingFactor
+ */
+ public void setDownSamplingFactor(float downSamplingFactor) {
+ this.downSamplingFactor = downSamplingFactor;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(glowMode, "glowMode", GlowMode.Scene);
+ oc.write(blurScale, "blurScale", 1.5f);
+ oc.write(exposurePower, "exposurePower", 5.0f);
+ oc.write(exposureCutOff, "exposureCutOff", 0.0f);
+ oc.write(bloomIntensity, "bloomIntensity", 2.0f);
+ oc.write(downSamplingFactor, "downSamplingFactor", 1);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ glowMode = ic.readEnum("glowMode", GlowMode.class, GlowMode.Scene);
+ blurScale = ic.readFloat("blurScale", 1.5f);
+ exposurePower = ic.readFloat("exposurePower", 5.0f);
+ exposureCutOff = ic.readFloat("exposureCutOff", 0.0f);
+ bloomIntensity = ic.readFloat("bloomIntensity", 2.0f);
+ downSamplingFactor = ic.readFloat("downSamplingFactor", 1);
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/CartoonEdgeFilter.java b/engine/src/core-effects/com/jme3/post/filters/CartoonEdgeFilter.java
new file mode 100644
index 0000000..3e9998b
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/CartoonEdgeFilter.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.post.Filter.Pass;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.texture.Image.Format;
+
+/**
+ * Applies a cartoon-style edge detection filter to all objects in the scene.
+ *
+ * @author Kirill Vainer
+ */
+public class CartoonEdgeFilter extends Filter {
+
+ private Pass normalPass;
+ private float edgeWidth = 1.0f;
+ private float edgeIntensity = 1.0f;
+ private float normalThreshold = 0.5f;
+ private float depthThreshold = 0.1f;
+ private float normalSensitivity = 1.0f;
+ private float depthSensitivity = 10.0f;
+ private ColorRGBA edgeColor = new ColorRGBA(0, 0, 0, 1);
+
+ /**
+ * Creates a CartoonEdgeFilter
+ */
+ public CartoonEdgeFilter() {
+ super("CartoonEdgeFilter");
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
+ Renderer r = renderManager.getRenderer();
+ r.setFrameBuffer(normalPass.getRenderFrameBuffer());
+ renderManager.getRenderer().clearBuffers(true, true, true);
+ renderManager.setForcedTechnique("PreNormalPass");
+ renderManager.renderViewPortQueues(viewPort, false);
+ renderManager.setForcedTechnique(null);
+ renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
+ }
+
+ @Override
+ protected Material getMaterial() {
+ material.setTexture("NormalsTexture", normalPass.getRenderedTexture());
+ return material;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ normalPass = new Pass();
+ normalPass.init(renderManager.getRenderer(), w, h, Format.RGBA8, Format.Depth);
+ material = new Material(manager, "Common/MatDefs/Post/CartoonEdge.j3md");
+ material.setFloat("EdgeWidth", edgeWidth);
+ material.setFloat("EdgeIntensity", edgeIntensity);
+ material.setFloat("NormalThreshold", normalThreshold);
+ material.setFloat("DepthThreshold", depthThreshold);
+ material.setFloat("NormalSensitivity", normalSensitivity);
+ material.setFloat("DepthSensitivity", depthSensitivity);
+ material.setColor("EdgeColor", edgeColor);
+ }
+
+ /**
+ * Return the depth sensitivity<br>
+ * for more details see {@link setDepthSensitivity(float depthSensitivity)}
+ * @return
+ */
+ public float getDepthSensitivity() {
+ return depthSensitivity;
+ }
+
+ /**
+ * sets the depth sensitivity<br>
+ * defines how much depth will influence edges, default is 10
+ * @param depthSensitivity
+ */
+ public void setDepthSensitivity(float depthSensitivity) {
+ this.depthSensitivity = depthSensitivity;
+ if (material != null) {
+ material.setFloat("DepthSensitivity", depthSensitivity);
+ }
+ }
+
+ /**
+ * returns the depth threshold<br>
+ * for more details see {@link setDepthThreshold(float depthThreshold)}
+ * @return
+ */
+ public float getDepthThreshold() {
+ return depthThreshold;
+ }
+
+ /**
+ * sets the depth threshold<br>
+ * Defines at what threshold of difference of depth an edge is outlined default is 0.1f
+ * @param depthThreshold
+ */
+ public void setDepthThreshold(float depthThreshold) {
+ this.depthThreshold = depthThreshold;
+ if (material != null) {
+ material.setFloat("DepthThreshold", depthThreshold);
+ }
+ }
+
+ /**
+ * returns the edge intensity<br>
+ * for more details see {@link setEdgeIntensity(float edgeIntensity) }
+ * @return
+ */
+ public float getEdgeIntensity() {
+ return edgeIntensity;
+ }
+
+ /**
+ * sets the edge intensity<br>
+ * Defineshow visilble will be the outlined edges
+ * @param edgeIntensity
+ */
+ public void setEdgeIntensity(float edgeIntensity) {
+ this.edgeIntensity = edgeIntensity;
+ if (material != null) {
+ material.setFloat("EdgeIntensity", edgeIntensity);
+ }
+ }
+
+ /**
+ * returns the width of the edges
+ * @return
+ */
+ public float getEdgeWidth() {
+ return edgeWidth;
+ }
+
+ /**
+ * sets the witdh of the edge in pixels default is 1
+ * @param edgeWidth
+ */
+ public void setEdgeWidth(float edgeWidth) {
+ this.edgeWidth = edgeWidth;
+ if (material != null) {
+ material.setFloat("EdgeWidth", edgeWidth);
+ }
+
+ }
+
+ /**
+ * returns the normals sensitivity<br>
+ * form more details see {@link setNormalSensitivity(float normalSensitivity)}
+ * @return
+ */
+ public float getNormalSensitivity() {
+ return normalSensitivity;
+ }
+
+ /**
+ * sets the normals sensitivity default is 1
+ * @param normalSensitivity
+ */
+ public void setNormalSensitivity(float normalSensitivity) {
+ this.normalSensitivity = normalSensitivity;
+ if (material != null) {
+ material.setFloat("NormalSensitivity", normalSensitivity);
+ }
+ }
+
+ /**
+ * returns the normal threshold<br>
+ * for more details see {@link setNormalThreshold(float normalThreshold)}
+ *
+ * @return
+ */
+ public float getNormalThreshold() {
+ return normalThreshold;
+ }
+
+ /**
+ * sets the normal threshold default is 0.5
+ * @param normalThreshold
+ */
+ public void setNormalThreshold(float normalThreshold) {
+ this.normalThreshold = normalThreshold;
+ if (material != null) {
+ material.setFloat("NormalThreshold", normalThreshold);
+ }
+ }
+
+ /**
+ * returns the edge color
+ * @return
+ */
+ public ColorRGBA getEdgeColor() {
+ return edgeColor;
+ }
+
+ /**
+ * Sets the edge color, default is black
+ * @param edgeColor
+ */
+ public void setEdgeColor(ColorRGBA edgeColor) {
+ this.edgeColor = edgeColor;
+ if (material != null) {
+ material.setColor("EdgeColor", edgeColor);
+ }
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/ColorOverlayFilter.java b/engine/src/core-effects/com/jme3/post/filters/ColorOverlayFilter.java
new file mode 100644
index 0000000..a7f30f8
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/ColorOverlayFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import java.io.IOException;
+
+/**
+ * This filter simply multiply the whole scene by a color
+ * @author Rémy Bouquet aka Nehon
+ */
+public class ColorOverlayFilter extends Filter {
+
+ private ColorRGBA color = ColorRGBA.White;
+
+ /**
+ * creates a colorOverlayFilter with a white coor (transparent)
+ */
+ public ColorOverlayFilter() {
+ super("Color Overlay");
+ }
+
+ /**
+ * creates a colorOverlayFilter with the given color
+ * @param color
+ */
+ public ColorOverlayFilter(ColorRGBA color) {
+ this();
+ this.color = color;
+ }
+
+ @Override
+ protected Material getMaterial() {
+
+ material.setColor("Color", color);
+ return material;
+ }
+
+ /**
+ * returns the color
+ * @return color
+ */
+ public ColorRGBA getColor() {
+ return color;
+ }
+
+ /**
+ * sets the color
+ * @param color
+ */
+ public void setColor(ColorRGBA color) {
+ this.color = color;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/Overlay.j3md");
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(color, "color", ColorRGBA.White);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ color = (ColorRGBA) ic.readSavable("color", ColorRGBA.White);
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/CrossHatchFilter.java b/engine/src/core-effects/com/jme3/post/filters/CrossHatchFilter.java
new file mode 100644
index 0000000..699a252
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/CrossHatchFilter.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+
+/**
+ * A Post Processing filter that makes the screen look like it was drawn as
+ * diagonal lines with a pen.
+ * Try combining this with a cartoon edge filter to obtain manga style visuals.
+ *
+ * Based on an article from Geeks3D:
+ * <a href="http://www.geeks3d.com/20110219/shader-library-crosshatching-glsl-filter/" rel="nofollow">http://www.geeks3d.com/20110219/shader-library-crosshatching-glsl-filter/</a>
+ *
+ * @author Roy Straver a.k.a. Baal Garnaal
+ */
+public class CrossHatchFilter extends Filter {
+
+ private ColorRGBA lineColor = ColorRGBA.Black.clone();
+ private ColorRGBA paperColor = ColorRGBA.White.clone();
+ private float colorInfluenceLine = 0.8f;
+ private float colorInfluencePaper = 0.1f;
+ private float fillValue = 0.9f;
+ private float luminance1 = 0.9f;
+ private float luminance2 = 0.7f;
+ private float luminance3 = 0.5f;
+ private float luminance4 = 0.3f;
+ private float luminance5 = 0.0f;
+ private float lineThickness = 1.0f;
+ private float lineDistance = 4.0f;
+
+ /**
+ * Creates a crossHatch filter
+ */
+ public CrossHatchFilter() {
+ super("CrossHatchFilter");
+ }
+
+ /**
+ * Creates a crossHatch filter
+ * @param lineColor the colors of the lines
+ * @param paperColor the paper color
+ */
+ public CrossHatchFilter(ColorRGBA lineColor, ColorRGBA paperColor) {
+ this();
+ this.lineColor = lineColor;
+ this.paperColor = paperColor;
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return false;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/CrossHatch.j3md");
+ material.setColor("LineColor", lineColor);
+ material.setColor("PaperColor", paperColor);
+
+ material.setFloat("ColorInfluenceLine", colorInfluenceLine);
+ material.setFloat("ColorInfluencePaper", colorInfluencePaper);
+
+ material.setFloat("FillValue", fillValue);
+
+ material.setFloat("Luminance1", luminance1);
+ material.setFloat("Luminance2", luminance2);
+ material.setFloat("Luminance3", luminance3);
+ material.setFloat("Luminance4", luminance4);
+ material.setFloat("Luminance5", luminance5);
+
+ material.setFloat("LineThickness", lineThickness);
+ material.setFloat("LineDistance", lineDistance);
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ /**
+ * Sets color used to draw lines
+ * @param lineColor
+ */
+ public void setLineColor(ColorRGBA lineColor) {
+ this.lineColor = lineColor;
+ if (material != null) {
+ material.setColor("LineColor", lineColor);
+ }
+ }
+
+ /**
+ * Sets color used as background
+ * @param paperColor
+ */
+ public void setPaperColor(ColorRGBA paperColor) {
+ this.paperColor = paperColor;
+ if (material != null) {
+ material.setColor("PaperColor", paperColor);
+ }
+ }
+
+ /**
+ * Sets color influence of original image on lines drawn
+ * @param colorInfluenceLine
+ */
+ public void setColorInfluenceLine(float colorInfluenceLine) {
+ this.colorInfluenceLine = colorInfluenceLine;
+ if (material != null) {
+ material.setFloat("ColorInfluenceLine", colorInfluenceLine);
+ }
+ }
+
+ /**
+ * Sets color influence of original image on non-line areas
+ * @param colorInfluencePaper
+ */
+ public void setColorInfluencePaper(float colorInfluencePaper) {
+ this.colorInfluencePaper = colorInfluencePaper;
+ if (material != null) {
+ material.setFloat("ColorInfluencePaper", colorInfluencePaper);
+ }
+ }
+
+ /**
+ * Sets line/paper color ratio for areas with values < luminance5,
+ * really dark areas get no lines but a filled blob instead
+ * @param fillValue
+ */
+ public void setFillValue(float fillValue) {
+ this.fillValue = fillValue;
+ if (material != null) {
+ material.setFloat("FillValue", fillValue);
+ }
+ }
+
+ /**
+ *
+ * Sets minimum luminance levels for lines drawn
+ * @param luminance1 Top-left to down right 1
+ * @param luminance2 Top-right to bottom left 1
+ * @param luminance3 Top-left to down right 2
+ * @param luminance4 Top-right to bottom left 2
+ * @param luminance5 Blobs
+ */
+ public void setLuminanceLevels(float luminance1, float luminance2, float luminance3, float luminance4, float luminance5) {
+ this.luminance1 = luminance1;
+ this.luminance2 = luminance2;
+ this.luminance3 = luminance3;
+ this.luminance4 = luminance4;
+ this.luminance5 = luminance5;
+
+ if (material != null) {
+ material.setFloat("Luminance1", luminance1);
+ material.setFloat("Luminance2", luminance2);
+ material.setFloat("Luminance3", luminance3);
+ material.setFloat("Luminance4", luminance4);
+ material.setFloat("Luminance5", luminance5);
+ }
+ }
+
+ /**
+ * Sets the thickness of lines drawn
+ * @param lineThickness
+ */
+ public void setLineThickness(float lineThickness) {
+ this.lineThickness = lineThickness;
+ if (material != null) {
+ material.setFloat("LineThickness", lineThickness);
+ }
+ }
+
+ /**
+ * Sets minimum distance between lines drawn
+ * Primary lines are drawn at 2*lineDistance
+ * Secondary lines are drawn at lineDistance
+ * @param lineDistance
+ */
+ public void setLineDistance(float lineDistance) {
+ this.lineDistance = lineDistance;
+ if (material != null) {
+ material.setFloat("LineDistance", lineDistance);
+ }
+ }
+
+ /**
+ * Returns line color
+ * @return
+ */
+ public ColorRGBA getLineColor() {
+ return lineColor;
+ }
+
+ /**
+ * Returns paper background color
+ * @return
+ */
+ public ColorRGBA getPaperColor() {
+ return paperColor;
+ }
+
+ /**
+ * Returns current influence of image colors on lines
+ */
+ public float getColorInfluenceLine() {
+ return colorInfluenceLine;
+ }
+
+ /**
+ * Returns current influence of image colors on paper background
+ */
+ public float getColorInfluencePaper() {
+ return colorInfluencePaper;
+ }
+
+ /**
+ * Returns line/paper color ratio for blobs
+ */
+ public float getFillValue() {
+ return fillValue;
+ }
+
+ /**
+ * Returns the thickness of the lines drawn
+ */
+ public float getLineThickness() {
+ return lineThickness;
+ }
+
+ /**
+ * Returns minimum distance between lines
+ */
+ public float getLineDistance() {
+ return lineDistance;
+ }
+
+ /**
+ * Returns treshold for lines 1
+ */
+ public float getLuminance1() {
+ return luminance1;
+ }
+
+ /**
+ * Returns treshold for lines 2
+ */
+ public float getLuminance2() {
+ return luminance2;
+ }
+
+ /**
+ * Returns treshold for lines 3
+ */
+ public float getLuminance3() {
+ return luminance3;
+ }
+
+ /**
+ * Returns treshold for lines 4
+ */
+ public float getLuminance4() {
+ return luminance4;
+ }
+
+ /**
+ * Returns treshold for blobs
+ */
+ public float getLuminance5() {
+ return luminance5;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/com/jme3/post/filters/DepthOfFieldFilter.java b/engine/src/core-effects/com/jme3/post/filters/DepthOfFieldFilter.java
new file mode 100644
index 0000000..55591c9
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/DepthOfFieldFilter.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+
+/**
+ * A post-processing filter that performs a depth range
+ * blur using a scaled convolution filter.
+ *
+ * @version $Revision: 779 $
+ * @author Paul Speed
+ */
+public class DepthOfFieldFilter extends Filter {
+
+ private float focusDistance = 50f;
+ private float focusRange = 10f;
+ private float blurScale = 1f;
+ // These values are set internally based on the
+ // viewport size.
+ private float xScale;
+ private float yScale;
+
+ /**
+ * Creates a DepthOfField filter
+ */
+ public DepthOfFieldFilter() {
+ super("Depth Of Field");
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected Material getMaterial() {
+
+ return material;
+ }
+
+ @Override
+ protected void initFilter(AssetManager assets, RenderManager renderManager,
+ ViewPort vp, int w, int h) {
+ material = new Material(assets, "Common/MatDefs/Post/DepthOfField.j3md");
+ material.setFloat("FocusDistance", focusDistance);
+ material.setFloat("FocusRange", focusRange);
+
+
+ xScale = 1.0f / w;
+ yScale = 1.0f / h;
+
+ material.setFloat("XScale", blurScale * xScale);
+ material.setFloat("YScale", blurScale * yScale);
+ }
+
+ /**
+ * Sets the distance at which objects are purely in focus.
+ */
+ public void setFocusDistance(float f) {
+
+ this.focusDistance = f;
+ if (material != null) {
+ material.setFloat("FocusDistance", focusDistance);
+ }
+
+ }
+
+ /**
+ * returns the focus distance
+ * @return
+ */
+ public float getFocusDistance() {
+ return focusDistance;
+ }
+
+ /**
+ * Sets the range to either side of focusDistance where the
+ * objects go gradually out of focus. Less than focusDistance - focusRange
+ * and greater than focusDistance + focusRange, objects are maximally "blurred".
+ */
+ public void setFocusRange(float f) {
+ this.focusRange = f;
+ if (material != null) {
+ material.setFloat("FocusRange", focusRange);
+ }
+
+ }
+
+ /**
+ * returns the focus range
+ * @return
+ */
+ public float getFocusRange() {
+ return focusRange;
+ }
+
+ /**
+ * Sets the blur amount by scaling the convolution filter up or
+ * down. A value of 1 (the default) performs a sparse 5x5 evenly
+ * distribubted convolution at pixel level accuracy. Higher values skip
+ * more pixels, and so on until you are no longer blurring the image
+ * but simply hashing it.
+ *
+ * The sparse convolution is as follows:
+ *%MINIFYHTMLc3d0cd9fab65de6875a381fd3f83e1b338%*
+ * Where 'x' is the texel being modified. Setting blur scale higher
+ * than 1 spaces the samples out.
+ */
+ public void setBlurScale(float f) {
+ this.blurScale = f;
+ if (material != null) {
+ material.setFloat("XScale", blurScale * xScale);
+ material.setFloat("YScale", blurScale * yScale);
+ }
+ }
+
+ /**
+ * returns the blur scale
+ * @return
+ */
+ public float getBlurScale() {
+ return blurScale;
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/FXAAFilter.java b/engine/src/core-effects/com/jme3/post/filters/FXAAFilter.java
new file mode 100644
index 0000000..8ba3c16
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/FXAAFilter.java
@@ -0,0 +1,95 @@
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+
+/**
+ * <a href="http://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/" rel="nofollow">http://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-<span class="domtooltips" title="OpenGL (Open Graphics Library) is a standard specification defining a cross-language, cross-platform API for writing applications that produce 2D and 3D computer graphics." id="domtooltipsspan11">opengl</span>-test-radeon-geforce/3/</a>
+ * <a href="http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf" rel="nofollow">http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf</a>
+ *
+ * @author Phate666 (adapted to jme3)
+ *
+ */
+public class FXAAFilter extends Filter {
+
+ private float subPixelShift = 1.0f / 4.0f;
+ private float vxOffset = 0.0f;
+ private float spanMax = 8.0f;
+ private float reduceMul = 1.0f / 8.0f;
+
+ public FXAAFilter() {
+ super("FXAAFilter");
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager,
+ RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/FXAA.j3md");
+ material.setFloat("SubPixelShift", subPixelShift);
+ material.setFloat("VxOffset", vxOffset);
+ material.setFloat("SpanMax", spanMax);
+ material.setFloat("ReduceMul", reduceMul);
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ public void setSpanMax(float spanMax) {
+ this.spanMax = spanMax;
+ if (material != null) {
+ material.setFloat("SpanMax", this.spanMax);
+ }
+ }
+
+ /**
+ * set to 0.0f for higher quality
+ *
+ * @param subPixelShift
+ */
+ public void setSubPixelShift(float subPixelShift) {
+ this.subPixelShift = subPixelShift;
+ if (material != null) {
+ material.setFloat("SubPixelShif", this.subPixelShift);
+ }
+ }
+
+ /**
+ * set to 0.0f for higher quality
+ *
+ * @param reduceMul
+ */
+ public void setReduceMul(float reduceMul) {
+ this.reduceMul = reduceMul;
+ if (material != null) {
+ material.setFloat("ReduceMul", this.reduceMul);
+ }
+ }
+
+ public void setVxOffset(float vxOffset) {
+ this.vxOffset = vxOffset;
+ if (material != null) {
+ material.setFloat("VxOffset", this.vxOffset);
+ }
+ }
+
+ public float getReduceMul() {
+ return reduceMul;
+ }
+
+ public float getSpanMax() {
+ return spanMax;
+ }
+
+ public float getSubPixelShift() {
+ return subPixelShift;
+ }
+
+ public float getVxOffset() {
+ return vxOffset;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/com/jme3/post/filters/FadeFilter.java b/engine/src/core-effects/com/jme3/post/filters/FadeFilter.java
new file mode 100644
index 0000000..1fb2340
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/FadeFilter.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import java.io.IOException;
+
+/**
+ *
+ * Fade Filter allows you to make an animated fade effect on a scene.
+ * @author Rémy Bouquet aka Nehon
+ * implemented from boxjar implementation
+ * @see <a href="http://jmonkeyengine.org/groups/graphics/forum/topic/newbie-question-general-fade-inout-effect/#post-105559">http://jmonkeyengine.org/groups/graphics/forum/topic/newbie-question-general-fade-inout-effect/#post-105559</a>
+ */
+public class FadeFilter extends Filter {
+
+ private float value = 1;
+ private boolean playing = false;
+ private float direction = 1;
+ private float duration = 1;
+
+ /**
+ * Creates a FadeFilter
+ */
+ public FadeFilter() {
+ super("Fade In/Out");
+ }
+
+ /**
+ * Creates a FadeFilter with the given duration
+ * @param duration
+ */
+ public FadeFilter(float duration) {
+ this();
+ this.duration = duration;
+ }
+
+ @Override
+ protected Material getMaterial() {
+ material.setFloat("Value", value);
+ return material;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/Fade.j3md");
+ }
+
+ @Override
+ protected void preFrame(float tpf) {
+ if (playing) {
+ value += tpf * direction / duration;
+
+ if (direction > 0 && value > 1) {
+ value = 1;
+ playing = false;
+ setEnabled(false);
+ }
+ if (direction < 0 && value < 0) {
+ value = 0;
+ playing = false;
+ setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * returns the duration of the effect
+ * @return
+ */
+ public float getDuration() {
+ return duration;
+ }
+
+ /**
+ * Sets the duration of the filter default is 1 second
+ * @param duration
+ */
+ public void setDuration(float duration) {
+ this.duration = duration;
+ }
+
+ /**
+ * fades the scene in (black to scene)
+ */
+ public void fadeIn() {
+ setEnabled(true);
+ direction = 1;
+ playing = true;
+ }
+
+ /**
+ * fades the scene out (scene to black)
+ */
+ public void fadeOut() {
+ setEnabled(true);
+ direction = -1;
+ playing = true;
+
+ }
+
+ public void pause() {
+ playing = false;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(duration, "duration", 1);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ duration = ic.readFloat("duration", 1);
+ }
+
+ /**
+ * return the current value of the fading
+ * can be used to chack if fade is complete (eg value=1)
+ * @return
+ */
+ public float getValue() {
+ return value;
+ }
+
+ /**
+ * sets the fade value
+ * can be used to force complete black or compete scene
+ * @param value
+ */
+ public void setValue(float value) {
+ this.value = value;
+ if (material != null) {
+ material.setFloat("Value", value);
+ }
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/FogFilter.java b/engine/src/core-effects/com/jme3/post/filters/FogFilter.java
new file mode 100644
index 0000000..c1df3b7
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/FogFilter.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import java.io.IOException;
+
+/**
+ * A filter to render a fog effect
+ * @author Rémy Bouquet aka Nehon
+ */
+public class FogFilter extends Filter {
+
+ private ColorRGBA fogColor = ColorRGBA.White.clone();
+ private float fogDensity = 0.7f;
+ private float fogDistance = 1000;
+
+ /**
+ * Creates a FogFilter
+ */
+ public FogFilter() {
+ super("FogFilter");
+ }
+
+ /**
+ * Create a fog filter
+ * @param fogColor the color of the fog (default is white)
+ * @param fogDensity the density of the fog (default is 0.7)
+ * @param fogDistance the distance of the fog (default is 1000)
+ */
+ public FogFilter(ColorRGBA fogColor, float fogDensity, float fogDistance) {
+ this();
+ this.fogColor = fogColor;
+ this.fogDensity = fogDensity;
+ this.fogDistance = fogDistance;
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/Fog.j3md");
+ material.setColor("FogColor", fogColor);
+ material.setFloat("FogDensity", fogDensity);
+ material.setFloat("FogDistance", fogDistance);
+ }
+
+ @Override
+ protected Material getMaterial() {
+
+ return material;
+ }
+
+
+ /**
+ * returns the fog color
+ * @return
+ */
+ public ColorRGBA getFogColor() {
+ return fogColor;
+ }
+
+ /**
+ * Sets the color of the fog
+ * @param fogColor
+ */
+ public void setFogColor(ColorRGBA fogColor) {
+ if (material != null) {
+ material.setColor("FogColor", fogColor);
+ }
+ this.fogColor = fogColor;
+ }
+
+ /**
+ * returns the fog density
+ * @return
+ */
+ public float getFogDensity() {
+ return fogDensity;
+ }
+
+ /**
+ * Sets the density of the fog, a high value gives a thick fog
+ * @param fogDensity
+ */
+ public void setFogDensity(float fogDensity) {
+ if (material != null) {
+ material.setFloat("FogDensity", fogDensity);
+ }
+ this.fogDensity = fogDensity;
+ }
+
+ /**
+ * returns the fog distance
+ * @return
+ */
+ public float getFogDistance() {
+ return fogDistance;
+ }
+
+ /**
+ * the distance of the fog. the higer the value the distant the fog looks
+ * @param fogDistance
+ */
+ public void setFogDistance(float fogDistance) {
+ if (material != null) {
+ material.setFloat("FogDistance", fogDistance);
+ }
+ this.fogDistance = fogDistance;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(fogColor, "fogColor", ColorRGBA.White.clone());
+ oc.write(fogDensity, "fogDensity", 0.7f);
+ oc.write(fogDistance, "fogDistance", 1000);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ fogColor = (ColorRGBA) ic.readSavable("fogColor", ColorRGBA.White.clone());
+ fogDensity = ic.readFloat("fogDensity", 0.7f);
+ fogDistance = ic.readFloat("fogDistance", 1000);
+ }
+
+
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/GammaCorrectionFilter.java b/engine/src/core-effects/com/jme3/post/filters/GammaCorrectionFilter.java
new file mode 100644
index 0000000..9e283ca
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/GammaCorrectionFilter.java
@@ -0,0 +1,78 @@
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+
+/**
+ *
+ * @author Phate666
+ * @version 1.0 initial version
+ * @version 1.1 added luma
+ */
+public class GammaCorrectionFilter extends Filter
+{
+ private float gamma = 2.0f;
+ private boolean computeLuma = false;
+
+ public GammaCorrectionFilter()
+ {
+ super("GammaCorrectionFilter");
+ }
+
+ public GammaCorrectionFilter(float gamma)
+ {
+ this();
+ this.setGamma(gamma);
+ }
+
+ @Override
+ protected Material getMaterial()
+ {
+ return material;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager,
+ RenderManager renderManager, ViewPort vp, int w, int h)
+ {
+ material = new Material(manager,
+ "Common/MatDefs/Post/GammaCorrection.j3md");
+ material.setFloat("gamma", gamma);
+ material.setBoolean("computeLuma", computeLuma);
+ }
+
+ public float getGamma()
+ {
+ return gamma;
+ }
+
+ /**
+ * set to 0.0 to disable gamma correction
+ * @param gamma
+ */
+ public void setGamma(float gamma)
+ {
+ if (material != null)
+ {
+ material.setFloat("gamma", gamma);
+ }
+ this.gamma = gamma;
+ }
+
+ public boolean isComputeLuma()
+ {
+ return computeLuma;
+ }
+
+ public void setComputeLuma(boolean computeLuma)
+ {
+ if (material != null)
+ {
+ material.setBoolean("computeLuma", computeLuma);
+ }
+ this.computeLuma = computeLuma;
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java b/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java
new file mode 100644
index 0000000..953f10a
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.post.Filter;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import java.io.IOException;
+
+/**
+ * LightScattering filters creates rays comming from a light sources
+ * This is often reffered as god rays.
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
+public class LightScatteringFilter extends Filter {
+
+ private Vector3f lightPosition;
+ private Vector3f screenLightPos = new Vector3f();
+ private int nbSamples = 50;
+ private float blurStart = 0.02f;
+ private float blurWidth = 0.9f;
+ private float lightDensity = 1.4f;
+ private boolean adaptative = true;
+ Vector3f viewLightPos = new Vector3f();
+ private boolean display = true;
+ private float innerLightDensity;
+
+ /**
+ * creates a lightScaterring filter
+ */
+ public LightScatteringFilter() {
+ super("Light Scattering");
+ }
+
+ /**
+ * Creates a lightScatteringFilter
+ * @param lightPosition
+ */
+ public LightScatteringFilter(Vector3f lightPosition) {
+ this();
+ this.lightPosition = lightPosition;
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected Material getMaterial() {
+ material.setVector3("LightPosition", screenLightPos);
+ material.setInt("NbSamples", nbSamples);
+ material.setFloat("BlurStart", blurStart);
+ material.setFloat("BlurWidth", blurWidth);
+ material.setFloat("LightDensity", innerLightDensity);
+ material.setBoolean("Display", display);
+ return material;
+ }
+
+ @Override
+ protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
+ getClipCoordinates(lightPosition, screenLightPos, viewPort.getCamera());
+ // screenLightPos.x = screenLightPos.x / viewPort.getCamera().getWidth();
+ // screenLightPos.y = screenLightPos.y / viewPort.getCamera().getHeight();
+
+ viewPort.getCamera().getViewMatrix().mult(lightPosition, viewLightPos);
+ //System.err.println("viewLightPos "+viewLightPos);
+ display = screenLightPos.x < 1.6f && screenLightPos.x > -0.6f && screenLightPos.y < 1.6f && screenLightPos.y > -0.6f && viewLightPos.z < 0;
+//System.err.println("camdir "+viewPort.getCamera().getDirection());
+//System.err.println("lightPos "+lightPosition);
+//System.err.println("screenLightPos "+screenLightPos);
+ if (adaptative) {
+ innerLightDensity = Math.max(lightDensity - Math.max(screenLightPos.x, screenLightPos.y), 0.0f);
+ } else {
+ innerLightDensity = lightDensity;
+ }
+ }
+
+ private Vector3f getClipCoordinates(Vector3f worldPosition, Vector3f store, Camera cam) {
+
+ float w = cam.getViewProjectionMatrix().multProj(worldPosition, store);
+ store.divideLocal(w);
+
+ store.x = ((store.x + 1f) * (cam.getViewPortRight() - cam.getViewPortLeft()) / 2f + cam.getViewPortLeft());
+ store.y = ((store.y + 1f) * (cam.getViewPortTop() - cam.getViewPortBottom()) / 2f + cam.getViewPortBottom());
+ store.z = (store.z + 1f) / 2f;
+
+ return store;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/LightScattering.j3md");
+ }
+
+ /**
+ * returns the blur start of the scattering
+ * see {@link setBlurStart(float blurStart)}
+ * @return
+ */
+ public float getBlurStart() {
+ return blurStart;
+ }
+
+ /**
+ * sets the blur start<br>
+ * at which distance from the light source the effect starts default is 0.02
+ * @param blurStart
+ */
+ public void setBlurStart(float blurStart) {
+ this.blurStart = blurStart;
+ }
+
+ /**
+ * returns the blur width<br>
+ * see {@link setBlurWidth(float blurWidth)}
+ * @return
+ */
+ public float getBlurWidth() {
+ return blurWidth;
+ }
+
+ /**
+ * sets the blur width default is 0.9
+ * @param blurWidth
+ */
+ public void setBlurWidth(float blurWidth) {
+ this.blurWidth = blurWidth;
+ }
+
+ /**
+ * retiurns the light density<br>
+ * see {@link setLightDensity(float lightDensity)}
+ *
+ * @return
+ */
+ public float getLightDensity() {
+ return lightDensity;
+ }
+
+ /**
+ * sets how much the effect is visible over the rendered scene default is 1.4
+ * @param lightDensity
+ */
+ public void setLightDensity(float lightDensity) {
+ this.lightDensity = lightDensity;
+ }
+
+ /**
+ * returns the light position
+ * @return
+ */
+ public Vector3f getLightPosition() {
+ return lightPosition;
+ }
+
+ /**
+ * sets the light position
+ * @param lightPosition
+ */
+ public void setLightPosition(Vector3f lightPosition) {
+ this.lightPosition = lightPosition;
+ }
+
+ /**
+ * returns the nmber of samples for the radial blur
+ * @return
+ */
+ public int getNbSamples() {
+ return nbSamples;
+ }
+
+ /**
+ * sets the number of samples for the radial blur default is 50
+ * the higher the value the higher the quality, but the slower the performances.
+ * @param nbSamples
+ */
+ public void setNbSamples(int nbSamples) {
+ this.nbSamples = nbSamples;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(lightPosition, "lightPosition", Vector3f.ZERO);
+ oc.write(nbSamples, "nbSamples", 50);
+ oc.write(blurStart, "blurStart", 0.02f);
+ oc.write(blurWidth, "blurWidth", 0.9f);
+ oc.write(lightDensity, "lightDensity", 1.4f);
+ oc.write(adaptative, "adaptative", true);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ lightPosition = (Vector3f) ic.readSavable("lightPosition", Vector3f.ZERO);
+ nbSamples = ic.readInt("nbSamples", 50);
+ blurStart = ic.readFloat("blurStart", 0.02f);
+ blurWidth = ic.readFloat("blurWidth", 0.9f);
+ lightDensity = ic.readFloat("lightDensity", 1.4f);
+ adaptative = ic.readBoolean("adaptative", true);
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/PosterizationFilter.java b/engine/src/core-effects/com/jme3/post/filters/PosterizationFilter.java
new file mode 100644
index 0000000..c980eda
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/PosterizationFilter.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+
+/**
+ * A Post Processing filter to change colors appear with sharp edges as if the
+ * available amount of colors available was not enough to draw the true image.
+ * Possibly useful in cartoon styled games. Use the strength variable to lessen
+ * influence of this filter on the total result. Values from 0.2 to 0.7 appear
+ * to give nice results.
+ *
+ * Based on an article from Geeks3D:
+ * <a href="http://www.geeks3d.com/20091027/shader-library-posterization-post-processing-effect-glsl/" rel="nofollow">http://www.geeks3d.com/20091027/shader-library-posterization-post-processing-effect-glsl/</a>
+ *
+ * @author: Roy Straver a.k.a. Baal Garnaal
+ */
+public class PosterizationFilter extends Filter {
+
+ private int numColors = 8;
+ private float gamma = 0.6f;
+ private float strength = 1.0f;
+
+ /**
+ * Creates a posterization Filter
+ */
+ public PosterizationFilter() {
+ super("PosterizationFilter");
+ }
+
+ /**
+ * Creates a posterization Filter with the given number of colors
+ * @param numColors
+ */
+ public PosterizationFilter(int numColors) {
+ this();
+ this.numColors = numColors;
+ }
+
+ /**
+ * Creates a posterization Filter with the given number of colors and gamma
+ * @param numColors
+ * @param gamma
+ */
+ public PosterizationFilter(int numColors, float gamma) {
+ this(numColors);
+ this.gamma = gamma;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Post/Posterization.j3md");
+ material.setInt("NumColors", numColors);
+ material.setFloat("Gamma", gamma);
+ material.setFloat("Strength", strength);
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ /**
+ * Sets number of color levels used to draw the screen
+ */
+ public void setNumColors(int numColors) {
+ this.numColors = numColors;
+ if (material != null) {
+ material.setInt("NumColors", numColors);
+ }
+ }
+
+ /**
+ * Sets gamma level used to enhange visual quality
+ */
+ public void setGamma(float gamma) {
+ this.gamma = gamma;
+ if (material != null) {
+ material.setFloat("Gamma", gamma);
+ }
+ }
+
+ /**
+ * Sets urrent strength value, i.e. influence on final image
+ */
+ public void setStrength(float strength) {
+ this.strength = strength;
+ if (material != null) {
+ material.setFloat("Strength", strength);
+ }
+ }
+
+ /**
+ * Returns number of color levels used
+ */
+ public int getNumColors() {
+ return numColors;
+ }
+
+ /**
+ * Returns current gamma value
+ */
+ public float getGamma() {
+ return gamma;
+ }
+
+ /**
+ * Returns current strength value, i.e. influence on final image
+ */
+ public float getStrength() {
+ return strength;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-effects/com/jme3/post/filters/RadialBlurFilter.java b/engine/src/core-effects/com/jme3/post/filters/RadialBlurFilter.java
new file mode 100644
index 0000000..0bcae5f
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/RadialBlurFilter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.shader.VarType;
+import java.io.IOException;
+
+/**
+ * Radially blurs the scene from the center of it
+ * @author Rémy Bouquet aka Nehon
+ */
+public class RadialBlurFilter extends Filter {
+
+ private float sampleDist = 1.0f;
+ private float sampleStrength = 2.2f;
+ private float[] samples = {-0.08f, -0.05f, -0.03f, -0.02f, -0.01f, 0.01f, 0.02f, 0.03f, 0.05f, 0.08f};
+
+ /**
+ * Creates a RadialBlurFilter
+ */
+ public RadialBlurFilter() {
+ super("Radial blur");
+ }
+
+ /**
+ * Creates a RadialBlurFilter
+ * @param sampleDist the distance between samples
+ * @param sampleStrength the strenght of each sample
+ */
+ public RadialBlurFilter(float sampleDist, float sampleStrength) {
+ this();
+ this.sampleDist = sampleDist;
+ this.sampleStrength = sampleStrength;
+ }
+
+ @Override
+ protected Material getMaterial() {
+
+ material.setFloat("SampleDist", sampleDist);
+ material.setFloat("SampleStrength", sampleStrength);
+ material.setParam("Samples", VarType.FloatArray, samples);
+
+ return material;
+ }
+
+ /**
+ * return the sample distance
+ * @return
+ */
+ public float getSampleDistance() {
+ return sampleDist;
+ }
+
+ /**
+ * sets the samples distances default is 1
+ * @param sampleDist
+ */
+ public void setSampleDistance(float sampleDist) {
+ this.sampleDist = sampleDist;
+ }
+
+ /**
+ *
+ * @return
+ * @deprecated use {@link #getSampleDistance()}
+ */
+ @Deprecated
+ public float getSampleDist() {
+ return sampleDist;
+ }
+
+ /**
+ *
+ * @param sampleDist
+ * @deprecated use {@link #setSampleDistance(float sampleDist)}
+ */
+ @Deprecated
+ public void setSampleDist(float sampleDist) {
+ this.sampleDist = sampleDist;
+ }
+
+ /**
+ * Returns the sample Strength
+ * @return
+ */
+ public float getSampleStrength() {
+ return sampleStrength;
+ }
+
+ /**
+ * sets the sample streanght default is 2.2
+ * @param sampleStrength
+ */
+ public void setSampleStrength(float sampleStrength) {
+ this.sampleStrength = sampleStrength;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ material = new Material(manager, "Common/MatDefs/Blur/RadialBlur.j3md");
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(sampleDist, "sampleDist", 1.0f);
+ oc.write(sampleStrength, "sampleStrength", 2.2f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ sampleDist = ic.readFloat("sampleDist", 1.0f);
+ sampleStrength = ic.readFloat("sampleStrength", 2.2f);
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java
new file mode 100644
index 0000000..47be413
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java
@@ -0,0 +1,80 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Texture2D;
+
+/**
+ * A filter to handle translucent objects when rendering a scene with filters that uses depth like WaterFilter and SSAOFilter
+ * just create a TranslucentBucketFilter and add it to the Filter list of a FilterPostPorcessor
+ * @author Nehon
+ */
+public final class TranslucentBucketFilter extends Filter {
+
+ private RenderManager renderManager;
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager rm, ViewPort vp, int w, int h) {
+ this.renderManager = rm;
+ material = new Material(manager, "Common/MatDefs/Post/Overlay.j3md");
+ material.setColor("Color", ColorRGBA.White);
+ Texture2D tex = processor.getFilterTexture();
+ material.setTexture("Texture", tex);
+ if (tex.getImage().getMultiSamples() > 1) {
+ material.setInt("NumSamples", tex.getImage().getMultiSamples());
+ } else {
+ material.clearParam("NumSamples");
+ }
+ renderManager.setHandleTranslucentBucket(false);
+ }
+
+ /**
+ * Override this method and return false if your Filter does not need the scene texture
+ * @return
+ */
+ @Override
+ protected boolean isRequiresSceneTexture() {
+ return false;
+ }
+
+ @Override
+ protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
+ renderManager.setCamera(viewPort.getCamera(), false);
+ if (prevFilterBuffer != sceneBuffer) {
+ renderManager.getRenderer().copyFrameBuffer(prevFilterBuffer, sceneBuffer, false);
+ }
+ renderManager.getRenderer().setFrameBuffer(sceneBuffer);
+ viewPort.getQueue().renderQueue(RenderQueue.Bucket.Translucent, renderManager, viewPort.getCamera());
+ }
+
+ @Override
+ protected void cleanUpFilter(Renderer r) {
+ if (renderManager != null) {
+ renderManager.setHandleTranslucentBucket(true);
+ }
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (renderManager != null) {
+ renderManager.setHandleTranslucentBucket(!enabled);
+ }
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/post/ssao/SSAOFilter.java b/engine/src/core-effects/com/jme3/post/ssao/SSAOFilter.java
new file mode 100644
index 0000000..cb0c037
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/post/ssao/SSAOFilter.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.ssao;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.Filter;
+import com.jme3.post.Filter.Pass;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * SSAO stands for screen space ambient occlusion
+ * It's a technique that fake ambient lighting by computing shadows that near by objects would casts on each others
+ * under the effect of an ambient light
+ * more info on this in this blog post <a href="http://jmonkeyengine.org/2010/08/16/screen-space-ambient-occlusion-for-jmonkeyengine-3-0/">http://jmonkeyengine.org/2010/08/16/screen-space-ambient-occlusion-for-jmonkeyengine-3-0/</a>
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
+public class SSAOFilter extends Filter {
+
+ private Pass normalPass;
+ private Vector3f frustumCorner;
+ private Vector2f frustumNearFar;
+ private Vector2f[] samples = {new Vector2f(1.0f, 0.0f), new Vector2f(-1.0f, 0.0f), new Vector2f(0.0f, 1.0f), new Vector2f(0.0f, -1.0f)};
+ private float sampleRadius = 5.1f;
+ private float intensity = 1.5f;
+ private float scale = 0.2f;
+ private float bias = 0.1f;
+ private boolean useOnlyAo = false;
+ private boolean useAo = true;
+ private Material ssaoMat;
+ private Pass ssaoPass;
+// private Material downSampleMat;
+// private Pass downSamplePass;
+ private float downSampleFactor = 1f;
+
+ /**
+ * Create a Screen Space Ambient Occlusion Filter
+ */
+ public SSAOFilter() {
+ super("SSAOFilter");
+ }
+
+ /**
+ * Create a Screen Space Ambient Occlusion Filter
+ * @param sampleRadius The radius of the area where random samples will be picked. default 5.1f
+ * @param intensity intensity of the resulting AO. default 1.2f
+ * @param scale distance between occluders and occludee. default 0.2f
+ * @param bias the width of the occlusion cone considered by the occludee. default 0.1f
+ */
+ public SSAOFilter(float sampleRadius, float intensity, float scale, float bias) {
+ this();
+ this.sampleRadius = sampleRadius;
+ this.intensity = intensity;
+ this.scale = scale;
+ this.bias = bias;
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
+ Renderer r = renderManager.getRenderer();
+ r.setFrameBuffer(normalPass.getRenderFrameBuffer());
+ renderManager.getRenderer().clearBuffers(true, true, true);
+ renderManager.setForcedTechnique("PreNormalPass");
+ renderManager.renderViewPortQueues(viewPort, false);
+ renderManager.setForcedTechnique(null);
+ renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ int screenWidth = w;
+ int screenHeight = h;
+ postRenderPasses = new ArrayList<Pass>();
+
+ normalPass = new Pass();
+ normalPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth);
+
+
+ frustumNearFar = new Vector2f();
+
+ float farY = (vp.getCamera().getFrustumTop() / vp.getCamera().getFrustumNear()) * vp.getCamera().getFrustumFar();
+ float farX = farY * ((float) screenWidth / (float) screenHeight);
+ frustumCorner = new Vector3f(farX, farY, vp.getCamera().getFrustumFar());
+ frustumNearFar.x = vp.getCamera().getFrustumNear();
+ frustumNearFar.y = vp.getCamera().getFrustumFar();
+
+
+
+
+
+ //ssao Pass
+ ssaoMat = new Material(manager, "Common/MatDefs/SSAO/ssao.j3md");
+ ssaoMat.setTexture("Normals", normalPass.getRenderedTexture());
+ Texture random = manager.loadTexture("Common/MatDefs/SSAO/Textures/random.png");
+ random.setWrap(Texture.WrapMode.Repeat);
+ ssaoMat.setTexture("RandomMap", random);
+
+ ssaoPass = new Pass() {
+
+ @Override
+ public boolean requiresDepthAsTexture() {
+ return true;
+ }
+ };
+
+ ssaoPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth, 1, ssaoMat);
+ ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
+ ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
+ postRenderPasses.add(ssaoPass);
+ material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
+ material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
+
+ ssaoMat.setVector3("FrustumCorner", frustumCorner);
+ ssaoMat.setFloat("SampleRadius", sampleRadius);
+ ssaoMat.setFloat("Intensity", intensity);
+ ssaoMat.setFloat("Scale", scale);
+ ssaoMat.setFloat("Bias", bias);
+ material.setBoolean("UseAo", useAo);
+ material.setBoolean("UseOnlyAo", useOnlyAo);
+ ssaoMat.setVector2("FrustumNearFar", frustumNearFar);
+ material.setVector2("FrustumNearFar", frustumNearFar);
+ ssaoMat.setParam("Samples", VarType.Vector2Array, samples);
+
+ float xScale = 1.0f / w;
+ float yScale = 1.0f / h;
+
+ float blurScale = 2f;
+ material.setFloat("XScale", blurScale * xScale);
+ material.setFloat("YScale", blurScale * yScale);
+
+ }
+
+ /**
+ * Return the bias<br>
+ * see {@link #setBias(float bias)}
+ * @return
+ */
+ public float getBias() {
+ return bias;
+ }
+
+ /**
+ * Sets the the width of the occlusion cone considered by the occludee default is 0.1f
+ * @param bias
+ */
+ public void setBias(float bias) {
+ this.bias = bias;
+ if (ssaoMat != null) {
+ ssaoMat.setFloat("Bias", bias);
+ }
+ }
+
+ /**
+ * returns the ambient occlusion intensity
+ * @return
+ */
+ public float getIntensity() {
+ return intensity;
+ }
+
+ /**
+ * Sets the Ambient occlusion intensity default is 1.2f
+ * @param intensity
+ */
+ public void setIntensity(float intensity) {
+ this.intensity = intensity;
+ if (ssaoMat != null) {
+ ssaoMat.setFloat("Intensity", intensity);
+ }
+
+ }
+
+ /**
+ * returns the sample radius<br>
+ * see {link setSampleRadius(float sampleRadius)}
+ * @return
+ */
+ public float getSampleRadius() {
+ return sampleRadius;
+ }
+
+ /**
+ * Sets the radius of the area where random samples will be picked dafault 5.1f
+ * @param sampleRadius
+ */
+ public void setSampleRadius(float sampleRadius) {
+ this.sampleRadius = sampleRadius;
+ if (ssaoMat != null) {
+ ssaoMat.setFloat("SampleRadius", sampleRadius);
+ }
+
+ }
+
+ /**
+ * returns the scale<br>
+ * see {@link #setScale(float scale)}
+ * @return
+ */
+ public float getScale() {
+ return scale;
+ }
+
+ /**
+ *
+ * Returns the distance between occluders and occludee. default 0.2f
+ * @param scale
+ */
+ public void setScale(float scale) {
+ this.scale = scale;
+ if (ssaoMat != null) {
+ ssaoMat.setFloat("Scale", scale);
+ }
+ }
+
+ /**
+ * debugging only , will be removed
+ * @return
+ */
+ public boolean isUseAo() {
+ return useAo;
+ }
+
+ /**
+ * debugging only , will be removed
+ */
+ public void setUseAo(boolean useAo) {
+ this.useAo = useAo;
+ if (material != null) {
+ material.setBoolean("UseAo", useAo);
+ }
+
+ }
+
+ /**
+ * debugging only , will be removed
+ * @return
+ */
+ public boolean isUseOnlyAo() {
+ return useOnlyAo;
+ }
+
+ /**
+ * debugging only , will be removed
+ */
+ public void setUseOnlyAo(boolean useOnlyAo) {
+ this.useOnlyAo = useOnlyAo;
+ if (material != null) {
+ material.setBoolean("UseOnlyAo", useOnlyAo);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(sampleRadius, "sampleRadius", 5.1f);
+ oc.write(intensity, "intensity", 1.5f);
+ oc.write(scale, "scale", 0.2f);
+ oc.write(bias, "bias", 0.1f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ sampleRadius = ic.readFloat("sampleRadius", 5.1f);
+ intensity = ic.readFloat("intensity", 1.5f);
+ scale = ic.readFloat("scale", 0.2f);
+ bias = ic.readFloat("bias", 0.1f);
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/water/ReflectionProcessor.java b/engine/src/core-effects/com/jme3/water/ReflectionProcessor.java
new file mode 100644
index 0000000..9a14df8
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/water/ReflectionProcessor.java
@@ -0,0 +1,125 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.water;
+
+import com.jme3.math.Plane;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+
+/**
+ * Reflection Processor
+ * Used to render the reflected scene in an off view port
+ */
+public class ReflectionProcessor implements SceneProcessor {
+
+ private RenderManager rm;
+ private ViewPort vp;
+ private Camera reflectionCam;
+ private FrameBuffer reflectionBuffer;
+ private Plane reflectionClipPlane;
+
+ /**
+ * Creates a ReflectionProcessor
+ * @param reflectionCam the cam to use for reflection
+ * @param reflectionBuffer the FrameBuffer to render to
+ * @param reflectionClipPlane the clipping plane
+ */
+ public ReflectionProcessor(Camera reflectionCam, FrameBuffer reflectionBuffer, Plane reflectionClipPlane) {
+ this.reflectionCam = reflectionCam;
+ this.reflectionBuffer = reflectionBuffer;
+ this.reflectionClipPlane = reflectionClipPlane;
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ this.rm = rm;
+ this.vp = vp;
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public boolean isInitialized() {
+ return rm != null;
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ //we need special treatement for the sky because it must not be clipped
+ rm.getRenderer().setFrameBuffer(reflectionBuffer);
+ reflectionCam.setProjectionMatrix(null);
+ rm.setCamera(reflectionCam, false);
+ rm.getRenderer().clearBuffers(true, true, true);
+ //Rendering the sky whithout clipping
+ rm.getRenderer().setDepthRange(1, 1);
+ vp.getQueue().renderQueue(RenderQueue.Bucket.Sky, rm, reflectionCam, true);
+ rm.getRenderer().setDepthRange(0, 1);
+ //setting the clip plane to the cam
+ reflectionCam.setClipPlane(reflectionClipPlane, Plane.Side.Positive);//,1
+ rm.setCamera(reflectionCam, false);
+
+ }
+
+ public void postFrame(FrameBuffer out) {
+ }
+
+ public void cleanup() {
+ }
+
+ /**
+ * Internal use only<br>
+ * returns the frame buffer
+ * @return
+ */
+ public FrameBuffer getReflectionBuffer() {
+ return reflectionBuffer;
+ }
+
+ /**
+ * Internal use only<br>
+ * sets the frame buffer
+ * @param reflectionBuffer
+ */
+ public void setReflectionBuffer(FrameBuffer reflectionBuffer) {
+ this.reflectionBuffer = reflectionBuffer;
+ }
+
+ /**
+ * returns the reflection cam
+ * @return
+ */
+ public Camera getReflectionCam() {
+ return reflectionCam;
+ }
+
+ /**
+ * sets the reflection cam
+ * @param reflectionCam
+ */
+ public void setReflectionCam(Camera reflectionCam) {
+ this.reflectionCam = reflectionCam;
+ }
+
+ /**
+ * returns the reflection clip plane
+ * @return
+ */
+ public Plane getReflectionClipPlane() {
+ return reflectionClipPlane;
+ }
+
+ /**
+ * Sets the reflection clip plane
+ * @param reflectionClipPlane
+ */
+ public void setReflectionClipPlane(Plane reflectionClipPlane) {
+ this.reflectionClipPlane = reflectionClipPlane;
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java b/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java
new file mode 100644
index 0000000..70ccd11
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.water;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+
+/**
+ *
+ * Simple Water renders a simple plane that use reflection and refraction to look like water.
+ * It's pretty basic, but much faster than the WaterFilter
+ * It's useful if you aim low specs hardware and still want a good looking water.
+ * Usage is :
+ * <code>
+ * SimpleWaterProcessor waterProcessor = new SimpleWaterProcessor(assetManager);
+ * //setting the scene to use for reflection
+ * waterProcessor.setReflectionScene(mainScene);
+ * //setting the light position
+ * waterProcessor.setLightPosition(lightPos);
+ *
+ * //setting the water plane
+ * Vector3f waterLocation=new Vector3f(0,-20,0);
+ * waterProcessor.setPlane(new Plane(Vector3f.UNIT_Y, waterLocation.dot(Vector3f.UNIT_Y)));
+ * //setting the water color
+ * waterProcessor.setWaterColor(ColorRGBA.Brown);
+ *
+ * //creating a quad to render water to
+ * Quad quad = new Quad(400,400);
+ *
+ * //the texture coordinates define the general size of the waves
+ * quad.scaleTextureCoordinates(new Vector2f(6f,6f));
+ *
+ * //creating a geom to attach the water material
+ * Geometry water=new Geometry("water", quad);
+ * water.setLocalTranslation(-200, -20, 250);
+ * water.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
+ * //finally setting the material
+ * water.setMaterial(waterProcessor.getMaterial());
+ *
+ * //attaching the water to the root node
+ * rootNode.attachChild(water);
+ * </code>
+ * @author Normen Hansen & Rémy Bouquet
+ */
+public class SimpleWaterProcessor implements SceneProcessor {
+
+ protected RenderManager rm;
+ protected ViewPort vp;
+ protected Spatial reflectionScene;
+ protected ViewPort reflectionView;
+ protected ViewPort refractionView;
+ protected FrameBuffer reflectionBuffer;
+ protected FrameBuffer refractionBuffer;
+ protected Camera reflectionCam;
+ protected Camera refractionCam;
+ protected Texture2D reflectionTexture;
+ protected Texture2D refractionTexture;
+ protected Texture2D depthTexture;
+ protected Texture2D normalTexture;
+ protected Texture2D dudvTexture;
+ protected int renderWidth = 512;
+ protected int renderHeight = 512;
+ protected Plane plane = new Plane(Vector3f.UNIT_Y, Vector3f.ZERO.dot(Vector3f.UNIT_Y));
+ protected float speed = 0.05f;
+ protected Ray ray = new Ray();
+ protected Vector3f targetLocation = new Vector3f();
+ protected AssetManager manager;
+ protected Material material;
+ protected float waterDepth = 1;
+ protected float waterTransparency = 0.4f;
+ protected boolean debug = false;
+ private Picture dispRefraction;
+ private Picture dispReflection;
+ private Picture dispDepth;
+ private Plane reflectionClipPlane;
+ private Plane refractionClipPlane;
+ private float refractionClippingOffset = 0.3f;
+ private float reflectionClippingOffset = -5f;
+ private Vector3f vect1 = new Vector3f();
+ private Vector3f vect2 = new Vector3f();
+ private Vector3f vect3 = new Vector3f();
+
+ /**
+ * Creates a SimpleWaterProcessor
+ * @param manager the asset manager
+ */
+ public SimpleWaterProcessor(AssetManager manager) {
+ this.manager = manager;
+ material = new Material(manager, "Common/MatDefs/Water/SimpleWater.j3md");
+ material.setFloat("waterDepth", waterDepth);
+ material.setFloat("waterTransparency", waterTransparency / 10);
+ material.setColor("waterColor", ColorRGBA.White);
+ material.setVector3("lightPos", new Vector3f(1, -1, 1));
+
+ material.setColor("distortionScale", new ColorRGBA(0.2f, 0.2f, 0.2f, 0.2f));
+ material.setColor("distortionMix", new ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f));
+ material.setColor("texScale", new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
+ updateClipPlanes();
+
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ this.rm = rm;
+ this.vp = vp;
+
+ loadTextures(manager);
+ createTextures();
+ applyTextures(material);
+
+ createPreViews();
+
+ material.setVector2("FrustumNearFar", new Vector2f(vp.getCamera().getFrustumNear(), vp.getCamera().getFrustumFar()));
+
+ if (debug) {
+ dispRefraction = new Picture("dispRefraction");
+ dispRefraction.setTexture(manager, refractionTexture, false);
+ dispReflection = new Picture("dispRefraction");
+ dispReflection.setTexture(manager, reflectionTexture, false);
+ dispDepth = new Picture("depthTexture");
+ dispDepth.setTexture(manager, depthTexture, false);
+ }
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public boolean isInitialized() {
+ return rm != null;
+ }
+ float time = 0;
+ float savedTpf = 0;
+
+ public void preFrame(float tpf) {
+ time = time + (tpf * speed);
+ if (time > 1f) {
+ time = 0;
+ }
+ material.setFloat("time", time);
+ savedTpf = tpf;
+ }
+
+ public void postQueue(RenderQueue rq) {
+ Camera sceneCam = rm.getCurrentCamera();
+
+ //update ray
+ ray.setOrigin(sceneCam.getLocation());
+ ray.setDirection(sceneCam.getDirection());
+
+ //update refraction cam
+ refractionCam.setLocation(sceneCam.getLocation());
+ refractionCam.setRotation(sceneCam.getRotation());
+ refractionCam.setFrustum(sceneCam.getFrustumNear(),
+ sceneCam.getFrustumFar(),
+ sceneCam.getFrustumLeft(),
+ sceneCam.getFrustumRight(),
+ sceneCam.getFrustumTop(),
+ sceneCam.getFrustumBottom());
+
+ //update reflection cam
+ boolean inv = false;
+ if (!ray.intersectsWherePlane(plane, targetLocation)) {
+ ray.setDirection(ray.getDirection().negateLocal());
+ ray.intersectsWherePlane(plane, targetLocation);
+ inv = true;
+ }
+ Vector3f loc = plane.reflect(sceneCam.getLocation(), new Vector3f());
+ reflectionCam.setLocation(loc);
+ reflectionCam.setFrustum(sceneCam.getFrustumNear(),
+ sceneCam.getFrustumFar(),
+ sceneCam.getFrustumLeft(),
+ sceneCam.getFrustumRight(),
+ sceneCam.getFrustumTop(),
+ sceneCam.getFrustumBottom());
+ // tempVec and calcVect are just temporary vector3f objects
+ vect1.set(sceneCam.getLocation()).addLocal(sceneCam.getUp());
+ float planeDistance = plane.pseudoDistance(vect1);
+ vect2.set(plane.getNormal()).multLocal(planeDistance * 2.0f);
+ vect3.set(vect1.subtractLocal(vect2)).subtractLocal(loc).normalizeLocal().negateLocal();
+ // now set the up vector
+ reflectionCam.lookAt(targetLocation, vect3);
+ if (inv) {
+ reflectionCam.setAxes(reflectionCam.getLeft().negateLocal(), reflectionCam.getUp(), reflectionCam.getDirection().negateLocal());
+ }
+
+ //Rendering reflection and refraction
+ rm.renderViewPort(reflectionView, savedTpf);
+ rm.renderViewPort(refractionView, savedTpf);
+ rm.getRenderer().setFrameBuffer(vp.getOutputFrameBuffer());
+ rm.setCamera(sceneCam, false);
+
+ }
+
+ public void postFrame(FrameBuffer out) {
+ if (debug) {
+ displayMap(rm.getRenderer(), dispRefraction, 64);
+ displayMap(rm.getRenderer(), dispReflection, 256);
+ displayMap(rm.getRenderer(), dispDepth, 448);
+ }
+ }
+
+ public void cleanup() {
+ }
+
+ //debug only : displays maps
+ protected void displayMap(Renderer r, Picture pic, int left) {
+ Camera cam = vp.getCamera();
+ rm.setCamera(cam, true);
+ int h = cam.getHeight();
+
+ pic.setPosition(left, h / 20f);
+
+ pic.setWidth(128);
+ pic.setHeight(128);
+ pic.updateGeometricState();
+ rm.renderGeometry(pic);
+ rm.setCamera(cam, false);
+ }
+
+ protected void loadTextures(AssetManager manager) {
+ normalTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/water_normalmap.dds");
+ dudvTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/dudv_map.jpg");
+ normalTexture.setWrap(WrapMode.Repeat);
+ dudvTexture.setWrap(WrapMode.Repeat);
+ }
+
+ protected void createTextures() {
+ reflectionTexture = new Texture2D(renderWidth, renderHeight, Format.RGBA8);
+ refractionTexture = new Texture2D(renderWidth, renderHeight, Format.RGBA8);
+ depthTexture = new Texture2D(renderWidth, renderHeight, Format.Depth);
+ }
+
+ protected void applyTextures(Material mat) {
+ mat.setTexture("water_reflection", reflectionTexture);
+ mat.setTexture("water_refraction", refractionTexture);
+ mat.setTexture("water_depthmap", depthTexture);
+ mat.setTexture("water_normalmap", normalTexture);
+ mat.setTexture("water_dudvmap", dudvTexture);
+ }
+
+ protected void createPreViews() {
+ reflectionCam = new Camera(renderWidth, renderHeight);
+ refractionCam = new Camera(renderWidth, renderHeight);
+
+ // create a pre-view. a view that is rendered before the main view
+ reflectionView = new ViewPort("Reflection View", reflectionCam);
+ reflectionView.setClearFlags(true, true, true);
+ reflectionView.setBackgroundColor(ColorRGBA.Black);
+ // create offscreen framebuffer
+ reflectionBuffer = new FrameBuffer(renderWidth, renderHeight, 1);
+ //setup framebuffer to use texture
+ reflectionBuffer.setDepthBuffer(Format.Depth);
+ reflectionBuffer.setColorTexture(reflectionTexture);
+
+ //set viewport to render to offscreen framebuffer
+ reflectionView.setOutputFrameBuffer(reflectionBuffer);
+ reflectionView.addProcessor(new ReflectionProcessor(reflectionCam, reflectionBuffer, reflectionClipPlane));
+ // attach the scene to the viewport to be rendered
+ reflectionView.attachScene(reflectionScene);
+
+ // create a pre-view. a view that is rendered before the main view
+ refractionView = new ViewPort("Refraction View", refractionCam);
+ refractionView.setClearFlags(true, true, true);
+ refractionView.setBackgroundColor(ColorRGBA.Black);
+ // create offscreen framebuffer
+ refractionBuffer = new FrameBuffer(renderWidth, renderHeight, 1);
+ //setup framebuffer to use texture
+ refractionBuffer.setDepthBuffer(Format.Depth);
+ refractionBuffer.setColorTexture(refractionTexture);
+ refractionBuffer.setDepthTexture(depthTexture);
+ //set viewport to render to offscreen framebuffer
+ refractionView.setOutputFrameBuffer(refractionBuffer);
+ refractionView.addProcessor(new RefractionProcessor());
+ // attach the scene to the viewport to be rendered
+ refractionView.attachScene(reflectionScene);
+ }
+
+ protected void destroyViews() {
+ // rm.removePreView(reflectionView);
+ rm.removePreView(refractionView);
+ }
+
+ /**
+ * Get the water material from this processor, apply this to your water quad.
+ * @return
+ */
+ public Material getMaterial() {
+ return material;
+ }
+
+ /**
+ * Sets the reflected scene, should not include the water quad!
+ * Set before adding processor.
+ * @param spat
+ */
+ public void setReflectionScene(Spatial spat) {
+ reflectionScene = spat;
+ }
+
+ /**
+ * returns the width of the reflection and refraction textures
+ * @return
+ */
+ public int getRenderWidth() {
+ return renderWidth;
+ }
+
+ /**
+ * returns the height of the reflection and refraction textures
+ * @return
+ */
+ public int getRenderHeight() {
+ return renderHeight;
+ }
+
+ /**
+ * Set the reflection Texture render size,
+ * set before adding the processor!
+ * @param with
+ * @param height
+ */
+ public void setRenderSize(int width, int height) {
+ renderWidth = width;
+ renderHeight = height;
+ }
+
+ /**
+ * returns the water plane
+ * @return
+ */
+ public Plane getPlane() {
+ return plane;
+ }
+
+ /**
+ * Set the water plane for this processor.
+ * @param plane
+ */
+ public void setPlane(Plane plane) {
+ this.plane.setConstant(plane.getConstant());
+ this.plane.setNormal(plane.getNormal());
+ updateClipPlanes();
+ }
+
+ /**
+ * Set the water plane using an origin (location) and a normal (reflection direction).
+ * @param origin Set to 0,-6,0 if your water quad is at that location for correct reflection
+ * @param normal Set to 0,1,0 (Vector3f.UNIT_Y) for normal planar water
+ */
+ public void setPlane(Vector3f origin, Vector3f normal) {
+ this.plane.setOriginNormal(origin, normal);
+ updateClipPlanes();
+ }
+
+ private void updateClipPlanes() {
+ reflectionClipPlane = plane.clone();
+ reflectionClipPlane.setConstant(reflectionClipPlane.getConstant() + reflectionClippingOffset);
+ refractionClipPlane = plane.clone();
+ refractionClipPlane.setConstant(refractionClipPlane.getConstant() + refractionClippingOffset);
+
+ }
+
+ /**
+ * Set the light Position for the processor
+ * @param position
+ */
+ //TODO maybe we should provide a convenient method to compute position from direction
+ public void setLightPosition(Vector3f position) {
+ material.setVector3("lightPos", position);
+ }
+
+ /**
+ * Set the color that will be added to the refraction texture.
+ * @param color
+ */
+ public void setWaterColor(ColorRGBA color) {
+ material.setColor("waterColor", color);
+ }
+
+ /**
+ * Higher values make the refraction texture shine through earlier.
+ * Default is 4
+ * @param depth
+ */
+ public void setWaterDepth(float depth) {
+ waterDepth = depth;
+ material.setFloat("waterDepth", depth);
+ }
+
+ /**
+ * return the water depth
+ * @return
+ */
+ public float getWaterDepth() {
+ return waterDepth;
+ }
+
+ /**
+ * returns water transparency
+ * @return
+ */
+ public float getWaterTransparency() {
+ return waterTransparency;
+ }
+
+ /**
+ * sets the water transparency default os 0.1f
+ * @param waterTransparency
+ */
+ public void setWaterTransparency(float waterTransparency) {
+ this.waterTransparency = Math.max(0, waterTransparency);
+ material.setFloat("waterTransparency", waterTransparency / 10);
+ }
+
+ /**
+ * Sets the speed of the wave animation, default = 0.05f.
+ * @param speed
+ */
+ public void setWaveSpeed(float speed) {
+ this.speed = speed;
+ }
+
+ /**
+ * Sets the scale of distortion by the normal map, default = 0.2
+ */
+ public void setDistortionScale(float value) {
+ material.setColor("distortionScale", new ColorRGBA(value, value, value, value));
+ }
+
+ /**
+ * Sets how the normal and dudv map are mixed to create the wave effect, default = 0.5
+ */
+ public void setDistortionMix(float value) {
+ material.setColor("distortionMix", new ColorRGBA(value, value, value, value));
+ }
+
+ /**
+ * Sets the scale of the normal/dudv texture, default = 1.
+ * Note that the waves should be scaled by the texture coordinates of the quad to avoid animation artifacts,
+ * use mesh.scaleTextureCoordinates(Vector2f) for that.
+ */
+ public void setTexScale(float value) {
+ material.setColor("texScale", new ColorRGBA(value, value, value, value));
+ }
+
+ /**
+ * retruns true if the waterprocessor is in debug mode
+ * @return
+ */
+ public boolean isDebug() {
+ return debug;
+ }
+
+ /**
+ * set to true to display reflection and refraction textures in the GUI for debug purpose
+ * @param debug
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Creates a quad with the water material applied to it.
+ * @param width
+ * @param height
+ * @return
+ */
+ public Geometry createWaterGeometry(float width, float height) {
+ Quad quad = new Quad(width, height);
+ Geometry geom = new Geometry("WaterGeometry", quad);
+ geom.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
+ geom.setMaterial(material);
+ return geom;
+ }
+
+ /**
+ * returns the reflection clipping plane offset
+ * @return
+ */
+ public float getReflectionClippingOffset() {
+ return reflectionClippingOffset;
+ }
+
+ /**
+ * sets the reflection clipping plane offset
+ * set a nagetive value to lower the clipping plane for relection texture rendering.
+ * @param reflectionClippingOffset
+ */
+ public void setReflectionClippingOffset(float reflectionClippingOffset) {
+ this.reflectionClippingOffset = reflectionClippingOffset;
+ updateClipPlanes();
+ }
+
+ /**
+ * returns the refraction clipping plane offset
+ * @return
+ */
+ public float getRefractionClippingOffset() {
+ return refractionClippingOffset;
+ }
+
+ /**
+ * Sets the refraction clipping plane offset
+ * set a positive value to raise the clipping plane for refraction texture rendering
+ * @param refractionClippingOffset
+ */
+ public void setRefractionClippingOffset(float refractionClippingOffset) {
+ this.refractionClippingOffset = refractionClippingOffset;
+ updateClipPlanes();
+ }
+
+ /**
+ * Refraction Processor
+ */
+ public class RefractionProcessor implements SceneProcessor {
+
+ RenderManager rm;
+ ViewPort vp;
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ this.rm = rm;
+ this.vp = vp;
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public boolean isInitialized() {
+ return rm != null;
+ }
+
+ public void preFrame(float tpf) {
+ refractionCam.setClipPlane(refractionClipPlane, Plane.Side.Negative);//,-1
+
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ public void postFrame(FrameBuffer out) {
+ }
+
+ public void cleanup() {
+ }
+ }
+}
diff --git a/engine/src/core-effects/com/jme3/water/WaterFilter.java b/engine/src/core-effects/com/jme3/water/WaterFilter.java
new file mode 100644
index 0000000..2943dce
--- /dev/null
+++ b/engine/src/core-effects/com/jme3/water/WaterFilter.java
@@ -0,0 +1,1050 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.water;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.Filter;
+import com.jme3.post.Filter.Pass;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * The WaterFilter is a 2D post process that simulate water.
+ * It renders water above and under water.
+ * See this blog post for more info <a href="http://jmonkeyengine.org/2011/01/15/new-advanced-water-effect-for-jmonkeyengine-3/">http://jmonkeyengine.org/2011/01/15/new-advanced-water-effect-for-jmonkeyengine-3/</a>
+ *
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
+public class WaterFilter extends Filter {
+
+ private Pass reflectionPass;
+ protected Spatial reflectionScene;
+ protected ViewPort reflectionView;
+ private Texture2D normalTexture;
+ private Texture2D foamTexture;
+ private Texture2D causticsTexture;
+ private Texture2D heightTexture;
+ private Plane plane;
+ private Camera reflectionCam;
+ protected Ray ray = new Ray();
+ private Vector3f targetLocation = new Vector3f();
+ private ReflectionProcessor reflectionProcessor;
+ private Matrix4f biasMatrix = new Matrix4f(0.5f, 0.0f, 0.0f, 0.5f,
+ 0.0f, 0.5f, 0.0f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ private Matrix4f textureProjMatrix = new Matrix4f();
+ private boolean underWater;
+ private RenderManager renderManager;
+ private ViewPort viewPort;
+ private float time = 0;
+ //properties
+ private float speed = 1;
+ private Vector3f lightDirection = new Vector3f(0, -1, 0);
+ private ColorRGBA lightColor = ColorRGBA.White;
+ private float waterHeight = 0.0f;
+ private ColorRGBA waterColor = new ColorRGBA(0.0078f, 0.3176f, 0.5f, 1.0f);
+ private ColorRGBA deepWaterColor = new ColorRGBA(0.0039f, 0.00196f, 0.145f, 1.0f);
+ private Vector3f colorExtinction = new Vector3f(5.0f, 20.0f, 30.0f);
+ private float waterTransparency = 0.1f;
+ private float maxAmplitude = 1.5f;
+ private float shoreHardness = 0.1f;
+ private boolean useFoam = true;
+ private float foamIntensity = 0.5f;
+ private float foamHardness = 1.0f;
+ private Vector3f foamExistence = new Vector3f(0.45f, 4.35f, 1.5f);
+ private float waveScale = 0.005f;
+ private float sunScale = 3.0f;
+ private float shininess = 0.7f;
+ private Vector2f windDirection = new Vector2f(0.0f, -1.0f);
+ private int reflectionMapSize = 512;
+ private boolean useRipples = true;
+ private float normalScale = 3.0f;
+ private boolean useHQShoreline = true;
+ private boolean useSpecular = true;
+ private boolean useRefraction = true;
+ private float refractionStrength = 0.0f;
+ private float refractionConstant = 0.5f;
+ private float reflectionDisplace = 30;
+ private float underWaterFogDistance = 120;
+ private boolean useCaustics = true;
+ private float causticsIntensity = 0.5f;
+
+ /**
+ * Create a Water Filter
+ */
+ public WaterFilter() {
+ super("WaterFilter");
+ }
+
+ public WaterFilter(Node reflectionScene, Vector3f lightDirection) {
+ super("WaterFilter");
+ this.reflectionScene = reflectionScene;
+ this.lightDirection = lightDirection;
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected void preFrame(float tpf) {
+ time = time + (tpf * speed);
+ material.setFloat("Time", time);
+ Camera sceneCam = viewPort.getCamera();
+ biasMatrix.mult(sceneCam.getViewProjectionMatrix(), textureProjMatrix);
+ material.setMatrix4("TextureProjMatrix", textureProjMatrix);
+ material.setVector3("CameraPosition", sceneCam.getLocation());
+ material.setMatrix4("ViewProjectionMatrixInverse", sceneCam.getViewProjectionMatrix().invert());
+
+ material.setFloat("WaterHeight", waterHeight);
+
+ //update reflection cam
+ ray.setOrigin(sceneCam.getLocation());
+ ray.setDirection(sceneCam.getDirection());
+ plane = new Plane(Vector3f.UNIT_Y, new Vector3f(0, waterHeight, 0).dot(Vector3f.UNIT_Y));
+ reflectionProcessor.setReflectionClipPlane(plane);
+ boolean inv = false;
+ if (!ray.intersectsWherePlane(plane, targetLocation)) {
+ ray.setDirection(ray.getDirection().negateLocal());
+ ray.intersectsWherePlane(plane, targetLocation);
+ inv = true;
+ }
+ Vector3f loc = plane.reflect(sceneCam.getLocation(), new Vector3f());
+ reflectionCam.setLocation(loc);
+ reflectionCam.setFrustum(sceneCam.getFrustumNear(),
+ sceneCam.getFrustumFar(),
+ sceneCam.getFrustumLeft(),
+ sceneCam.getFrustumRight(),
+ sceneCam.getFrustumTop(),
+ sceneCam.getFrustumBottom());
+ TempVars vars = TempVars.get();
+
+
+ vars.vect1.set(sceneCam.getLocation()).addLocal(sceneCam.getUp());
+ float planeDistance = plane.pseudoDistance(vars.vect1);
+ vars.vect2.set(plane.getNormal()).multLocal(planeDistance * 2.0f);
+ vars.vect3.set(vars.vect1.subtractLocal(vars.vect2)).subtractLocal(loc).normalizeLocal().negateLocal();
+
+ reflectionCam.lookAt(targetLocation, vars.vect3);
+ vars.release();
+
+ if (inv) {
+ reflectionCam.setAxes(reflectionCam.getLeft().negateLocal(), reflectionCam.getUp(), reflectionCam.getDirection().negateLocal());
+ }
+
+ //if we're under water no need to compute reflection
+ if (sceneCam.getLocation().y >= waterHeight) {
+ boolean rtb = true;
+ if (!renderManager.isHandleTranslucentBucket()) {
+ renderManager.setHandleTranslucentBucket(true);
+ rtb = false;
+ }
+ renderManager.renderViewPort(reflectionView, tpf);
+ if (!rtb) {
+ renderManager.setHandleTranslucentBucket(false);
+ }
+ renderManager.setCamera(sceneCam, false);
+ renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
+
+
+ underWater = false;
+ } else {
+ underWater = true;
+ }
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ private DirectionalLight findLight(Node node) {
+ for (Light light : node.getWorldLightList()) {
+ if (light instanceof DirectionalLight) {
+ return (DirectionalLight) light;
+ }
+ }
+ for (Spatial child : node.getChildren()) {
+ if (child instanceof Node) {
+ return findLight((Node) child);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+
+ if (reflectionScene == null) {
+ reflectionScene = vp.getScenes().get(0);
+ DirectionalLight l = findLight((Node) reflectionScene);
+ if (l != null) {
+ lightDirection = l.getDirection();
+ }
+
+ }
+
+ this.renderManager = renderManager;
+ this.viewPort = vp;
+ reflectionPass = new Pass();
+ reflectionPass.init(renderManager.getRenderer(), reflectionMapSize, reflectionMapSize, Format.RGBA8, Format.Depth);
+ reflectionCam = new Camera(reflectionMapSize, reflectionMapSize);
+ reflectionView = new ViewPort("reflectionView", reflectionCam);
+ reflectionView.setClearFlags(true, true, true);
+ reflectionView.attachScene(reflectionScene);
+ reflectionView.setOutputFrameBuffer(reflectionPass.getRenderFrameBuffer());
+ plane = new Plane(Vector3f.UNIT_Y, new Vector3f(0, waterHeight, 0).dot(Vector3f.UNIT_Y));
+ reflectionProcessor = new ReflectionProcessor(reflectionCam, reflectionPass.getRenderFrameBuffer(), plane);
+ reflectionView.addProcessor(reflectionProcessor);
+
+ normalTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/water_normalmap.dds");
+ if (foamTexture == null) {
+ foamTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/foam.jpg");
+ }
+ if (causticsTexture == null) {
+ causticsTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/caustics.jpg");
+ }
+ heightTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/heightmap.jpg");
+
+ normalTexture.setWrap(WrapMode.Repeat);
+ foamTexture.setWrap(WrapMode.Repeat);
+ causticsTexture.setWrap(WrapMode.Repeat);
+ heightTexture.setWrap(WrapMode.Repeat);
+
+ material = new Material(manager, "Common/MatDefs/Water/Water.j3md");
+ material.setTexture("HeightMap", heightTexture);
+ material.setTexture("CausticsMap", causticsTexture);
+ material.setTexture("FoamMap", foamTexture);
+ material.setTexture("NormalMap", normalTexture);
+ material.setTexture("ReflectionMap", reflectionPass.getRenderedTexture());
+
+ material.setFloat("WaterTransparency", waterTransparency);
+ material.setFloat("NormalScale", normalScale);
+ material.setFloat("R0", refractionConstant);
+ material.setFloat("MaxAmplitude", maxAmplitude);
+ material.setVector3("LightDir", lightDirection);
+ material.setColor("LightColor", lightColor);
+ material.setFloat("ShoreHardness", shoreHardness);
+ material.setFloat("RefractionStrength", refractionStrength);
+ material.setFloat("WaveScale", waveScale);
+ material.setVector3("FoamExistence", foamExistence);
+ material.setFloat("SunScale", sunScale);
+ material.setVector3("ColorExtinction", colorExtinction);
+ material.setFloat("Shininess", shininess);
+ material.setColor("WaterColor", waterColor);
+ material.setColor("DeepWaterColor", deepWaterColor);
+ material.setVector2("WindDirection", windDirection);
+ material.setFloat("FoamHardness", foamHardness);
+ material.setBoolean("UseRipples", useRipples);
+ material.setBoolean("UseHQShoreline", useHQShoreline);
+ material.setBoolean("UseSpecular", useSpecular);
+ material.setBoolean("UseFoam", useFoam);
+ material.setBoolean("UseCaustics", useCaustics);
+ material.setBoolean("UseRefraction", useRefraction);
+ material.setFloat("ReflectionDisplace", reflectionDisplace);
+ material.setFloat("FoamIntensity", foamIntensity);
+ material.setFloat("UnderWaterFogDistance", underWaterFogDistance);
+ material.setFloat("CausticsIntensity", causticsIntensity);
+
+
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+
+ oc.write(speed, "speed", 1f);
+ oc.write(lightDirection, "lightDirection", new Vector3f(0, -1, 0));
+ oc.write(lightColor, "lightColor", ColorRGBA.White);
+ oc.write(waterHeight, "waterHeight", 0.0f);
+ oc.write(waterColor, "waterColor", new ColorRGBA(0.0078f, 0.3176f, 0.5f, 1.0f));
+ oc.write(deepWaterColor, "deepWaterColor", new ColorRGBA(0.0039f, 0.00196f, 0.145f, 1.0f));
+
+ oc.write(colorExtinction, "colorExtinction", new Vector3f(5.0f, 20.0f, 30.0f));
+ oc.write(waterTransparency, "waterTransparency", 0.1f);
+ oc.write(maxAmplitude, "maxAmplitude", 1.5f);
+ oc.write(shoreHardness, "shoreHardness", 0.1f);
+ oc.write(useFoam, "useFoam", true);
+
+ oc.write(foamIntensity, "foamIntensity", 0.5f);
+ oc.write(foamHardness, "foamHardness", 1.0f);
+
+ oc.write(foamExistence, "foamExistence", new Vector3f(0.45f, 4.35f, 1.5f));
+ oc.write(waveScale, "waveScale", 0.005f);
+
+ oc.write(sunScale, "sunScale", 3.0f);
+ oc.write(shininess, "shininess", 0.7f);
+ oc.write(windDirection, "windDirection", new Vector2f(0.0f, -1.0f));
+ oc.write(reflectionMapSize, "reflectionMapSize", 512);
+ oc.write(useRipples, "useRipples", true);
+
+ oc.write(normalScale, "normalScale", 3.0f);
+ oc.write(useHQShoreline, "useHQShoreline", true);
+
+ oc.write(useSpecular, "useSpecular", true);
+
+ oc.write(useRefraction, "useRefraction", true);
+ oc.write(refractionStrength, "refractionStrength", 0.0f);
+ oc.write(refractionConstant, "refractionConstant", 0.5f);
+ oc.write(reflectionDisplace, "reflectionDisplace", 30f);
+ oc.write(underWaterFogDistance, "underWaterFogDistance", 120f);
+ oc.write(causticsIntensity, "causticsIntensity", 0.5f);
+
+ oc.write(useCaustics, "useCaustics", true);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ speed = ic.readFloat("speed", 1f);
+ lightDirection = (Vector3f) ic.readSavable("lightDirection", new Vector3f(0, -1, 0));
+ lightColor = (ColorRGBA) ic.readSavable("lightColor", ColorRGBA.White);
+ waterHeight = ic.readFloat("waterHeight", 0.0f);
+ waterColor = (ColorRGBA) ic.readSavable("waterColor", new ColorRGBA(0.0078f, 0.3176f, 0.5f, 1.0f));
+ deepWaterColor = (ColorRGBA) ic.readSavable("deepWaterColor", new ColorRGBA(0.0039f, 0.00196f, 0.145f, 1.0f));
+
+ colorExtinction = (Vector3f) ic.readSavable("colorExtinction", new Vector3f(5.0f, 20.0f, 30.0f));
+ waterTransparency = ic.readFloat("waterTransparency", 0.1f);
+ maxAmplitude = ic.readFloat("maxAmplitude", 1.5f);
+ shoreHardness = ic.readFloat("shoreHardness", 0.1f);
+ useFoam = ic.readBoolean("useFoam", true);
+
+ foamIntensity = ic.readFloat("foamIntensity", 0.5f);
+ foamHardness = ic.readFloat("foamHardness", 1.0f);
+
+ foamExistence = (Vector3f) ic.readSavable("foamExistence", new Vector3f(0.45f, 4.35f, 1.5f));
+ waveScale = ic.readFloat("waveScale", 0.005f);
+
+ sunScale = ic.readFloat("sunScale", 3.0f);
+ shininess = ic.readFloat("shininess", 0.7f);
+ windDirection = (Vector2f) ic.readSavable("windDirection", new Vector2f(0.0f, -1.0f));
+ reflectionMapSize = ic.readInt("reflectionMapSize", 512);
+ useRipples = ic.readBoolean("useRipples", true);
+
+ normalScale = ic.readFloat("normalScale", 3.0f);
+ useHQShoreline = ic.readBoolean("useHQShoreline", true);
+
+ useSpecular = ic.readBoolean("useSpecular", true);
+
+ useRefraction = ic.readBoolean("useRefraction", true);
+ refractionStrength = ic.readFloat("refractionStrength", 0.0f);
+ refractionConstant = ic.readFloat("refractionConstant", 0.5f);
+ reflectionDisplace = ic.readFloat("reflectionDisplace", 30f);
+ underWaterFogDistance = ic.readFloat("underWaterFogDistance", 120f);
+ causticsIntensity = ic.readFloat("causticsIntensity", 0.5f);
+
+ useCaustics = ic.readBoolean("useCaustics", true);
+
+ }
+
+ /**
+ * gets the height of the water plane
+ * @return
+ */
+ public float getWaterHeight() {
+ return waterHeight;
+ }
+
+ /**
+ * Sets the height of the water plane
+ * default is 0.0
+ * @param waterHeight
+ */
+ public void setWaterHeight(float waterHeight) {
+ this.waterHeight = waterHeight;
+ }
+
+ /**
+ * sets the scene to render in the reflection map
+ * @param reflectionScene
+ */
+ public void setReflectionScene(Spatial reflectionScene) {
+ this.reflectionScene = reflectionScene;
+ }
+
+ /**
+ * returns the waterTransparency value
+ * @return
+ */
+ public float getWaterTransparency() {
+ return waterTransparency;
+ }
+
+ /**
+ * Sets how fast will colours fade out. You can also think about this
+ * values as how clear water is. Therefore use smaller values (eg. 0.05)
+ * to have crystal clear water and bigger to achieve "muddy" water.
+ * default is 0.1f
+ * @param waterTransparency
+ */
+ public void setWaterTransparency(float waterTransparency) {
+ this.waterTransparency = waterTransparency;
+ if (material != null) {
+ material.setFloat("WaterTransparency", waterTransparency);
+ }
+ }
+
+ /**
+ * Returns the normal scales applied to the normal map
+ * @return
+ */
+ public float getNormalScale() {
+ return normalScale;
+ }
+
+ /**
+ * Sets the normal scaling factors to apply to the normal map.
+ * the higher the value the more small ripples will be visible on the waves.
+ * default is 1.0
+ * @param normalScale
+ */
+ public void setNormalScale(float normalScale) {
+ this.normalScale = normalScale;
+ if (material != null) {
+ material.setFloat("NormalScale", normalScale);
+ }
+ }
+
+ /**
+ * returns the refractoin constant
+ * @return
+ */
+ public float getRefractionConstant() {
+ return refractionConstant;
+ }
+
+ /**
+ * This is a constant related to the index of refraction (IOR) used to compute the fresnel term.
+ * F = R0 + (1-R0)( 1 - N.V)^5
+ * where F is the fresnel term, R0 the constant, N the normal vector and V tne view vector.
+ * It usually depend on the material you are lookinh through (here water).
+ * Default value is 0.3f
+ * In practice, the lowest the value and the less the reflection can be seen on water
+ * @param refractionConstant
+ */
+ public void setRefractionConstant(float refractionConstant) {
+ this.refractionConstant = refractionConstant;
+ if (material != null) {
+ material.setFloat("R0", refractionConstant);
+ }
+ }
+
+ /**
+ * return the maximum wave amplitude
+ * @return
+ */
+ public float getMaxAmplitude() {
+ return maxAmplitude;
+ }
+
+ /**
+ * Sets the maximum waves amplitude
+ * default is 1.0
+ * @param maxAmplitude
+ */
+ public void setMaxAmplitude(float maxAmplitude) {
+ this.maxAmplitude = maxAmplitude;
+ if (material != null) {
+ material.setFloat("MaxAmplitude", maxAmplitude);
+ }
+ }
+
+ /**
+ * gets the light direction
+ * @return
+ */
+ public Vector3f getLightDirection() {
+ return lightDirection;
+ }
+
+ /**
+ * Sets the light direction
+ * @param lightDirection
+ */
+ public void setLightDirection(Vector3f lightDirection) {
+ this.lightDirection = lightDirection;
+ if (material != null) {
+ material.setVector3("LightDir", lightDirection);
+ }
+ }
+
+ /**
+ * returns the light color
+ * @return
+ */
+ public ColorRGBA getLightColor() {
+ return lightColor;
+ }
+
+ /**
+ * Sets the light color to use
+ * default is white
+ * @param lightColor
+ */
+ public void setLightColor(ColorRGBA lightColor) {
+ this.lightColor = lightColor;
+ if (material != null) {
+ material.setColor("LightColor", lightColor);
+ }
+ }
+
+ /**
+ * Return the shoreHardeness
+ * @return
+ */
+ public float getShoreHardness() {
+ return shoreHardness;
+ }
+
+ /**
+ * The smaller this value is, the softer the transition between
+ * shore and water. If you want hard edges use very big value.
+ * Default is 0.1f.
+ * @param shoreHardness
+ */
+ public void setShoreHardness(float shoreHardness) {
+ this.shoreHardness = shoreHardness;
+ if (material != null) {
+ material.setFloat("ShoreHardness", shoreHardness);
+ }
+ }
+
+ /**
+ * returns the foam hardness
+ * @return
+ */
+ public float getFoamHardness() {
+ return foamHardness;
+ }
+
+ /**
+ * Sets the foam hardness : How much the foam will blend with the shore to avoid hard edged water plane.
+ * Default is 1.0
+ * @param foamHardness
+ */
+ public void setFoamHardness(float foamHardness) {
+ this.foamHardness = foamHardness;
+ if (material != null) {
+ material.setFloat("FoamHardness", foamHardness);
+ }
+ }
+
+ /**
+ * returns the refractionStrenght
+ * @return
+ */
+ public float getRefractionStrength() {
+ return refractionStrength;
+ }
+
+ /**
+ * This value modifies current fresnel term. If you want to weaken
+ * reflections use bigger value. If you want to empasize them use
+ * value smaller then 0. Default is 0.0f.
+ * @param refractionStrength
+ */
+ public void setRefractionStrength(float refractionStrength) {
+ this.refractionStrength = refractionStrength;
+ if (material != null) {
+ material.setFloat("RefractionStrength", refractionStrength);
+ }
+ }
+
+ /**
+ * returns the scale factor of the waves height map
+ * @return
+ */
+ public float getWaveScale() {
+ return waveScale;
+ }
+
+ /**
+ * Sets the scale factor of the waves height map
+ * the smaller the value the bigger the waves
+ * default is 0.005f
+ * @param waveScale
+ */
+ public void setWaveScale(float waveScale) {
+ this.waveScale = waveScale;
+ if (material != null) {
+ material.setFloat("WaveScale", waveScale);
+ }
+ }
+
+ /**
+ * returns the foam existance vector
+ * @return
+ */
+ public Vector3f getFoamExistence() {
+ return foamExistence;
+ }
+
+ /**
+ * Describes at what depth foam starts to fade out and
+ * at what it is completely invisible. The third value is at
+ * what height foam for waves appear (+ waterHeight).
+ * default is (0.45, 4.35, 1.0);
+ * @param foamExistence
+ */
+ public void setFoamExistence(Vector3f foamExistence) {
+ this.foamExistence = foamExistence;
+ if (material != null) {
+ material.setVector3("FoamExistence", foamExistence);
+ }
+ }
+
+ /**
+ * gets the scale of the sun
+ * @return
+ */
+ public float getSunScale() {
+ return sunScale;
+ }
+
+ /**
+ * Sets the scale of the sun for specular effect
+ * @param sunScale
+ */
+ public void setSunScale(float sunScale) {
+ this.sunScale = sunScale;
+ if (material != null) {
+ material.setFloat("SunScale", sunScale);
+ }
+ }
+
+ /**
+ * Returns the color exctinction vector of the water
+ * @return
+ */
+ public Vector3f getColorExtinction() {
+ return colorExtinction;
+ }
+
+ /**
+ * Return at what depth the refraction color extinct
+ * the first value is for red
+ * the second is for green
+ * the third is for blue
+ * Play with thos parameters to "trouble" the water
+ * default is (5.0, 20.0, 30.0f);
+ * @param colorExtinction
+ */
+ public void setColorExtinction(Vector3f colorExtinction) {
+ this.colorExtinction = colorExtinction;
+ if (material != null) {
+ material.setVector3("ColorExtinction", colorExtinction);
+ }
+ }
+
+ /**
+ * Sets the foam texture
+ * @param foamTexture
+ */
+ public void setFoamTexture(Texture2D foamTexture) {
+ this.foamTexture = foamTexture;
+ foamTexture.setWrap(WrapMode.Repeat);
+ if (material != null) {
+ material.setTexture("FoamMap", foamTexture);
+ }
+ }
+
+ /**
+ * Sets the height texture
+ * @param heightTexture
+ */
+ public void setHeightTexture(Texture2D heightTexture) {
+ this.heightTexture = heightTexture;
+ heightTexture.setWrap(WrapMode.Repeat);
+ }
+
+ /**
+ * Sets the normal Texture
+ * @param normalTexture
+ */
+ public void setNormalTexture(Texture2D normalTexture) {
+ this.normalTexture = normalTexture;
+ normalTexture.setWrap(WrapMode.Repeat);
+ }
+
+ /**
+ * return the shininess factor of the water
+ * @return
+ */
+ public float getShininess() {
+ return shininess;
+ }
+
+ /**
+ * Sets the shinines factor of the water
+ * default is 0.7f
+ * @param shininess
+ */
+ public void setShininess(float shininess) {
+ this.shininess = shininess;
+ if (material != null) {
+ material.setFloat("Shininess", shininess);
+ }
+ }
+
+ /**
+ * retruns the speed of the waves
+ * @return
+ */
+ public float getSpeed() {
+ return speed;
+ }
+
+ /**
+ * Set the speed of the waves (0.0 is still) default is 1.0
+ * @param speed
+ */
+ public void setSpeed(float speed) {
+ this.speed = speed;
+ }
+
+ /**
+ * returns the color of the water
+ *
+ * @return
+ */
+ public ColorRGBA getWaterColor() {
+ return waterColor;
+ }
+
+ /**
+ * Sets the color of the water
+ * see setDeepWaterColor for deep water color
+ * default is (0.0078f, 0.5176f, 0.5f,1.0f) (greenish blue)
+ * @param waterColor
+ */
+ public void setWaterColor(ColorRGBA waterColor) {
+ this.waterColor = waterColor;
+ if (material != null) {
+ material.setColor("WaterColor", waterColor);
+ }
+ }
+
+ /**
+ * returns the deep water color
+ * @return
+ */
+ public ColorRGBA getDeepWaterColor() {
+ return deepWaterColor;
+ }
+
+ /**
+ * sets the deep water color
+ * see setWaterColor for general color
+ * default is (0.0039f, 0.00196f, 0.145f,1.0f) (very dark blue)
+ * @param deepWaterColor
+ */
+ public void setDeepWaterColor(ColorRGBA deepWaterColor) {
+ this.deepWaterColor = deepWaterColor;
+ if (material != null) {
+ material.setColor("DeepWaterColor", deepWaterColor);
+ }
+ }
+
+ /**
+ * returns the wind direction
+ * @return
+ */
+ public Vector2f getWindDirection() {
+ return windDirection;
+ }
+
+ /**
+ * sets the wind direction
+ * the direction where the waves move
+ * default is (0.0f, -1.0f)
+ * @param windDirection
+ */
+ public void setWindDirection(Vector2f windDirection) {
+ this.windDirection = windDirection;
+ if (material != null) {
+ material.setVector2("WindDirection", windDirection);
+ }
+ }
+
+ /**
+ * returns the size of the reflection map
+ * @return
+ */
+ public int getReflectionMapSize() {
+ return reflectionMapSize;
+ }
+
+ /**
+ * Sets the size of the reflection map
+ * default is 512, the higher, the better quality, but the slower the effect.
+ * @param reflectionMapSize
+ */
+ public void setReflectionMapSize(int reflectionMapSize) {
+ this.reflectionMapSize = reflectionMapSize;
+ }
+
+ /**
+ * returns true if the water uses foam
+ * @return
+ */
+ public boolean isUseFoam() {
+ return useFoam;
+ }
+
+ /**
+ * set to true to use foam with water
+ * default true
+ * @param useFoam
+ */
+ public void setUseFoam(boolean useFoam) {
+ this.useFoam = useFoam;
+ if (material != null) {
+ material.setBoolean("UseFoam", useFoam);
+ }
+
+ }
+
+ /**
+ * sets the texture to use to render caustics on the ground underwater
+ * @param causticsTexture
+ */
+ public void setCausticsTexture(Texture2D causticsTexture) {
+ this.causticsTexture = causticsTexture;
+ if (material != null) {
+ material.setTexture("causticsMap", causticsTexture);
+ }
+ }
+
+ /**
+ * returns true if caustics are rendered
+ * @return
+ */
+ public boolean isUseCaustics() {
+ return useCaustics;
+ }
+
+ /**
+ * set to true if you want caustics to be rendered on the ground underwater, false otherwise
+ * @param useCaustics
+ */
+ public void setUseCaustics(boolean useCaustics) {
+ this.useCaustics = useCaustics;
+ if (material != null) {
+ material.setBoolean("UseCaustics", useCaustics);
+ }
+ }
+
+ /**
+ * return true
+ * @return
+ */
+ public boolean isUseHQShoreline() {
+ return useHQShoreline;
+ }
+
+ public void setUseHQShoreline(boolean useHQShoreline) {
+ this.useHQShoreline = useHQShoreline;
+ if (material != null) {
+ material.setBoolean("UseHQShoreline", useHQShoreline);
+ }
+
+ }
+
+ /**
+ * returns true if the water use the refraction
+ * @return
+ */
+ public boolean isUseRefraction() {
+ return useRefraction;
+ }
+
+ /**
+ * set to true to use refraction (default is true)
+ * @param useRefraction
+ */
+ public void setUseRefraction(boolean useRefraction) {
+ this.useRefraction = useRefraction;
+ if (material != null) {
+ material.setBoolean("UseRefraction", useRefraction);
+ }
+
+ }
+
+ /**
+ * returns true if the ater use ripples
+ * @return
+ */
+ public boolean isUseRipples() {
+ return useRipples;
+ }
+
+ /**
+ *
+ * Set to true tu use ripples
+ * @param useRipples
+ */
+ public void setUseRipples(boolean useRipples) {
+ this.useRipples = useRipples;
+ if (material != null) {
+ material.setBoolean("UseRipples", useRipples);
+ }
+
+ }
+
+ /**
+ * returns true if the water use specular
+ * @return
+ */
+ public boolean isUseSpecular() {
+ return useSpecular;
+ }
+
+ /**
+ * Set to true to use specular lightings on the water
+ * @param useSpecular
+ */
+ public void setUseSpecular(boolean useSpecular) {
+ this.useSpecular = useSpecular;
+ if (material != null) {
+ material.setBoolean("UseSpecular", useSpecular);
+ }
+ }
+
+ /**
+ * returns the foam intensity
+ * @return
+ */
+ public float getFoamIntensity() {
+ return foamIntensity;
+ }
+
+ /**
+ * sets the foam intensity default is 0.5f
+ * @param foamIntensity
+ */
+ public void setFoamIntensity(float foamIntensity) {
+ this.foamIntensity = foamIntensity;
+ if (material != null) {
+ material.setFloat("FoamIntensity", foamIntensity);
+
+ }
+ }
+
+ /**
+ * returns the reflection displace
+ * see {@link setReflectionDisplace(float reflectionDisplace)}
+ * @return
+ */
+ public float getReflectionDisplace() {
+ return reflectionDisplace;
+ }
+
+ /**
+ * Sets the reflection displace. define how troubled will look the reflection in the water. default is 30
+ * @param reflectionDisplace
+ */
+ public void setReflectionDisplace(float reflectionDisplace) {
+ this.reflectionDisplace = reflectionDisplace;
+ if (material != null) {
+ material.setFloat("m_ReflectionDisplace", reflectionDisplace);
+ }
+ }
+
+ /**
+ * returns true if the camera is under the water level
+ * @return
+ */
+ public boolean isUnderWater() {
+ return underWater;
+ }
+
+ /**
+ * returns the distance of the fog when under water
+ * @return
+ */
+ public float getUnderWaterFogDistance() {
+ return underWaterFogDistance;
+ }
+
+ /**
+ * sets the distance of the fog when under water.
+ * default is 120 (120 world units) use a high value to raise the view range under water
+ * @param underWaterFogDistance
+ */
+ public void setUnderWaterFogDistance(float underWaterFogDistance) {
+ this.underWaterFogDistance = underWaterFogDistance;
+ if (material != null) {
+ material.setFloat("UnderWaterFogDistance", underWaterFogDistance);
+ }
+ }
+
+ /**
+ * get the intensity of caustics under water
+ * @return
+ */
+ public float getCausticsIntensity() {
+ return causticsIntensity;
+ }
+
+ /**
+ * sets the intensity of caustics under water. goes from 0 to 1, default is 0.5f
+ * @param causticsIntensity
+ */
+ public void setCausticsIntensity(float causticsIntensity) {
+ this.causticsIntensity = causticsIntensity;
+ if (material != null) {
+ material.setFloat("CausticsIntensity", causticsIntensity);
+ }
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/ClasspathLocator.java b/engine/src/core-plugins/com/jme3/asset/plugins/ClasspathLocator.java
new file mode 100644
index 0000000..f3570de
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/ClasspathLocator.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.system.JmeSystem;
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ * The <code>ClasspathLocator</code> looks up an asset in the classpath.
+ * @author Kirill Vainer
+ */
+public class ClasspathLocator implements AssetLocator {
+
+ private static final Logger logger = Logger.getLogger(ClasspathLocator.class.getName());
+ private String root = "";
+
+ public ClasspathLocator(){
+ }
+
+ public void setRootPath(String rootPath) {
+ this.root = rootPath;
+ if (root.equals("/"))
+ root = "";
+ else if (root.length() > 1){
+ if (root.startsWith("/")){
+ root = root.substring(1);
+ }
+ if (!root.endsWith("/"))
+ root += "/";
+ }
+ }
+
+ public AssetInfo locate(AssetManager manager, AssetKey key) {
+ URL url;
+ String name = key.getName();
+ if (name.startsWith("/"))
+ name = name.substring(1);
+
+ name = root + name;
+// if (!name.startsWith(root)){
+// name = root + name;
+// }
+
+ if (JmeSystem.isLowPermissions()){
+ url = ClasspathLocator.class.getResource("/" + name);
+ }else{
+ url = Thread.currentThread().getContextClassLoader().getResource(name);
+ }
+ if (url == null)
+ return null;
+
+ if (url.getProtocol().equals("file")){
+ try {
+ String path = new File(url.toURI()).getCanonicalPath();
+
+ // convert to / for windows
+ if (File.separatorChar == '\\'){
+ path = path.replace('\\', '/');
+ }
+
+ // compare path
+ if (!path.endsWith(name)){
+ throw new AssetNotFoundException("Asset name doesn't match requirements.\n"+
+ "\"" + path + "\" doesn't match \"" + name + "\"");
+ }
+ } catch (URISyntaxException ex) {
+ throw new AssetLoadException("Error converting URL to URI", ex);
+ } catch (IOException ex){
+ throw new AssetLoadException("Failed to get canonical path for " + url, ex);
+ }
+ }
+
+ try{
+ return UrlAssetInfo.create(manager, key, url);
+ }catch (IOException ex){
+ // This is different handling than URL locator
+ // since classpath locating would return null at the getResource()
+ // call, otherwise there's a more critical error...
+ throw new AssetLoadException("Failed to read URL " + url, ex);
+ }
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/FileLocator.java b/engine/src/core-plugins/com/jme3/asset/plugins/FileLocator.java
new file mode 100644
index 0000000..f448fa3
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/FileLocator.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.*;
+import java.io.*;
+
+/**
+ * <code>FileLocator</code> allows you to specify a folder where to
+ * look for assets.
+ * @author Kirill Vainer
+ */
+public class FileLocator implements AssetLocator {
+
+ private File root;
+
+ public void setRootPath(String rootPath) {
+ if (rootPath == null)
+ throw new NullPointerException();
+
+ try {
+ root = new File(rootPath).getCanonicalFile();
+ if (!root.isDirectory()){
+ throw new IllegalArgumentException("Given root path \"" + root + "\" not a directory");
+ }
+ } catch (IOException ex) {
+ throw new AssetLoadException("Root path is invalid", ex);
+ }
+ }
+
+ private static class AssetInfoFile extends AssetInfo {
+
+ private File file;
+
+ public AssetInfoFile(AssetManager manager, AssetKey key, File file){
+ super(manager, key);
+ this.file = file;
+ }
+
+ @Override
+ public InputStream openStream() {
+ try{
+ return new FileInputStream(file);
+ }catch (FileNotFoundException ex){
+ // NOTE: Can still happen even if file.exists() is true, e.g.
+ // permissions issue and similar
+ throw new AssetLoadException("Failed to open file: " + file, ex);
+ }
+ }
+ }
+
+ public AssetInfo locate(AssetManager manager, AssetKey key) {
+ String name = key.getName();
+ File file = new File(root, name);
+ if (file.exists() && file.isFile()){
+ try {
+ // Now, check asset name requirements
+ String canonical = file.getCanonicalPath();
+ String absolute = file.getAbsolutePath();
+ if (!canonical.endsWith(absolute)){
+ throw new AssetNotFoundException("Asset name doesn't match requirements.\n"+
+ "\"" + canonical + "\" doesn't match \"" + absolute + "\"");
+ }
+ } catch (IOException ex) {
+ throw new AssetLoadException("Failed to get file canonical path " + file, ex);
+ }
+
+ return new AssetInfoFile(manager, key, file);
+ }else{
+ return null;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java
new file mode 100644
index 0000000..0637221
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLocator;
+import com.jme3.asset.AssetManager;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipEntry;
+
+public class HttpZipLocator implements AssetLocator {
+
+ private static final Logger logger = Logger.getLogger(HttpZipLocator.class.getName());
+
+ private URL zipUrl;
+ private String rootPath = "";
+ private int numEntries;
+ private int tableOffset;
+ private int tableLength;
+ private HashMap<String, ZipEntry2> entries;
+
+ private static final ByteBuffer byteBuf = ByteBuffer.allocate(250);
+ private static final CharBuffer charBuf = CharBuffer.allocate(250);
+ private static final CharsetDecoder utf8Decoder;
+
+ public static final long LOCSIG = 0x4034b50, EXTSIG = 0x8074b50,
+ CENSIG = 0x2014b50, ENDSIG = 0x6054b50;
+
+ public static final int LOCHDR = 30, EXTHDR = 16, CENHDR = 46, ENDHDR = 22,
+ LOCVER = 4, LOCFLG = 6, LOCHOW = 8, LOCTIM = 10, LOCCRC = 14,
+ LOCSIZ = 18, LOCLEN = 22, LOCNAM = 26, LOCEXT = 28, EXTCRC = 4,
+ EXTSIZ = 8, EXTLEN = 12, CENVEM = 4, CENVER = 6, CENFLG = 8,
+ CENHOW = 10, CENTIM = 12, CENCRC = 16, CENSIZ = 20, CENLEN = 24,
+ CENNAM = 28, CENEXT = 30, CENCOM = 32, CENDSK = 34, CENATT = 36,
+ CENATX = 38, CENOFF = 42, ENDSUB = 8, ENDTOT = 10, ENDSIZ = 12,
+ ENDOFF = 16, ENDCOM = 20;
+
+ static {
+ Charset utf8 = Charset.forName("UTF-8");
+ utf8Decoder = utf8.newDecoder();
+ }
+
+ private static class ZipEntry2 {
+ String name;
+ int length;
+ int offset;
+ int compSize;
+ long crc;
+ boolean deflate;
+
+ @Override
+ public String toString(){
+ return "ZipEntry[name=" + name +
+ ", length=" + length +
+ ", compSize=" + compSize +
+ ", offset=" + offset + "]";
+ }
+ }
+
+ private static int get16(byte[] b, int off) {
+ return (b[off++] & 0xff) |
+ ((b[off] & 0xff) << 8);
+ }
+
+ private static int get32(byte[] b, int off) {
+ return (b[off++] & 0xff) |
+ ((b[off++] & 0xff) << 8) |
+ ((b[off++] & 0xff) << 16) |
+ ((b[off] & 0xff) << 24);
+ }
+
+ private static long getu32(byte[] b, int off) throws IOException{
+ return (b[off++]&0xff) |
+ ((b[off++]&0xff) << 8) |
+ ((b[off++]&0xff) << 16) |
+ (((long)(b[off]&0xff)) << 24);
+ }
+
+ private static String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException {
+ StringBuilder sb = new StringBuilder();
+
+ int read = 0;
+ while (read < len){
+ // Either read n remaining bytes in b or 250 if n is higher.
+ int toRead = Math.min(len - read, byteBuf.capacity());
+
+ boolean endOfInput = toRead < byteBuf.capacity();
+
+ // read 'toRead' bytes into byteBuf
+ byteBuf.put(b, off + read, toRead);
+
+ // set limit to position and set position to 0
+ // so data can be decoded
+ byteBuf.flip();
+
+ // decode data in byteBuf
+ CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput);
+
+ // if the result is not an underflow its an error
+ // that cannot be handled.
+ // if the error is an underflow and its the end of input
+ // then the decoder expects more bytes but there are no more => error
+ if (!result.isUnderflow() || !endOfInput){
+ result.throwException();
+ }
+
+ // flip the char buf to get the string just decoded
+ charBuf.flip();
+
+ // append the decoded data into the StringBuilder
+ sb.append(charBuf.toString());
+
+ // clear buffers for next use
+ byteBuf.clear();
+ charBuf.clear();
+
+ read += toRead;
+ }
+
+ return sb.toString();
+ }
+
+ private InputStream readData(int offset, int length) throws IOException{
+ HttpURLConnection conn = (HttpURLConnection) zipUrl.openConnection();
+ conn.setDoOutput(false);
+ conn.setUseCaches(false);
+ conn.setInstanceFollowRedirects(false);
+ String range = "-";
+ if (offset != Integer.MAX_VALUE){
+ range = offset + range;
+ }
+ if (length != Integer.MAX_VALUE){
+ if (offset != Integer.MAX_VALUE){
+ range = range + (offset + length - 1);
+ }else{
+ range = range + length;
+ }
+ }
+
+ conn.setRequestProperty("Range", "bytes=" + range);
+ conn.connect();
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
+ return conn.getInputStream();
+ }else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){
+ throw new IOException("Your server does not support HTTP feature Content-Range. Please contact your server administrator.");
+ }else{
+ throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage());
+ }
+ }
+
+ private int readTableEntry(byte[] table, int offset) throws IOException{
+ if (get32(table, offset) != CENSIG){
+ throw new IOException("Central directory error, expected 'PK12'");
+ }
+
+ int nameLen = get16(table, offset + CENNAM);
+ int extraLen = get16(table, offset + CENEXT);
+ int commentLen = get16(table, offset + CENCOM);
+ int newOffset = offset + CENHDR + nameLen + extraLen + commentLen;
+
+ int flags = get16(table, offset + CENFLG);
+ if ((flags & 1) == 1){
+ // ignore this entry, it uses encryption
+ return newOffset;
+ }
+
+ int method = get16(table, offset + CENHOW);
+ if (method != ZipEntry.DEFLATED && method != ZipEntry.STORED){
+ // ignore this entry, it uses unknown compression method
+ return newOffset;
+ }
+
+ String name = getUTF8String(table, offset + CENHDR, nameLen);
+ if (name.charAt(name.length()-1) == '/'){
+ // ignore this entry, it is directory node
+ // or it has no name (?)
+ return newOffset;
+ }
+
+ ZipEntry2 entry = new ZipEntry2();
+ entry.name = name;
+ entry.deflate = (method == ZipEntry.DEFLATED);
+ entry.crc = getu32(table, offset + CENCRC);
+ entry.length = get32(table, offset + CENLEN);
+ entry.compSize = get32(table, offset + CENSIZ);
+ entry.offset = get32(table, offset + CENOFF);
+
+ // we want offset directly into file data ..
+ // move the offset forward to skip the LOC header
+ entry.offset += LOCHDR + nameLen + extraLen;
+
+ entries.put(entry.name, entry);
+
+ return newOffset;
+ }
+
+ private void fillByteArray(byte[] array, InputStream source) throws IOException{
+ int total = 0;
+ int length = array.length;
+ while (total < length) {
+ int read = source.read(array, total, length - total);
+ if (read < 0)
+ throw new IOException("Failed to read entire array");
+
+ total += read;
+ }
+ }
+
+ private void readCentralDirectory() throws IOException{
+ InputStream in = readData(tableOffset, tableLength);
+ byte[] header = new byte[tableLength];
+
+ // Fix for "PK12 bug in town.zip": sometimes
+ // not entire byte array will be read with InputStream.read()
+ // (especially for big headers)
+ fillByteArray(header, in);
+
+// in.read(header);
+ in.close();
+
+ entries = new HashMap<String, ZipEntry2>(numEntries);
+ int offset = 0;
+ for (int i = 0; i < numEntries; i++){
+ offset = readTableEntry(header, offset);
+ }
+ }
+
+ private void readEndHeader() throws IOException{
+
+// InputStream in = readData(Integer.MAX_VALUE, ENDHDR);
+// byte[] header = new byte[ENDHDR];
+// fillByteArray(header, in);
+// in.close();
+//
+// if (get32(header, 0) != ENDSIG){
+// throw new IOException("End header error, expected 'PK56'");
+// }
+
+ // Fix for "PK56 bug in town.zip":
+ // If there's a zip comment inside the end header,
+ // PK56 won't appear in the -22 position relative to the end of the
+ // file!
+ // In that case, we have to search for it.
+ // Increase search space to 200 bytes
+
+ InputStream in = readData(Integer.MAX_VALUE, 200);
+ byte[] header = new byte[200];
+ fillByteArray(header, in);
+ in.close();
+
+ int offset = -1;
+ for (int i = 200 - 22; i >= 0; i--){
+ if (header[i] == (byte) (ENDSIG & 0xff)
+ && get32(header, i) == ENDSIG){
+ // found location
+ offset = i;
+ break;
+ }
+ }
+ if (offset == -1)
+ throw new IOException("Cannot find Zip End Header in file!");
+
+ numEntries = get16(header, offset + ENDTOT);
+ tableLength = get32(header, offset + ENDSIZ);
+ tableOffset = get32(header, offset + ENDOFF);
+ }
+
+ public void load(URL url) throws IOException {
+ if (!url.getProtocol().equals("http"))
+ throw new UnsupportedOperationException();
+
+ zipUrl = url;
+ readEndHeader();
+ readCentralDirectory();
+ }
+
+ private InputStream openStream(ZipEntry2 entry) throws IOException{
+ InputStream in = readData(entry.offset, entry.compSize);
+ if (entry.deflate){
+ return new InflaterInputStream(in, new Inflater(true));
+ }
+ return in;
+ }
+
+ public InputStream openStream(String name) throws IOException{
+ ZipEntry2 entry = entries.get(name);
+ if (entry == null)
+ throw new RuntimeException("Entry not found: "+name);
+
+ return openStream(entry);
+ }
+
+ public void setRootPath(String path){
+ if (!rootPath.equals(path)){
+ rootPath = path;
+ try {
+ load(new URL(path));
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Failed to set root path "+path, ex);
+ }
+ }
+ }
+
+ public AssetInfo locate(AssetManager manager, AssetKey key){
+ final ZipEntry2 entry = entries.get(key.getName());
+ if (entry == null)
+ return null;
+
+ return new AssetInfo(manager, key){
+ @Override
+ public InputStream openStream() {
+ try {
+ return HttpZipLocator.this.openStream(entry);
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Error retrieving "+entry.name, ex);
+ return null;
+ }
+ }
+ };
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~
new file mode 100644
index 0000000..f5fbfd1
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLocator;
+import com.jme3.asset.AssetManager;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipEntry;
+
+public class HttpZipLocator implements AssetLocator {
+
+ private static final Logger logger = Logger.getLogger(HttpZipLocator.class.getName());
+
+ private URL zipUrl;
+ private String rootPath = "";
+ private int numEntries;
+ private int tableOffset;
+ private int tableLength;
+ private HashMap<String, ZipEntry2> entries;
+
+ private static final ByteBuffer byteBuf = ByteBuffer.allocate(250);
+ private static final CharBuffer charBuf = CharBuffer.allocate(250);
+ private static final CharsetDecoder utf8Decoder;
+
+ static {
+ Charset utf8 = Charset.forName("UTF-8");
+ utf8Decoder = utf8.newDecoder();
+ }
+
+ private static class ZipEntry2 {
+ String name;
+ int length;
+ int offset;
+ int compSize;
+ long crc;
+ boolean deflate;
+
+ @Override
+ public String toString(){
+ return "ZipEntry[name=" + name +
+ ", length=" + length +
+ ", compSize=" + compSize +
+ ", offset=" + offset + "]";
+ }
+ }
+
+ private static int get16(byte[] b, int off) {
+ return (b[off++] & 0xff) |
+ ((b[off] & 0xff) << 8);
+ }
+
+ private static int get32(byte[] b, int off) {
+ return (b[off++] & 0xff) |
+ ((b[off++] & 0xff) << 8) |
+ ((b[off++] & 0xff) << 16) |
+ ((b[off] & 0xff) << 24);
+ }
+
+ private static long getu32(byte[] b, int off) throws IOException{
+ return (b[off++]&0xff) |
+ ((b[off++]&0xff) << 8) |
+ ((b[off++]&0xff) << 16) |
+ (((long)(b[off]&0xff)) << 24);
+ }
+
+ private static String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException {
+ StringBuilder sb = new StringBuilder();
+
+ int read = 0;
+ while (read < len){
+ // Either read n remaining bytes in b or 250 if n is higher.
+ int toRead = Math.min(len - read, byteBuf.capacity());
+
+ boolean endOfInput = toRead < byteBuf.capacity();
+
+ // read 'toRead' bytes into byteBuf
+ byteBuf.put(b, off + read, toRead);
+
+ // set limit to position and set position to 0
+ // so data can be decoded
+ byteBuf.flip();
+
+ // decode data in byteBuf
+ CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput);
+
+ // if the result is not an underflow its an error
+ // that cannot be handled.
+ // if the error is an underflow and its the end of input
+ // then the decoder expects more bytes but there are no more => error
+ if (!result.isUnderflow() || !endOfInput){
+ result.throwException();
+ }
+
+ // flip the char buf to get the string just decoded
+ charBuf.flip();
+
+ // append the decoded data into the StringBuilder
+ sb.append(charBuf.toString());
+
+ // clear buffers for next use
+ byteBuf.clear();
+ charBuf.clear();
+
+ read += toRead;
+ }
+
+ return sb.toString();
+ }
+
+ private InputStream readData(int offset, int length) throws IOException{
+ HttpURLConnection conn = (HttpURLConnection) zipUrl.openConnection();
+ conn.setDoOutput(false);
+ conn.setUseCaches(false);
+ conn.setInstanceFollowRedirects(false);
+ String range = "-";
+ if (offset != Integer.MAX_VALUE){
+ range = offset + range;
+ }
+ if (length != Integer.MAX_VALUE){
+ if (offset != Integer.MAX_VALUE){
+ range = range + (offset + length - 1);
+ }else{
+ range = range + length;
+ }
+ }
+
+ conn.setRequestProperty("Range", "bytes=" + range);
+ conn.connect();
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
+ return conn.getInputStream();
+ }else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){
+ throw new IOException("Your server does not support HTTP feature Content-Range. Please contact your server administrator.");
+ }else{
+ throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage());
+ }
+ }
+
+ private int readTableEntry(byte[] table, int offset) throws IOException{
+ if (get32(table, offset) != ZipEntry.CENSIG){
+ throw new IOException("Central directory error, expected 'PK12'");
+ }
+
+ int nameLen = get16(table, offset + ZipEntry.CENNAM);
+ int extraLen = get16(table, offset + ZipEntry.CENEXT);
+ int commentLen = get16(table, offset + ZipEntry.CENCOM);
+ int newOffset = offset + ZipEntry.CENHDR + nameLen + extraLen + commentLen;
+
+ int flags = get16(table, offset + ZipEntry.CENFLG);
+ if ((flags & 1) == 1){
+ // ignore this entry, it uses encryption
+ return newOffset;
+ }
+
+ int method = get16(table, offset + ZipEntry.CENHOW);
+ if (method != ZipEntry.DEFLATED && method != ZipEntry.STORED){
+ // ignore this entry, it uses unknown compression method
+ return newOffset;
+ }
+
+ String name = getUTF8String(table, offset + ZipEntry.CENHDR, nameLen);
+ if (name.charAt(name.length()-1) == '/'){
+ // ignore this entry, it is directory node
+ // or it has no name (?)
+ return newOffset;
+ }
+
+ ZipEntry2 entry = new ZipEntry2();
+ entry.name = name;
+ entry.deflate = (method == ZipEntry.DEFLATED);
+ entry.crc = getu32(table, offset + ZipEntry.CENCRC);
+ entry.length = get32(table, offset + ZipEntry.CENLEN);
+ entry.compSize = get32(table, offset + ZipEntry.CENSIZ);
+ entry.offset = get32(table, offset + ZipEntry.CENOFF);
+
+ // we want offset directly into file data ..
+ // move the offset forward to skip the LOC header
+ entry.offset += ZipEntry.LOCHDR + nameLen + extraLen;
+
+ entries.put(entry.name, entry);
+
+ return newOffset;
+ }
+
+ private void fillByteArray(byte[] array, InputStream source) throws IOException{
+ int total = 0;
+ int length = array.length;
+ while (total < length) {
+ int read = source.read(array, total, length - total);
+ if (read < 0)
+ throw new IOException("Failed to read entire array");
+
+ total += read;
+ }
+ }
+
+ private void readCentralDirectory() throws IOException{
+ InputStream in = readData(tableOffset, tableLength);
+ byte[] header = new byte[tableLength];
+
+ // Fix for "PK12 bug in town.zip": sometimes
+ // not entire byte array will be read with InputStream.read()
+ // (especially for big headers)
+ fillByteArray(header, in);
+
+// in.read(header);
+ in.close();
+
+ entries = new HashMap<String, ZipEntry2>(numEntries);
+ int offset = 0;
+ for (int i = 0; i < numEntries; i++){
+ offset = readTableEntry(header, offset);
+ }
+ }
+
+ private void readEndHeader() throws IOException{
+
+// InputStream in = readData(Integer.MAX_VALUE, ZipEntry.ENDHDR);
+// byte[] header = new byte[ZipEntry.ENDHDR];
+// fillByteArray(header, in);
+// in.close();
+//
+// if (get32(header, 0) != ZipEntry.ENDSIG){
+// throw new IOException("End header error, expected 'PK56'");
+// }
+
+ // Fix for "PK56 bug in town.zip":
+ // If there's a zip comment inside the end header,
+ // PK56 won't appear in the -22 position relative to the end of the
+ // file!
+ // In that case, we have to search for it.
+ // Increase search space to 200 bytes
+
+ InputStream in = readData(Integer.MAX_VALUE, 200);
+ byte[] header = new byte[200];
+ fillByteArray(header, in);
+ in.close();
+
+ int offset = -1;
+ for (int i = 200 - 22; i >= 0; i--){
+ if (header[i] == (byte) (ZipEntry.ENDSIG & 0xff)
+ && get32(header, i) == ZipEntry.ENDSIG){
+ // found location
+ offset = i;
+ break;
+ }
+ }
+ if (offset == -1)
+ throw new IOException("Cannot find Zip End Header in file!");
+
+ numEntries = get16(header, offset + ZipEntry.ENDTOT);
+ tableLength = get32(header, offset + ZipEntry.ENDSIZ);
+ tableOffset = get32(header, offset + ZipEntry.ENDOFF);
+ }
+
+ public void load(URL url) throws IOException {
+ if (!url.getProtocol().equals("http"))
+ throw new UnsupportedOperationException();
+
+ zipUrl = url;
+ readEndHeader();
+ readCentralDirectory();
+ }
+
+ private InputStream openStream(ZipEntry2 entry) throws IOException{
+ InputStream in = readData(entry.offset, entry.compSize);
+ if (entry.deflate){
+ return new InflaterInputStream(in, new Inflater(true));
+ }
+ return in;
+ }
+
+ public InputStream openStream(String name) throws IOException{
+ ZipEntry2 entry = entries.get(name);
+ if (entry == null)
+ throw new RuntimeException("Entry not found: "+name);
+
+ return openStream(entry);
+ }
+
+ public void setRootPath(String path){
+ if (!rootPath.equals(path)){
+ rootPath = path;
+ try {
+ load(new URL(path));
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Failed to set root path "+path, ex);
+ }
+ }
+ }
+
+ public AssetInfo locate(AssetManager manager, AssetKey key){
+ final ZipEntry2 entry = entries.get(key.getName());
+ if (entry == null)
+ return null;
+
+ return new AssetInfo(manager, key){
+ @Override
+ public InputStream openStream() {
+ try {
+ return HttpZipLocator.this.openStream(entry);
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Error retrieving "+entry.name, ex);
+ return null;
+ }
+ }
+ };
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/UrlAssetInfo.java b/engine/src/core-plugins/com/jme3/asset/plugins/UrlAssetInfo.java
new file mode 100644
index 0000000..941764f
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/UrlAssetInfo.java
@@ -0,0 +1,65 @@
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoadException;
+import com.jme3.asset.AssetManager;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * Handles loading of assets from a URL
+ *
+ * @author Kirill Vainer
+ */
+public class UrlAssetInfo extends AssetInfo {
+
+ private URL url;
+ private InputStream in;
+
+ public static UrlAssetInfo create(AssetManager assetManager, AssetKey key, URL url) throws IOException {
+ // Check if URL can be reached. This will throw
+ // IOException which calling code will handle.
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ InputStream in = conn.getInputStream();
+
+ // For some reason url cannot be reached?
+ if (in == null){
+ return null;
+ }else{
+ return new UrlAssetInfo(assetManager, key, url, in);
+ }
+ }
+
+ private UrlAssetInfo(AssetManager assetManager, AssetKey key, URL url, InputStream in) throws IOException {
+ super(assetManager, key);
+ this.url = url;
+ this.in = in;
+ }
+
+ public boolean hasInitialConnection(){
+ return in != null;
+ }
+
+ @Override
+ public InputStream openStream() {
+ if (in != null){
+ // Reuse the already existing stream (only once)
+ InputStream in2 = in;
+ in = null;
+ return in2;
+ }else{
+ // Create a new stream for subsequent invocations.
+ try {
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ return conn.getInputStream();
+ } catch (IOException ex) {
+ throw new AssetLoadException("Failed to read URL " + url, ex);
+ }
+ }
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/UrlLocator.java b/engine/src/core-plugins/com/jme3/asset/plugins/UrlLocator.java
new file mode 100644
index 0000000..e770930
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/UrlLocator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLocator;
+import com.jme3.asset.AssetManager;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>UrlLocator</code> is a locator that combines a root URL
+ * and the given path in the AssetKey to construct a new URL
+ * that allows locating the asset.
+ * @author Kirill Vainer
+ */
+public class UrlLocator implements AssetLocator {
+
+ private static final Logger logger = Logger.getLogger(UrlLocator.class.getName());
+ private URL root;
+
+ public void setRootPath(String rootPath) {
+ try {
+ this.root = new URL(rootPath);
+ } catch (MalformedURLException ex) {
+ throw new IllegalArgumentException("Invalid rootUrl specified", ex);
+ }
+ }
+
+ public AssetInfo locate(AssetManager manager, AssetKey key) {
+ String name = key.getName();
+ try{
+ //TODO: remove workaround for SDK
+// URL url = new URL(root, name);
+ if(name.startsWith("/")){
+ name = name.substring(1);
+ }
+ URL url = new URL(root.toExternalForm() + name);
+ return UrlAssetInfo.create(manager, key, url);
+ }catch (FileNotFoundException e){
+ return null;
+ }catch (IOException ex){
+ logger.log(Level.WARNING, "Error while locating " + name, ex);
+ return null;
+ }
+ }
+
+
+}
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/ZipLocator.java b/engine/src/core-plugins/com/jme3/asset/plugins/ZipLocator.java
new file mode 100644
index 0000000..ddfdfe8
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/asset/plugins/ZipLocator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset.plugins;
+
+import com.jme3.asset.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * <code>ZipLocator</code> is a locator that looks up resources in a .ZIP file.
+ * @author Kirill Vainer
+ */
+public class ZipLocator implements AssetLocator {
+
+ private ZipFile zipfile;
+ private static final Logger logger = Logger.getLogger(ZipLocator.class.getName());
+
+ private class JarAssetInfo extends AssetInfo {
+
+ private final ZipEntry entry;
+
+ public JarAssetInfo(AssetManager manager, AssetKey key, ZipEntry entry){
+ super(manager, key);
+ this.entry = entry;
+ }
+
+ public InputStream openStream(){
+ try{
+ return zipfile.getInputStream(entry);
+ }catch (IOException ex){
+ throw new AssetLoadException("Failed to load zip entry: "+entry, ex);
+ }
+ }
+ }
+
+ public void setRootPath(String rootPath) {
+ try{
+ zipfile = new ZipFile(new File(rootPath), ZipFile.OPEN_READ);
+ }catch (IOException ex){
+ throw new AssetLoadException("Failed to open zip file: " + rootPath, ex);
+ }
+ }
+
+ public AssetInfo locate(AssetManager manager, AssetKey key) {
+ String name = key.getName();
+ ZipEntry entry = zipfile.getEntry(name);
+ if (entry == null)
+ return null;
+
+ return new JarAssetInfo(manager, key, entry);
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/audio/plugins/WAVLoader.java b/engine/src/core-plugins/com/jme3/audio/plugins/WAVLoader.java
new file mode 100644
index 0000000..ab6a19b
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/audio/plugins/WAVLoader.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.audio.AudioBuffer;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioKey;
+import com.jme3.audio.AudioStream;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.LittleEndien;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class WAVLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(WAVLoader.class.getName());
+
+ // all these are in big endian
+ private static final int i_RIFF = 0x46464952;
+ private static final int i_WAVE = 0x45564157;
+ private static final int i_fmt = 0x20746D66;
+ private static final int i_data = 0x61746164;
+
+ private boolean readStream = false;
+
+ private AudioBuffer audioBuffer;
+ private AudioStream audioStream;
+ private AudioData audioData;
+ private int bytesPerSec;
+ private float duration;
+
+ private LittleEndien in;
+
+ private void readFormatChunk(int size) throws IOException{
+ // if other compressions are supported, size doesn't have to be 16
+// if (size != 16)
+// logger.warning("Expected size of format chunk to be 16");
+
+ int compression = in.readShort();
+ if (compression != 1){
+ throw new IOException("WAV Loader only supports PCM wave files");
+ }
+
+ int channels = in.readShort();
+ int sampleRate = in.readInt();
+
+ bytesPerSec = in.readInt(); // used to calculate duration
+
+ int bytesPerSample = in.readShort();
+ int bitsPerSample = in.readShort();
+
+ int expectedBytesPerSec = (bitsPerSample * channels * sampleRate) / 8;
+ if (expectedBytesPerSec != bytesPerSec){
+ logger.log(Level.WARNING, "Expected {0} bytes per second, got {1}",
+ new Object[]{expectedBytesPerSec, bytesPerSec});
+ }
+
+ if (bitsPerSample != 8 && bitsPerSample != 16)
+ throw new IOException("Only 8 and 16 bits per sample are supported!");
+
+ if ( (bitsPerSample / 8) * channels != bytesPerSample)
+ throw new IOException("Invalid bytes per sample value");
+
+ if (bytesPerSample * sampleRate != bytesPerSec)
+ throw new IOException("Invalid bytes per second value");
+
+ audioData.setupFormat(channels, bitsPerSample, sampleRate);
+
+ int remaining = size - 16;
+ if (remaining > 0){
+ in.skipBytes(remaining);
+ }
+ }
+
+ private void readDataChunkForBuffer(int len) throws IOException {
+ ByteBuffer data = BufferUtils.createByteBuffer(len);
+ byte[] buf = new byte[512];
+ int read = 0;
+ while ( (read = in.read(buf)) > 0){
+ data.put(buf, 0, Math.min(read, data.remaining()) );
+ }
+ data.flip();
+ audioBuffer.updateData(data);
+ in.close();
+ }
+
+ private void readDataChunkForStream(int len) throws IOException {
+ audioStream.updateData(in, duration);
+ }
+
+ private AudioData load(InputStream inputStream, boolean stream) throws IOException{
+ this.in = new LittleEndien(inputStream);
+
+ int sig = in.readInt();
+ if (sig != i_RIFF)
+ throw new IOException("File is not a WAVE file");
+
+ // skip size
+ in.readInt();
+ if (in.readInt() != i_WAVE)
+ throw new IOException("WAVE File does not contain audio");
+
+ readStream = stream;
+ if (readStream){
+ audioStream = new AudioStream();
+ audioData = audioStream;
+ }else{
+ audioBuffer = new AudioBuffer();
+ audioData = audioBuffer;
+ }
+
+ while (true) {
+ int type = in.readInt();
+ int len = in.readInt();
+
+ switch (type) {
+ case i_fmt:
+ readFormatChunk(len);
+ break;
+ case i_data:
+ // Compute duration based on data chunk size
+ duration = len / bytesPerSec;
+
+ if (readStream) {
+ readDataChunkForStream(len);
+ } else {
+ readDataChunkForBuffer(len);
+ }
+ return audioData;
+ default:
+ int skipped = in.skipBytes(len);
+ if (skipped <= 0) {
+ return null;
+ }
+ break;
+ }
+ }
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ AudioData data;
+ InputStream inputStream = null;
+ try {
+ inputStream = info.openStream();
+ data = load(inputStream, ((AudioKey)info.getKey()).isStream());
+ if (data instanceof AudioStream){
+ inputStream = null;
+ }
+ return data;
+ } finally {
+ if (inputStream != null){
+ inputStream.close();
+ }
+ }
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java
new file mode 100644
index 0000000..20da0e7
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+class BinaryClassField {
+
+ public static final byte BYTE = 0;
+ public static final byte BYTE_1D = 1;
+ public static final byte BYTE_2D = 2;
+
+ public static final byte INT = 10;
+ public static final byte INT_1D = 11;
+ public static final byte INT_2D = 12;
+
+ public static final byte FLOAT = 20;
+ public static final byte FLOAT_1D = 21;
+ public static final byte FLOAT_2D = 22;
+
+ public static final byte DOUBLE = 30;
+ public static final byte DOUBLE_1D = 31;
+ public static final byte DOUBLE_2D = 32;
+
+ public static final byte LONG = 40;
+ public static final byte LONG_1D = 41;
+ public static final byte LONG_2D = 42;
+
+ public static final byte SHORT = 50;
+ public static final byte SHORT_1D = 51;
+ public static final byte SHORT_2D = 52;
+
+ public static final byte BOOLEAN = 60;
+ public static final byte BOOLEAN_1D = 61;
+ public static final byte BOOLEAN_2D = 62;
+
+ public static final byte STRING = 70;
+ public static final byte STRING_1D = 71;
+ public static final byte STRING_2D = 72;
+
+ public static final byte BITSET = 80;
+
+ public static final byte SAVABLE = 90;
+ public static final byte SAVABLE_1D = 91;
+ public static final byte SAVABLE_2D = 92;
+
+ public static final byte SAVABLE_ARRAYLIST = 100;
+ public static final byte SAVABLE_ARRAYLIST_1D = 101;
+ public static final byte SAVABLE_ARRAYLIST_2D = 102;
+
+ public static final byte SAVABLE_MAP = 105;
+ public static final byte STRING_SAVABLE_MAP = 106;
+ public static final byte INT_SAVABLE_MAP = 107;
+
+ public static final byte FLOATBUFFER_ARRAYLIST = 110;
+ public static final byte BYTEBUFFER_ARRAYLIST = 111;
+
+ public static final byte FLOATBUFFER = 120;
+ public static final byte INTBUFFER = 121;
+ public static final byte BYTEBUFFER = 122;
+ public static final byte SHORTBUFFER = 123;
+
+
+ byte type;
+ String name;
+ byte alias;
+
+ BinaryClassField(String name, byte alias, byte type) {
+ this.name = name;
+ this.alias = alias;
+ this.type = type;
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java
new file mode 100644
index 0000000..e66a945
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import java.util.HashMap;
+
+class BinaryClassObject {
+
+ // When exporting, use nameFields field, importing use aliasFields.
+ HashMap<String, BinaryClassField> nameFields;
+ HashMap<Byte, BinaryClassField> aliasFields;
+
+ byte[] alias;
+ String className;
+ int[] classHierarchyVersions;
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java
new file mode 100644
index 0000000..b9ec78d
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.FormatVersion;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.Savable;
+import com.jme3.export.SavableClassUtil;
+import com.jme3.math.FastMath;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Exports to the jME Binary Format. Format descriptor: (each numbered item
+ * denotes a series of bytes that follows sequentially one after the next.)
+ * <p>
+ * 1. "number of classes" - four bytes - int value representing the number of
+ * entries in the class lookup table.
+ * </p>
+ * <p>
+ * CLASS TABLE: There will be X blocks each consisting of numbers 2 thru 9,
+ * where X = the number read in 1.
+ * </p>
+ * <p>
+ * 2. "class alias" - 1...X bytes, where X = ((int) FastMath.log(aliasCount,
+ * 256) + 1) - an alias used when writing object data to match an object to its
+ * appropriate object class type.
+ * </p>
+ * <p>
+ * 3. "full class name size" - four bytes - int value representing number of
+ * bytes to read in for next field.
+ * </p>
+ * <p>
+ * 4. "full class name" - 1...X bytes representing a String value, where X = the
+ * number read in 3. The String is the fully qualified class name of the Savable
+ * class, eg "<code>com.jme.math.Vector3f</code>"
+ * </p>
+ * <p>
+ * 5. "number of fields" - four bytes - int value representing number of blocks
+ * to read in next (numbers 6 - 9), where each block represents a field in this
+ * class.
+ * </p>
+ * <p>
+ * 6. "field alias" - 1 byte - the alias used when writing out fields in a
+ * class. Because it is a single byte, a single class can not save out more than
+ * a total of 256 fields.
+ * </p>
+ * <p>
+ * 7. "field type" - 1 byte - a value representing the type of data a field
+ * contains. This value is taken from the static fields of
+ * <code>com.jme.util.export.binary.BinaryClassField</code>.
+ * </p>
+ * <p>
+ * 8. "field name size" - 4 bytes - int value representing the size of the next
+ * field.
+ * </p>
+ * <p>
+ * 9. "field name" - 1...X bytes representing a String value, where X = the
+ * number read in 8. The String is the full String value used when writing the
+ * current field.
+ * </p>
+ * <p>
+ * 10. "number of unique objects" - four bytes - int value representing the
+ * number of data entries in this file.
+ * </p>
+ * <p>
+ * DATA LOOKUP TABLE: There will be X blocks each consisting of numbers 11 and
+ * 12, where X = the number read in 10.
+ * </p>
+ * <p>
+ * 11. "data id" - four bytes - int value identifying a single unique object
+ * that was saved in this data file.
+ * </p>
+ * <p>
+ * 12. "data location" - four bytes - int value representing the offset in the
+ * object data portion of this file where the object identified in 11 is
+ * located.
+ * </p>
+ * <p>
+ * 13. "future use" - four bytes - hardcoded int value 1.
+ * </p>
+ * <p>
+ * 14. "root id" - four bytes - int value identifying the top level object.
+ * </p>
+ * <p>
+ * OBJECT DATA SECTION: There will be X blocks each consisting of numbers 15
+ * thru 19, where X = the number of unique location values named in 12.
+ * <p>
+ * 15. "class alias" - see 2.
+ * </p>
+ * <p>
+ * 16. "data length" - four bytes - int value representing the length in bytes
+ * of data stored in fields 17 and 18 for this object.
+ * </p>
+ * <p>
+ * FIELD ENTRY: There will be X blocks each consisting of numbers 18 and 19
+ * </p>
+ * <p>
+ * 17. "field alias" - see 6.
+ * </p>
+ * <p>
+ * 18. "field data" - 1...X bytes representing the field data. The data length
+ * is dependent on the field type and contents.
+ * </p>
+ *
+ * @author Joshua Slack
+ */
+
+public class BinaryExporter implements JmeExporter {
+ private static final Logger logger = Logger.getLogger(BinaryExporter.class
+ .getName());
+
+ protected int aliasCount = 1;
+ protected int idCount = 1;
+
+ protected IdentityHashMap<Savable, BinaryIdContentPair> contentTable
+ = new IdentityHashMap<Savable, BinaryIdContentPair>();
+
+ protected HashMap<Integer, Integer> locationTable
+ = new HashMap<Integer, Integer>();
+
+ // key - class name, value = bco
+ private HashMap<String, BinaryClassObject> classes
+ = new HashMap<String, BinaryClassObject>();
+
+ private ArrayList<Savable> contentKeys = new ArrayList<Savable>();
+
+ public static boolean debug = false;
+ public static boolean useFastBufs = true;
+
+ public BinaryExporter() {
+ }
+
+ public static BinaryExporter getInstance() {
+ return new BinaryExporter();
+ }
+
+ public boolean save(Savable object, OutputStream os) throws IOException {
+ // reset some vars
+ aliasCount = 1;
+ idCount = 1;
+ classes.clear();
+ contentTable.clear();
+ locationTable.clear();
+ contentKeys.clear();
+
+ // write signature and version
+ os.write(ByteUtils.convertToBytes(FormatVersion.SIGNATURE));
+ os.write(ByteUtils.convertToBytes(FormatVersion.VERSION));
+
+ int id = processBinarySavable(object);
+
+ // write out tag table
+ int classTableSize = 0;
+ int classNum = classes.keySet().size();
+ int aliasSize = ((int) FastMath.log(classNum, 256) + 1); // make all
+ // aliases a
+ // fixed width
+
+ os.write(ByteUtils.convertToBytes(classNum));
+ for (String key : classes.keySet()) {
+ BinaryClassObject bco = classes.get(key);
+
+ // write alias
+ byte[] aliasBytes = fixClassAlias(bco.alias,
+ aliasSize);
+ os.write(aliasBytes);
+ classTableSize += aliasSize;
+
+ // jME3 NEW: Write class hierarchy version numbers
+ os.write( bco.classHierarchyVersions.length );
+ for (int version : bco.classHierarchyVersions){
+ os.write(ByteUtils.convertToBytes(version));
+ }
+ classTableSize += 1 + bco.classHierarchyVersions.length * 4;
+
+ // write classname size & classname
+ byte[] classBytes = key.getBytes();
+ os.write(ByteUtils.convertToBytes(classBytes.length));
+ os.write(classBytes);
+ classTableSize += 4 + classBytes.length;
+
+ // for each field, write alias, type, and name
+ os.write(ByteUtils.convertToBytes(bco.nameFields.size()));
+ for (String fieldName : bco.nameFields.keySet()) {
+ BinaryClassField bcf = bco.nameFields.get(fieldName);
+ os.write(bcf.alias);
+ os.write(bcf.type);
+
+ // write classname size & classname
+ byte[] fNameBytes = fieldName.getBytes();
+ os.write(ByteUtils.convertToBytes(fNameBytes.length));
+ os.write(fNameBytes);
+ classTableSize += 2 + 4 + fNameBytes.length;
+ }
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ // write out data to a seperate stream
+ int location = 0;
+ // keep track of location for each piece
+ HashMap<String, ArrayList<BinaryIdContentPair>> alreadySaved = new HashMap<String, ArrayList<BinaryIdContentPair>>(
+ contentTable.size());
+ for (Savable savable : contentKeys) {
+ // look back at previous written data for matches
+ String savableName = savable.getClass().getName();
+ BinaryIdContentPair pair = contentTable.get(savable);
+ ArrayList<BinaryIdContentPair> bucket = alreadySaved
+ .get(savableName + getChunk(pair));
+ int prevLoc = findPrevMatch(pair, bucket);
+ if (prevLoc != -1) {
+ locationTable.put(pair.getId(), prevLoc);
+ continue;
+ }
+
+ locationTable.put(pair.getId(), location);
+ if (bucket == null) {
+ bucket = new ArrayList<BinaryIdContentPair>();
+ alreadySaved.put(savableName + getChunk(pair), bucket);
+ }
+ bucket.add(pair);
+ byte[] aliasBytes = fixClassAlias(classes.get(savableName).alias, aliasSize);
+ out.write(aliasBytes);
+ location += aliasSize;
+ BinaryOutputCapsule cap = contentTable.get(savable).getContent();
+ out.write(ByteUtils.convertToBytes(cap.bytes.length));
+ location += 4; // length of bytes
+ out.write(cap.bytes);
+ location += cap.bytes.length;
+ }
+
+ // write out location table
+ // tag/location
+ int numLocations = locationTable.keySet().size();
+ os.write(ByteUtils.convertToBytes(numLocations));
+ int locationTableSize = 0;
+ for (Integer key : locationTable.keySet()) {
+ os.write(ByteUtils.convertToBytes(key));
+ os.write(ByteUtils.convertToBytes(locationTable.get(key)));
+ locationTableSize += 8;
+ }
+
+ // write out number of root ids - hardcoded 1 for now
+ os.write(ByteUtils.convertToBytes(1));
+
+ // write out root id
+ os.write(ByteUtils.convertToBytes(id));
+
+ // append stream to the output stream
+ out.writeTo(os);
+
+
+ out = null;
+ os = null;
+
+ if (debug ) {
+ logger.info("Stats:");
+ logger.log(Level.INFO, "classes: {0}", classNum);
+ logger.log(Level.INFO, "class table: {0} bytes", classTableSize);
+ logger.log(Level.INFO, "objects: {0}", numLocations);
+ logger.log(Level.INFO, "location table: {0} bytes", locationTableSize);
+ logger.log(Level.INFO, "data: {0} bytes", location);
+ }
+
+ return true;
+ }
+
+ protected String getChunk(BinaryIdContentPair pair) {
+ return new String(pair.getContent().bytes, 0, Math.min(64, pair
+ .getContent().bytes.length));
+ }
+
+ protected int findPrevMatch(BinaryIdContentPair oldPair,
+ ArrayList<BinaryIdContentPair> bucket) {
+ if (bucket == null)
+ return -1;
+ for (int x = bucket.size(); --x >= 0;) {
+ BinaryIdContentPair pair = bucket.get(x);
+ if (pair.getContent().equals(oldPair.getContent()))
+ return locationTable.get(pair.getId());
+ }
+ return -1;
+ }
+
+ protected byte[] fixClassAlias(byte[] bytes, int width) {
+ if (bytes.length != width) {
+ byte[] newAlias = new byte[width];
+ for (int x = width - bytes.length; x < width; x++)
+ newAlias[x] = bytes[x - bytes.length];
+ return newAlias;
+ }
+ return bytes;
+ }
+
+ public boolean save(Savable object, File f) throws IOException {
+ File parentDirectory = f.getParentFile();
+ if(parentDirectory != null && !parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+
+ FileOutputStream fos = new FileOutputStream(f);
+ boolean rVal = save(object, fos);
+ fos.close();
+ return rVal;
+ }
+
+ public BinaryOutputCapsule getCapsule(Savable object) {
+ return contentTable.get(object).getContent();
+ }
+
+ private BinaryClassObject createClassObject(Class clazz) throws IOException{
+ BinaryClassObject bco = new BinaryClassObject();
+ bco.alias = generateTag();
+ bco.nameFields = new HashMap<String, BinaryClassField>();
+ bco.classHierarchyVersions = SavableClassUtil.getSavableVersions(clazz);
+
+ classes.put(clazz.getName(), bco);
+
+ return bco;
+ }
+
+ public int processBinarySavable(Savable object) throws IOException {
+ if (object == null) {
+ return -1;
+ }
+ Class<? extends Savable> clazz = object.getClass();
+ BinaryClassObject bco = classes.get(object.getClass().getName());
+ // is this class been looked at before? in tagTable?
+ if (bco == null) {
+ bco = createClassObject(object.getClass());
+ }
+
+ // is object in contentTable?
+ if (contentTable.get(object) != null) {
+ return (contentTable.get(object).getId());
+ }
+ BinaryIdContentPair newPair = generateIdContentPair(bco);
+ BinaryIdContentPair old = contentTable.put(object, newPair);
+ if (old == null) {
+ contentKeys.add(object);
+ }
+ object.write(this);
+ newPair.getContent().finish();
+ return newPair.getId();
+
+ }
+
+ protected byte[] generateTag() {
+ int width = ((int) FastMath.log(aliasCount, 256) + 1);
+ int count = aliasCount;
+ aliasCount++;
+ byte[] bytes = new byte[width];
+ for (int x = width - 1; x >= 0; x--) {
+ int pow = (int) FastMath.pow(256, x);
+ int factor = count / pow;
+ bytes[width - x - 1] = (byte) factor;
+ count %= pow;
+ }
+ return bytes;
+ }
+
+ protected BinaryIdContentPair generateIdContentPair(BinaryClassObject bco) {
+ BinaryIdContentPair pair = new BinaryIdContentPair(idCount++,
+ new BinaryOutputCapsule(this, bco));
+ return pair;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java
new file mode 100644
index 0000000..b999a3c
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+class BinaryIdContentPair {
+
+ private int id;
+ private BinaryOutputCapsule content;
+
+ BinaryIdContentPair(int id, BinaryOutputCapsule content) {
+ this.id = id;
+ this.content = content;
+ }
+
+ BinaryOutputCapsule getContent() {
+ return content;
+ }
+
+ void setContent(BinaryOutputCapsule content) {
+ this.content = content;
+ }
+
+ int getId() {
+ return id;
+ }
+
+ void setId(int id) {
+ this.id = id;
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java
new file mode 100644
index 0000000..7f94ba4
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.math.FastMath;
+import java.io.*;
+import java.net.URL;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Joshua Slack
+ * @author Kirill Vainer - Version number, Fast buffer reading
+ */
+public final class BinaryImporter implements JmeImporter {
+ private static final Logger logger = Logger.getLogger(BinaryImporter.class
+ .getName());
+
+ private AssetManager assetManager;
+
+ //Key - alias, object - bco
+ private HashMap<String, BinaryClassObject> classes
+ = new HashMap<String, BinaryClassObject>();
+ //Key - id, object - the savable
+ private HashMap<Integer, Savable> contentTable
+ = new HashMap<Integer, Savable>();
+ //Key - savable, object - capsule
+ private IdentityHashMap<Savable, BinaryInputCapsule> capsuleTable
+ = new IdentityHashMap<Savable, BinaryInputCapsule>();
+ //Key - id, opject - location in the file
+ private HashMap<Integer, Integer> locationTable
+ = new HashMap<Integer, Integer>();
+
+ public static boolean debug = false;
+
+ private byte[] dataArray;
+ private int aliasWidth;
+ private int formatVersion;
+
+ private static final boolean fastRead = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
+
+ public BinaryImporter() {
+ }
+
+ public int getFormatVersion(){
+ return formatVersion;
+ }
+
+ public static boolean canUseFastBuffers(){
+ return fastRead;
+ }
+
+ public static BinaryImporter getInstance() {
+ return new BinaryImporter();
+ }
+
+ public void setAssetManager(AssetManager manager){
+ this.assetManager = manager;
+ }
+
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ public Object load(AssetInfo info){
+// if (!(info.getKey() instanceof ModelKey))
+// throw new IllegalArgumentException("Model assets must be loaded using a ModelKey");
+
+ assetManager = info.getManager();
+
+ InputStream is = null;
+ try {
+ is = info.openStream();
+ Savable s = load(is);
+
+ return s;
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "An error occured while loading jME binary object", ex);
+ } finally {
+ if (is != null){
+ try {
+ is.close();
+ } catch (IOException ex) {}
+ }
+ }
+ return null;
+ }
+
+ public Savable load(InputStream is) throws IOException {
+ return load(is, null, null);
+ }
+
+ public Savable load(InputStream is, ReadListener listener) throws IOException {
+ return load(is, listener, null);
+ }
+
+ public Savable load(InputStream is, ReadListener listener, ByteArrayOutputStream baos) throws IOException {
+ contentTable.clear();
+ BufferedInputStream bis = new BufferedInputStream(is);
+
+ int numClasses;
+
+ // Try to read signature
+ int maybeSignature = ByteUtils.readInt(bis);
+ if (maybeSignature == FormatVersion.SIGNATURE){
+ // this is a new version J3O file
+ formatVersion = ByteUtils.readInt(bis);
+ numClasses = ByteUtils.readInt(bis);
+
+ // check if this binary is from the future
+ if (formatVersion > FormatVersion.VERSION){
+ throw new IOException("The binary file is of newer version than expected! " +
+ formatVersion + " > " + FormatVersion.VERSION);
+ }
+ }else{
+ // this is an old version J3O file
+ // the signature was actually the class count
+ numClasses = maybeSignature;
+
+ // 0 indicates version before we started adding
+ // version numbers
+ formatVersion = 0;
+ }
+
+ int bytes = 4;
+ aliasWidth = ((int)FastMath.log(numClasses, 256) + 1);
+
+ classes.clear();
+ for(int i = 0; i < numClasses; i++) {
+ String alias = readString(bis, aliasWidth);
+
+ // jME3 NEW: Read class version number
+ int[] classHierarchyVersions;
+ if (formatVersion >= 1){
+ int classHierarchySize = bis.read();
+ classHierarchyVersions = new int[classHierarchySize];
+ for (int j = 0; j < classHierarchySize; j++){
+ classHierarchyVersions[j] = ByteUtils.readInt(bis);
+ }
+ }else{
+ classHierarchyVersions = new int[]{ 0 };
+ }
+
+ // read classname and classname size
+ int classLength = ByteUtils.readInt(bis);
+ String className = readString(bis, classLength);
+
+ BinaryClassObject bco = new BinaryClassObject();
+ bco.alias = alias.getBytes();
+ bco.className = className;
+ bco.classHierarchyVersions = classHierarchyVersions;
+
+ int fields = ByteUtils.readInt(bis);
+ bytes += (8 + aliasWidth + classLength);
+
+ bco.nameFields = new HashMap<String, BinaryClassField>(fields);
+ bco.aliasFields = new HashMap<Byte, BinaryClassField>(fields);
+ for (int x = 0; x < fields; x++) {
+ byte fieldAlias = (byte)bis.read();
+ byte fieldType = (byte)bis.read();
+
+ int fieldNameLength = ByteUtils.readInt(bis);
+ String fieldName = readString(bis, fieldNameLength);
+ BinaryClassField bcf = new BinaryClassField(fieldName, fieldAlias, fieldType);
+ bco.nameFields.put(fieldName, bcf);
+ bco.aliasFields.put(fieldAlias, bcf);
+ bytes += (6 + fieldNameLength);
+ }
+ classes.put(alias, bco);
+ }
+ if (listener != null) listener.readBytes(bytes);
+
+ int numLocs = ByteUtils.readInt(bis);
+ bytes = 4;
+
+ capsuleTable.clear();
+ locationTable.clear();
+ for(int i = 0; i < numLocs; i++) {
+ int id = ByteUtils.readInt(bis);
+ int loc = ByteUtils.readInt(bis);
+ locationTable.put(id, loc);
+ bytes += 8;
+ }
+
+ @SuppressWarnings("unused")
+ int numbIDs = ByteUtils.readInt(bis); // XXX: NOT CURRENTLY USED
+ int id = ByteUtils.readInt(bis);
+ bytes += 8;
+ if (listener != null) listener.readBytes(bytes);
+
+ if (baos == null) {
+ baos = new ByteArrayOutputStream(bytes);
+ } else {
+ baos.reset();
+ }
+ int size = -1;
+ byte[] cache = new byte[4096];
+ while((size = bis.read(cache)) != -1) {
+ baos.write(cache, 0, size);
+ if (listener != null) listener.readBytes(size);
+ }
+ bis = null;
+
+ dataArray = baos.toByteArray();
+ baos = null;
+
+ Savable rVal = readObject(id);
+ if (debug) {
+ logger.info("Importer Stats: ");
+ logger.log(Level.INFO, "Tags: {0}", numClasses);
+ logger.log(Level.INFO, "Objects: {0}", numLocs);
+ logger.log(Level.INFO, "Data Size: {0}", dataArray.length);
+ }
+ dataArray = null;
+ return rVal;
+ }
+
+ public Savable load(URL f) throws IOException {
+ return load(f, null);
+ }
+
+ public Savable load(URL f, ReadListener listener) throws IOException {
+ InputStream is = f.openStream();
+ Savable rVal = load(is, listener);
+ is.close();
+ return rVal;
+ }
+
+ public Savable load(File f) throws IOException {
+ return load(f, null);
+ }
+
+ public Savable load(File f, ReadListener listener) throws IOException {
+ FileInputStream fis = new FileInputStream(f);
+ Savable rVal = load(fis, listener);
+ fis.close();
+ return rVal;
+ }
+
+ public Savable load(byte[] data) throws IOException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ Savable rVal = load(bais);
+ bais.close();
+ return rVal;
+ }
+
+ @Override
+ public InputCapsule getCapsule(Savable id) {
+ return capsuleTable.get(id);
+ }
+
+ protected String readString(InputStream f, int length) throws IOException {
+ byte[] data = new byte[length];
+ for(int j = 0; j < length; j++) {
+ data[j] = (byte)f.read();
+ }
+
+ return new String(data);
+ }
+
+ protected String readString(int length, int offset) throws IOException {
+ byte[] data = new byte[length];
+ for(int j = 0; j < length; j++) {
+ data[j] = dataArray[j+offset];
+ }
+
+ return new String(data);
+ }
+
+ public Savable readObject(int id) {
+
+ if(contentTable.get(id) != null) {
+ return contentTable.get(id);
+ }
+
+ try {
+ int loc = locationTable.get(id);
+
+ String alias = readString(aliasWidth, loc);
+ loc+=aliasWidth;
+
+ BinaryClassObject bco = classes.get(alias);
+
+ if(bco == null) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "NULL class object: " + alias);
+ return null;
+ }
+
+ int dataLength = ByteUtils.convertIntFromBytes(dataArray, loc);
+ loc+=4;
+
+ Savable out = null;
+ if (assetManager != null) {
+ out = SavableClassUtil.fromName(bco.className, assetManager.getClassLoaders());
+ } else {
+ out = SavableClassUtil.fromName(bco.className);
+ }
+
+ BinaryInputCapsule cap = new BinaryInputCapsule(this, out, bco);
+ cap.setContent(dataArray, loc, loc+dataLength);
+
+ capsuleTable.put(out, cap);
+ contentTable.put(id, out);
+
+ out.read(this);
+
+ capsuleTable.remove(out);
+
+ return out;
+
+ } catch (IOException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ } catch (ClassNotFoundException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ } catch (InstantiationException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ } catch (IllegalAccessException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java
new file mode 100644
index 0000000..cebd764
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java
@@ -0,0 +1,1380 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.export.SavableClassUtil;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Joshua Slack
+ */
+final class BinaryInputCapsule implements InputCapsule {
+
+ private static final Logger logger = Logger
+ .getLogger(BinaryInputCapsule.class.getName());
+
+ protected BinaryImporter importer;
+ protected BinaryClassObject cObj;
+ protected Savable savable;
+ protected HashMap<Byte, Object> fieldData;
+
+ protected int index = 0;
+
+ public BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco) {
+ this.importer = importer;
+ this.cObj = bco;
+ this.savable = savable;
+ }
+
+ public void setContent(byte[] content, int start, int limit) {
+ fieldData = new HashMap<Byte, Object>();
+ for (index = start; index < limit;) {
+ byte alias = content[index];
+
+ index++;
+
+ try {
+ byte type = cObj.aliasFields.get(alias).type;
+ Object value = null;
+
+ switch (type) {
+ case BinaryClassField.BITSET: {
+ value = readBitSet(content);
+ break;
+ }
+ case BinaryClassField.BOOLEAN: {
+ value = readBoolean(content);
+ break;
+ }
+ case BinaryClassField.BOOLEAN_1D: {
+ value = readBooleanArray(content);
+ break;
+ }
+ case BinaryClassField.BOOLEAN_2D: {
+ value = readBooleanArray2D(content);
+ break;
+ }
+ case BinaryClassField.BYTE: {
+ value = readByte(content);
+ break;
+ }
+ case BinaryClassField.BYTE_1D: {
+ value = readByteArray(content);
+ break;
+ }
+ case BinaryClassField.BYTE_2D: {
+ value = readByteArray2D(content);
+ break;
+ }
+ case BinaryClassField.BYTEBUFFER: {
+ value = readByteBuffer(content);
+ break;
+ }
+ case BinaryClassField.DOUBLE: {
+ value = readDouble(content);
+ break;
+ }
+ case BinaryClassField.DOUBLE_1D: {
+ value = readDoubleArray(content);
+ break;
+ }
+ case BinaryClassField.DOUBLE_2D: {
+ value = readDoubleArray2D(content);
+ break;
+ }
+ case BinaryClassField.FLOAT: {
+ value = readFloat(content);
+ break;
+ }
+ case BinaryClassField.FLOAT_1D: {
+ value = readFloatArray(content);
+ break;
+ }
+ case BinaryClassField.FLOAT_2D: {
+ value = readFloatArray2D(content);
+ break;
+ }
+ case BinaryClassField.FLOATBUFFER: {
+ value = readFloatBuffer(content);
+ break;
+ }
+ case BinaryClassField.FLOATBUFFER_ARRAYLIST: {
+ value = readFloatBufferArrayList(content);
+ break;
+ }
+ case BinaryClassField.BYTEBUFFER_ARRAYLIST: {
+ value = readByteBufferArrayList(content);
+ break;
+ }
+ case BinaryClassField.INT: {
+ value = readInt(content);
+ break;
+ }
+ case BinaryClassField.INT_1D: {
+ value = readIntArray(content);
+ break;
+ }
+ case BinaryClassField.INT_2D: {
+ value = readIntArray2D(content);
+ break;
+ }
+ case BinaryClassField.INTBUFFER: {
+ value = readIntBuffer(content);
+ break;
+ }
+ case BinaryClassField.LONG: {
+ value = readLong(content);
+ break;
+ }
+ case BinaryClassField.LONG_1D: {
+ value = readLongArray(content);
+ break;
+ }
+ case BinaryClassField.LONG_2D: {
+ value = readLongArray2D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE: {
+ value = readSavable(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_1D: {
+ value = readSavableArray(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_2D: {
+ value = readSavableArray2D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_ARRAYLIST: {
+ value = readSavableArray(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_ARRAYLIST_1D: {
+ value = readSavableArray2D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_ARRAYLIST_2D: {
+ value = readSavableArray3D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_MAP: {
+ value = readSavableMap(content);
+ break;
+ }
+ case BinaryClassField.STRING_SAVABLE_MAP: {
+ value = readStringSavableMap(content);
+ break;
+ }
+ case BinaryClassField.INT_SAVABLE_MAP: {
+ value = readIntSavableMap(content);
+ break;
+ }
+ case BinaryClassField.SHORT: {
+ value = readShort(content);
+ break;
+ }
+ case BinaryClassField.SHORT_1D: {
+ value = readShortArray(content);
+ break;
+ }
+ case BinaryClassField.SHORT_2D: {
+ value = readShortArray2D(content);
+ break;
+ }
+ case BinaryClassField.SHORTBUFFER: {
+ value = readShortBuffer(content);
+ break;
+ }
+ case BinaryClassField.STRING: {
+ value = readString(content);
+ break;
+ }
+ case BinaryClassField.STRING_1D: {
+ value = readStringArray(content);
+ break;
+ }
+ case BinaryClassField.STRING_2D: {
+ value = readStringArray2D(content);
+ break;
+ }
+
+ default:
+ // skip put statement
+ continue;
+ }
+
+ fieldData.put(alias, value);
+
+ } catch (IOException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(),
+ "setContent(byte[] content)", "Exception", e);
+ }
+ }
+ }
+
+ public int getSavableVersion(Class<? extends Savable> desiredClass){
+ return SavableClassUtil.getSavedSavableVersion(savable, desiredClass,
+ cObj.classHierarchyVersions, importer.getFormatVersion());
+ }
+
+ public BitSet readBitSet(String name, BitSet defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (BitSet) fieldData.get(field.alias);
+ }
+
+ public boolean readBoolean(String name, boolean defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Boolean) fieldData.get(field.alias)).booleanValue();
+ }
+
+ public boolean[] readBooleanArray(String name, boolean[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (boolean[]) fieldData.get(field.alias);
+ }
+
+ public boolean[][] readBooleanArray2D(String name, boolean[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (boolean[][]) fieldData.get(field.alias);
+ }
+
+ public byte readByte(String name, byte defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Byte) fieldData.get(field.alias)).byteValue();
+ }
+
+ public byte[] readByteArray(String name, byte[] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (byte[]) fieldData.get(field.alias);
+ }
+
+ public byte[][] readByteArray2D(String name, byte[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (byte[][]) fieldData.get(field.alias);
+ }
+
+ public ByteBuffer readByteBuffer(String name, ByteBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ByteBuffer) fieldData.get(field.alias);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ArrayList<ByteBuffer> readByteBufferArrayList(String name,
+ ArrayList<ByteBuffer> defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ArrayList<ByteBuffer>) fieldData.get(field.alias);
+ }
+
+ public double readDouble(String name, double defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Double) fieldData.get(field.alias)).doubleValue();
+ }
+
+ public double[] readDoubleArray(String name, double[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (double[]) fieldData.get(field.alias);
+ }
+
+ public double[][] readDoubleArray2D(String name, double[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (double[][]) fieldData.get(field.alias);
+ }
+
+ public float readFloat(String name, float defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Float) fieldData.get(field.alias)).floatValue();
+ }
+
+ public float[] readFloatArray(String name, float[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (float[]) fieldData.get(field.alias);
+ }
+
+ public float[][] readFloatArray2D(String name, float[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (float[][]) fieldData.get(field.alias);
+ }
+
+ public FloatBuffer readFloatBuffer(String name, FloatBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (FloatBuffer) fieldData.get(field.alias);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ArrayList<FloatBuffer> readFloatBufferArrayList(String name,
+ ArrayList<FloatBuffer> defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ArrayList<FloatBuffer>) fieldData.get(field.alias);
+ }
+
+ public int readInt(String name, int defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Integer) fieldData.get(field.alias)).intValue();
+ }
+
+ public int[] readIntArray(String name, int[] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (int[]) fieldData.get(field.alias);
+ }
+
+ public int[][] readIntArray2D(String name, int[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (int[][]) fieldData.get(field.alias);
+ }
+
+ public IntBuffer readIntBuffer(String name, IntBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (IntBuffer) fieldData.get(field.alias);
+ }
+
+ public long readLong(String name, long defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Long) fieldData.get(field.alias)).longValue();
+ }
+
+ public long[] readLongArray(String name, long[] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (long[]) fieldData.get(field.alias);
+ }
+
+ public long[][] readLongArray2D(String name, long[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (long[][]) fieldData.get(field.alias);
+ }
+
+ public Savable readSavable(String name, Savable defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value == null)
+ return null;
+ else if (value instanceof ID) {
+ value = importer.readObject(((ID) value).id);
+ fieldData.put(field.alias, value);
+ return (Savable) value;
+ } else
+ return defVal;
+ }
+
+ public Savable[] readSavableArray(String name, Savable[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object[] values = (Object[]) fieldData.get(field.alias);
+ if (values instanceof ID[]) {
+ values = resolveIDs(values);
+ fieldData.put(field.alias, values);
+ return (Savable[]) values;
+ } else
+ return defVal;
+ }
+
+ private Savable[] resolveIDs(Object[] values) {
+ if (values != null) {
+ Savable[] savables = new Savable[values.length];
+ for (int i = 0; i < values.length; i++) {
+ final ID id = (ID) values[i];
+ savables[i] = id != null ? importer.readObject(id.id) : null;
+ }
+ return savables;
+ } else {
+ return null;
+ }
+ }
+
+ public Savable[][] readSavableArray2D(String name, Savable[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null ||!fieldData.containsKey(field.alias))
+ return defVal;
+ Object[][] values = (Object[][]) fieldData.get(field.alias);
+ if (values instanceof ID[][]) {
+ Savable[][] savables = new Savable[values.length][];
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ savables[i] = resolveIDs(values[i]);
+ } else savables[i] = null;
+ }
+ values = savables;
+ fieldData.put(field.alias, values);
+ }
+ return (Savable[][]) values;
+ }
+
+ public Savable[][][] readSavableArray3D(String name, Savable[][][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object[][][] values = (Object[][][]) fieldData.get(field.alias);
+ if (values instanceof ID[][][]) {
+ Savable[][][] savables = new Savable[values.length][][];
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ savables[i] = new Savable[values[i].length][];
+ for (int j = 0; j < values[i].length; j++) {
+ savables[i][j] = resolveIDs(values[i][j]);
+ }
+ } else savables[i] = null;
+ }
+ fieldData.put(field.alias, savables);
+ return savables;
+ } else
+ return defVal;
+ }
+
+ private ArrayList<Savable> savableArrayListFromArray(Savable[] savables) {
+ if(savables == null) {
+ return null;
+ }
+ ArrayList<Savable> arrayList = new ArrayList<Savable>(savables.length);
+ for (int x = 0; x < savables.length; x++) {
+ arrayList.add(savables[x]);
+ }
+ return arrayList;
+ }
+
+ // Assumes array of size 2 arrays where pos 0 is key and pos 1 is value.
+ private Map<Savable, Savable> savableMapFrom2DArray(Savable[][] savables) {
+ if(savables == null) {
+ return null;
+ }
+ Map<Savable, Savable> map = new HashMap<Savable, Savable>(savables.length);
+ for (int x = 0; x < savables.length; x++) {
+ map.put(savables[x][0], savables[x][1]);
+ }
+ return map;
+ }
+
+ private Map<String, Savable> stringSavableMapFromKV(String[] keys, Savable[] values) {
+ if(keys == null || values == null) {
+ return null;
+ }
+
+ Map<String, Savable> map = new HashMap<String, Savable>(keys.length);
+ for (int x = 0; x < keys.length; x++)
+ map.put(keys[x], values[x]);
+
+ return map;
+ }
+
+ private IntMap<Savable> intSavableMapFromKV(int[] keys, Savable[] values) {
+ if(keys == null || values == null) {
+ return null;
+ }
+
+ IntMap<Savable> map = new IntMap<Savable>(keys.length);
+ for (int x = 0; x < keys.length; x++)
+ map.put(keys[x], values[x]);
+
+ return map;
+ }
+
+ public ArrayList readSavableArrayList(String name, ArrayList defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[]) {
+ // read Savable array and convert to ArrayList
+ Savable[] savables = readSavableArray(name, null);
+ value = savableArrayListFromArray(savables);
+ fieldData.put(field.alias, value);
+ }
+ return (ArrayList) value;
+ }
+
+ public ArrayList[] readSavableArrayListArray(String name, ArrayList[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[][]) {
+ // read 2D Savable array and convert to ArrayList array
+ Savable[][] savables = readSavableArray2D(name, null);
+ if (savables != null) {
+ ArrayList[] arrayLists = new ArrayList[savables.length];
+ for (int i = 0; i < savables.length; i++) {
+ arrayLists[i] = savableArrayListFromArray(savables[i]);
+ }
+ value = arrayLists;
+ } else
+ value = defVal;
+ fieldData.put(field.alias, value);
+ }
+ return (ArrayList[]) value;
+ }
+
+ public ArrayList[][] readSavableArrayListArray2D(String name,
+ ArrayList[][] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[][][]) {
+ // read 3D Savable array and convert to 2D ArrayList array
+ Savable[][][] savables = readSavableArray3D(name, null);
+ if (savables != null && savables.length > 0) {
+ ArrayList[][] arrayLists = new ArrayList[savables.length][];
+ for (int i = 0; i < savables.length; i++) {
+ arrayLists[i] = new ArrayList[savables[i].length];
+ for (int j = 0; j < savables[i].length; j++) {
+ arrayLists[i][j] = savableArrayListFromArray(savables[i][j]);
+ }
+ }
+ value = arrayLists;
+ } else
+ value = defVal;
+ fieldData.put(field.alias, value);
+ }
+ return (ArrayList[][]) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<? extends Savable, ? extends Savable> readSavableMap(String name, Map<? extends Savable, ? extends Savable> defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[][]) {
+ // read Savable array and convert to Map
+ Savable[][] savables = readSavableArray2D(name, null);
+ value = savableMapFrom2DArray(savables);
+ fieldData.put(field.alias, value);
+ }
+ return (Map<? extends Savable, ? extends Savable>) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, ? extends Savable> readStringSavableMap(String name, Map<String, ? extends Savable> defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof StringIDMap) {
+ // read Savable array and convert to Map values
+ StringIDMap in = (StringIDMap) value;
+ Savable[] values = resolveIDs(in.values);
+ value = stringSavableMapFromKV(in.keys, values);
+ fieldData.put(field.alias, value);
+ }
+ return (Map<String, Savable>) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public IntMap<? extends Savable> readIntSavableMap(String name, IntMap<? extends Savable> defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof IntIDMap) {
+ // read Savable array and convert to Map values
+ IntIDMap in = (IntIDMap) value;
+ Savable[] values = resolveIDs(in.values);
+ value = intSavableMapFromKV(in.keys, values);
+ fieldData.put(field.alias, value);
+ }
+ return (IntMap<Savable>) value;
+ }
+
+ public short readShort(String name, short defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Short) fieldData.get(field.alias)).shortValue();
+ }
+
+ public short[] readShortArray(String name, short[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (short[]) fieldData.get(field.alias);
+ }
+
+ public short[][] readShortArray2D(String name, short[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (short[][]) fieldData.get(field.alias);
+ }
+
+ public ShortBuffer readShortBuffer(String name, ShortBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ShortBuffer) fieldData.get(field.alias);
+ }
+
+ public String readString(String name, String defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (String) fieldData.get(field.alias);
+ }
+
+ public String[] readStringArray(String name, String[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (String[]) fieldData.get(field.alias);
+ }
+
+ public String[][] readStringArray2D(String name, String[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (String[][]) fieldData.get(field.alias);
+ }
+
+ // byte primitive
+
+ protected byte readByte(byte[] content) throws IOException {
+ byte value = content[index];
+ index++;
+ return value;
+ }
+
+ protected byte readByteForBuffer(byte[] content) throws IOException {
+ byte value = content[index];
+ index++;
+ return value;
+ }
+
+ protected byte[] readByteArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ byte[] value = new byte[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readByte(content);
+ return value;
+ }
+
+ protected byte[][] readByteArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ byte[][] value = new byte[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readByteArray(content);
+ return value;
+ }
+
+ // int primitive
+
+ protected int readIntForBuffer(byte[] content){
+ int number = ((content[index+3] & 0xFF) << 24)
+ + ((content[index+2] & 0xFF) << 16)
+ + ((content[index+1] & 0xFF) << 8)
+ + (content[index] & 0xFF);
+ index += 4;
+ return number;
+ }
+
+ protected int readInt(byte[] content) throws IOException {
+ byte[] bytes = inflateFrom(content, index);
+ index += 1 + bytes.length;
+ bytes = ByteUtils.rightAlignBytes(bytes, 4);
+ int value = ByteUtils.convertIntFromBytes(bytes);
+ if (value == BinaryOutputCapsule.NULL_OBJECT
+ || value == BinaryOutputCapsule.DEFAULT_OBJECT)
+ index -= 4;
+ return value;
+ }
+
+ protected int[] readIntArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ int[] value = new int[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readInt(content);
+ return value;
+ }
+
+ protected int[][] readIntArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ int[][] value = new int[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readIntArray(content);
+ return value;
+ }
+
+ // float primitive
+
+ protected float readFloat(byte[] content) throws IOException {
+ float value = ByteUtils.convertFloatFromBytes(content, index);
+ index += 4;
+ return value;
+ }
+
+ protected float readFloatForBuffer(byte[] content) throws IOException {
+ int number = readIntForBuffer(content);
+ return Float.intBitsToFloat(number);
+ }
+
+ protected float[] readFloatArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ float[] value = new float[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readFloat(content);
+ return value;
+ }
+
+ protected float[][] readFloatArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ float[][] value = new float[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readFloatArray(content);
+ return value;
+ }
+
+ // double primitive
+
+ protected double readDouble(byte[] content) throws IOException {
+ double value = ByteUtils.convertDoubleFromBytes(content, index);
+ index += 8;
+ return value;
+ }
+
+ protected double[] readDoubleArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ double[] value = new double[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readDouble(content);
+ return value;
+ }
+
+ protected double[][] readDoubleArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ double[][] value = new double[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readDoubleArray(content);
+ return value;
+ }
+
+ // long primitive
+
+ protected long readLong(byte[] content) throws IOException {
+ byte[] bytes = inflateFrom(content, index);
+ index += 1 + bytes.length;
+ bytes = ByteUtils.rightAlignBytes(bytes, 8);
+ long value = ByteUtils.convertLongFromBytes(bytes);
+ return value;
+ }
+
+ protected long[] readLongArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ long[] value = new long[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readLong(content);
+ return value;
+ }
+
+ protected long[][] readLongArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ long[][] value = new long[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readLongArray(content);
+ return value;
+ }
+
+ // short primitive
+
+ protected short readShort(byte[] content) throws IOException {
+ short value = ByteUtils.convertShortFromBytes(content, index);
+ index += 2;
+ return value;
+ }
+
+ protected short readShortForBuffer(byte[] content) throws IOException {
+ short number = (short) ((content[index+0] & 0xFF)
+ + ((content[index+1] & 0xFF) << 8));
+ index += 2;
+ return number;
+ }
+
+ protected short[] readShortArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ short[] value = new short[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readShort(content);
+ return value;
+ }
+
+ protected short[][] readShortArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ short[][] value = new short[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readShortArray(content);
+ return value;
+ }
+
+ // boolean primitive
+
+ protected boolean readBoolean(byte[] content) throws IOException {
+ boolean value = ByteUtils.convertBooleanFromBytes(content, index);
+ index += 1;
+ return value;
+ }
+
+ protected boolean[] readBooleanArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ boolean[] value = new boolean[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readBoolean(content);
+ return value;
+ }
+
+ protected boolean[][] readBooleanArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ boolean[][] value = new boolean[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readBooleanArray(content);
+ return value;
+ }
+
+ /*
+ * UTF-8 crash course:
+ *
+ * UTF-8 codepoints map to UTF-16 codepoints and vv, which is what Java uses for it's Strings.
+ * (so a UTF-8 codepoint can contain all possible values for a Java char)
+ *
+ * A UTF-8 codepoint can be 1, 2 or 3 bytes long. How long a codepint is can be told by reading the first byte:
+ * b < 0x80, 1 byte
+ * (b & 0xC0) == 0xC0, 2 bytes
+ * (b & 0xE0) == 0xE0, 3 bytes
+ *
+ * However there is an additional restriction to UTF-8, to enable you to find the start of a UTF-8 codepoint,
+ * if you start reading at a random point in a UTF-8 byte stream. That's why UTF-8 requires for the second and third byte of
+ * a multibyte codepoint:
+ * (b & 0x80) == 0x80 (in other words, first bit must be 1)
+ */
+ private final static int UTF8_START = 0; // next byte should be the start of a new
+ private final static int UTF8_2BYTE = 2; // next byte should be the second byte of a 2 byte codepoint
+ private final static int UTF8_3BYTE_1 = 3; // next byte should be the second byte of a 3 byte codepoint
+ private final static int UTF8_3BYTE_2 = 4; // next byte should be the third byte of a 3 byte codepoint
+ private final static int UTF8_ILLEGAL = 10; // not an UTF8 string
+
+ // String
+ protected String readString(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ /*
+ * @see ISSUE 276
+ *
+ * We'll transfer the bytes into a seperate byte array.
+ * While we do that we'll take the opportunity to check if the byte data is valid UTF-8.
+ *
+ * If it is not UTF-8 it is most likely saved with the BinaryOutputCapsule bug, that saves Strings using their native
+ * encoding. Unfortunatly there is no way to know what encoding was used, so we'll parse using the most common one in
+ * that case; latin-1 aka ISO8859_1
+ *
+ * Encoding of "low" ASCII codepoint (in plain speak: when no special characters are used) will usually look the same
+ * for UTF-8 and the other 1 byte codepoint encodings (espc true for numbers and regular letters of the alphabet). So these
+ * are valid UTF-8 and will give the same result (at most a few charakters will appear different, such as the euro sign).
+ *
+ * However, when "high" codepoints are used (any codepoint that over 0x7F, in other words where the first bit is a 1) it's
+ * a different matter and UTF-8 and the 1 byte encoding greatly will differ, as well as most 1 byte encodings relative to each
+ * other.
+ *
+ * It is impossible to detect which one-byte encoding is used. Since UTF8 and practically all 1-byte encodings share the most
+ * used characters (the "none-high" ones) parsing them will give the same result. However, not all byte sequences are legal in
+ * UTF-8 (see explantion above). If not UTF-8 encoded content is detected we therefor fallback on latin1. We also log a warning.
+ *
+ * By this method we detect all use of 1 byte encoding if they:
+ * - use a "high" codepoint after a "low" codepoint or a sequence of codepoints that is valid as UTF-8 bytes, that starts with 1000
+ * - use a "low" codepoint after a "high" codepoint
+ * - use a "low" codepoint after "high" codepoint, after a "high" codepoint that starts with 1110
+ *
+ * In practise this means that unless 2 or 3 "high" codepoints are used after each other in proper order, we'll detect the string
+ * was not originally UTF-8 encoded.
+ *
+ */
+ byte[] bytes = new byte[length];
+ int utf8State = UTF8_START;
+ int b;
+ for (int x = 0; x < length; x++) {
+ bytes[x] = content[index++];
+ b = (int) bytes[x] & 0xFF; // unsign our byte
+
+ switch (utf8State) {
+ case UTF8_START:
+ if (b < 0x80) {
+ // good
+ }
+ else if ((b & 0xC0) == 0xC0) {
+ utf8State = UTF8_2BYTE;
+ }
+ else if ((b & 0xE0) == 0xE0) {
+ utf8State = UTF8_3BYTE_1;
+ }
+ else {
+ utf8State = UTF8_ILLEGAL;
+ }
+ break;
+ case UTF8_3BYTE_1:
+ case UTF8_3BYTE_2:
+ case UTF8_2BYTE:
+ if ((b & 0x80) == 0x80)
+ utf8State = utf8State == UTF8_3BYTE_1 ? UTF8_3BYTE_2 : UTF8_START;
+ else
+ utf8State = UTF8_ILLEGAL;
+ break;
+ }
+ }
+
+ try {
+ // even though so far the parsing might have been a legal UTF-8 sequence, only if a codepoint is fully given is it correct UTF-8
+ if (utf8State == UTF8_START) {
+ // Java misspells UTF-8 as UTF8 for official use in java.lang
+ return new String(bytes, "UTF8");
+ }
+ else {
+ logger.log(
+ Level.WARNING,
+ "Your export has been saved with an incorrect encoding for it's String fields which means it might not load correctly " +
+ "due to encoding issues. You should probably re-export your work. See ISSUE 276 in the jME issue tracker."
+ );
+ // We use ISO8859_1 to be consistent across platforms. We could default to native encoding, but this would lead to inconsistent
+ // behaviour across platforms!
+ // Developers that have previously saved their exports using the old exporter (wich uses native encoding), can temporarly
+ // remove the ""ISO8859_1" parameter, and change the above if statement to "if (false)".
+ // They should then import and re-export their models using the same enviroment they were orginally created in.
+ return new String(bytes, "ISO8859_1");
+ }
+ } catch (UnsupportedEncodingException uee) {
+ // as a last resort fall back to platform native.
+ // JavaDoc is vague about what happens when a decoding a String that contains un undecodable sequence
+ // it also doesn't specify which encodings have to be supported (though UTF-8 and ISO8859 have been in the SUN JRE since at least 1.1)
+ logger.log(
+ Level.SEVERE,
+ "Your export has been saved with an incorrect encoding or your version of Java is unable to decode the stored string. " +
+ "While your export may load correctly by falling back, using it on different platforms or java versions might lead to "+
+ "very strange inconsitenties. You should probably re-export your work. See ISSUE 276 in the jME issue tracker."
+ );
+ return new String(bytes);
+ }
+ }
+
+ protected String[] readStringArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ String[] value = new String[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readString(content);
+ return value;
+ }
+
+ protected String[][] readStringArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ String[][] value = new String[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readStringArray(content);
+ return value;
+ }
+
+ // BitSet
+
+ protected BitSet readBitSet(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ BitSet value = new BitSet(length);
+ for (int x = 0; x < length; x++)
+ value.set(x, readBoolean(content));
+ return value;
+ }
+
+ // INFLATOR for int and long
+
+ protected static byte[] inflateFrom(byte[] contents, int index) {
+ byte firstByte = contents[index];
+ if (firstByte == BinaryOutputCapsule.NULL_OBJECT)
+ return ByteUtils.convertToBytes(BinaryOutputCapsule.NULL_OBJECT);
+ else if (firstByte == BinaryOutputCapsule.DEFAULT_OBJECT)
+ return ByteUtils.convertToBytes(BinaryOutputCapsule.DEFAULT_OBJECT);
+ else if (firstByte == 0)
+ return new byte[0];
+ else {
+ byte[] rVal = new byte[firstByte];
+ for (int x = 0; x < rVal.length; x++)
+ rVal[x] = contents[x + 1 + index];
+ return rVal;
+ }
+ }
+
+ // BinarySavable
+
+ protected ID readSavable(byte[] content) throws IOException {
+ int id = readInt(content);
+ if (id == BinaryOutputCapsule.NULL_OBJECT) {
+ return null;
+ }
+
+ return new ID(id);
+ }
+
+ // BinarySavable array
+
+ protected ID[] readSavableArray(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[] rVal = new ID[elements];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavable(content);
+ }
+ return rVal;
+ }
+
+ protected ID[][] readSavableArray2D(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[][] rVal = new ID[elements][];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavableArray(content);
+ }
+ return rVal;
+ }
+
+ protected ID[][][] readSavableArray3D(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[][][] rVal = new ID[elements][][];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavableArray2D(content);
+ }
+ return rVal;
+ }
+
+ // BinarySavable map
+
+ protected ID[][] readSavableMap(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[][] rVal = new ID[elements][];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavableArray(content);
+ }
+ return rVal;
+ }
+
+ protected StringIDMap readStringSavableMap(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ String[] keys = readStringArray(content);
+ ID[] values = readSavableArray(content);
+ StringIDMap rVal = new StringIDMap();
+ rVal.keys = keys;
+ rVal.values = values;
+ return rVal;
+ }
+
+ protected IntIDMap readIntSavableMap(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ int[] keys = readIntArray(content);
+ ID[] values = readSavableArray(content);
+ IntIDMap rVal = new IntIDMap();
+ rVal.keys = keys;
+ rVal.values = values;
+ return rVal;
+ }
+
+
+ // ArrayList<FloatBuffer>
+
+ protected ArrayList<FloatBuffer> readFloatBufferArrayList(byte[] content)
+ throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT) {
+ return null;
+ }
+ ArrayList<FloatBuffer> rVal = new ArrayList<FloatBuffer>(length);
+ for (int x = 0; x < length; x++) {
+ rVal.add(readFloatBuffer(content));
+ }
+ return rVal;
+ }
+
+ // ArrayList<ByteBuffer>
+
+ protected ArrayList<ByteBuffer> readByteBufferArrayList(byte[] content)
+ throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT) {
+ return null;
+ }
+ ArrayList<ByteBuffer> rVal = new ArrayList<ByteBuffer>(length);
+ for (int x = 0; x < length; x++) {
+ rVal.add(readByteBuffer(content));
+ }
+ return rVal;
+ }
+
+ // NIO BUFFERS
+ // float buffer
+
+ protected FloatBuffer readFloatBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length * 4);
+ value.put(content, index, length * 4).rewind();
+ index += length * 4;
+ return value.asFloatBuffer();
+ }else{
+ FloatBuffer value = BufferUtils.createFloatBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readFloatForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ // int buffer
+
+ protected IntBuffer readIntBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length * 4);
+ value.put(content, index, length * 4).rewind();
+ index += length * 4;
+ return value.asIntBuffer();
+ }else{
+ IntBuffer value = BufferUtils.createIntBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readIntForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ // byte buffer
+
+ protected ByteBuffer readByteBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length);
+ value.put(content, index, length).rewind();
+ index += length;
+ return value;
+ }else{
+ ByteBuffer value = BufferUtils.createByteBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readByteForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ // short buffer
+
+ protected ShortBuffer readShortBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length * 2);
+ value.put(content, index, length * 2).rewind();
+ index += length * 2;
+ return value.asShortBuffer();
+ }else{
+ ShortBuffer value = BufferUtils.createShortBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readShortForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ static private class ID {
+ public int id;
+
+ public ID(int id) {
+ this.id = id;
+ }
+ }
+
+ static private class StringIDMap {
+ public String[] keys;
+ public ID[] values;
+ }
+
+ static private class IntIDMap {
+ public int[] keys;
+ public ID[] values;
+ }
+
+ public <T extends Enum<T>> T readEnum(String name, Class<T> enumType, T defVal) throws IOException {
+ String eVal = readString(name, defVal != null ? defVal.name() : null);
+ if (eVal != null) {
+ return Enum.valueOf(enumType, eVal);
+ } else {
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java
new file mode 100644
index 0000000..4466db5
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java
@@ -0,0 +1,944 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * @author Joshua Slack
+ */
+final class BinaryOutputCapsule implements OutputCapsule {
+
+ public static final int NULL_OBJECT = -1;
+ public static final int DEFAULT_OBJECT = -2;
+
+ public static byte[] NULL_BYTES = new byte[] { (byte) -1 };
+ public static byte[] DEFAULT_BYTES = new byte[] { (byte) -2 };
+
+ protected ByteArrayOutputStream baos;
+ protected byte[] bytes;
+ protected BinaryExporter exporter;
+ protected BinaryClassObject cObj;
+
+ public BinaryOutputCapsule(BinaryExporter exporter, BinaryClassObject bco) {
+ this.baos = new ByteArrayOutputStream();
+ this.exporter = exporter;
+ this.cObj = bco;
+ }
+
+ public void write(byte value, String name, byte defVal) throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTE);
+ write(value);
+ }
+
+ public void write(byte[] value, String name, byte[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTE_1D);
+ write(value);
+ }
+
+ public void write(byte[][] value, String name, byte[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTE_2D);
+ write(value);
+ }
+
+ public void write(int value, String name, int defVal) throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT);
+ write(value);
+ }
+
+ public void write(int[] value, String name, int[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT_1D);
+ write(value);
+ }
+
+ public void write(int[][] value, String name, int[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT_2D);
+ write(value);
+ }
+
+ public void write(float value, String name, float defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOAT);
+ write(value);
+ }
+
+ public void write(float[] value, String name, float[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOAT_1D);
+ write(value);
+ }
+
+ public void write(float[][] value, String name, float[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOAT_2D);
+ write(value);
+ }
+
+ public void write(double value, String name, double defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.DOUBLE);
+ write(value);
+ }
+
+ public void write(double[] value, String name, double[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.DOUBLE_1D);
+ write(value);
+ }
+
+ public void write(double[][] value, String name, double[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.DOUBLE_2D);
+ write(value);
+ }
+
+ public void write(long value, String name, long defVal) throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.LONG);
+ write(value);
+ }
+
+ public void write(long[] value, String name, long[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.LONG_1D);
+ write(value);
+ }
+
+ public void write(long[][] value, String name, long[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.LONG_2D);
+ write(value);
+ }
+
+ public void write(short value, String name, short defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORT);
+ write(value);
+ }
+
+ public void write(short[] value, String name, short[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORT_1D);
+ write(value);
+ }
+
+ public void write(short[][] value, String name, short[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORT_2D);
+ write(value);
+ }
+
+ public void write(boolean value, String name, boolean defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BOOLEAN);
+ write(value);
+ }
+
+ public void write(boolean[] value, String name, boolean[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BOOLEAN_1D);
+ write(value);
+ }
+
+ public void write(boolean[][] value, String name, boolean[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BOOLEAN_2D);
+ write(value);
+ }
+
+ public void write(String value, String name, String defVal)
+ throws IOException {
+ if (value == null ? defVal == null : value.equals(defVal))
+ return;
+ writeAlias(name, BinaryClassField.STRING);
+ write(value);
+ }
+
+ public void write(String[] value, String name, String[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.STRING_1D);
+ write(value);
+ }
+
+ public void write(String[][] value, String name, String[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.STRING_2D);
+ write(value);
+ }
+
+ public void write(BitSet value, String name, BitSet defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BITSET);
+ write(value);
+ }
+
+ public void write(Savable object, String name, Savable defVal)
+ throws IOException {
+ if (object == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE);
+ write(object);
+ }
+
+ public void write(Savable[] objects, String name, Savable[] defVal)
+ throws IOException {
+ if (objects == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_1D);
+ write(objects);
+ }
+
+ public void write(Savable[][] objects, String name, Savable[][] defVal)
+ throws IOException {
+ if (objects == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_2D);
+ write(objects);
+ }
+
+ public void write(FloatBuffer value, String name, FloatBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOATBUFFER);
+ write(value);
+ }
+
+ public void write(IntBuffer value, String name, IntBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INTBUFFER);
+ write(value);
+ }
+
+ public void write(ByteBuffer value, String name, ByteBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTEBUFFER);
+ write(value);
+ }
+
+ public void write(ShortBuffer value, String name, ShortBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORTBUFFER);
+ write(value);
+ }
+
+ public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array,
+ String name, ArrayList<FloatBuffer> defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOATBUFFER_ARRAYLIST);
+ writeFloatBufferArrayList(array);
+ }
+
+ public void writeByteBufferArrayList(ArrayList<ByteBuffer> array,
+ String name, ArrayList<ByteBuffer> defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTEBUFFER_ARRAYLIST);
+ writeByteBufferArrayList(array);
+ }
+
+ public void writeSavableArrayList(ArrayList array, String name,
+ ArrayList defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST);
+ writeSavableArrayList(array);
+ }
+
+ public void writeSavableArrayListArray(ArrayList[] array, String name,
+ ArrayList[] defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST_1D);
+ writeSavableArrayListArray(array);
+ }
+
+ public void writeSavableArrayListArray2D(ArrayList[][] array, String name,
+ ArrayList[][] defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST_2D);
+ writeSavableArrayListArray2D(array);
+ }
+
+ public void writeSavableMap(Map<? extends Savable, ? extends Savable> map,
+ String name, Map<? extends Savable, ? extends Savable> defVal)
+ throws IOException {
+ if (map == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_MAP);
+ writeSavableMap(map);
+ }
+
+ public void writeStringSavableMap(Map<String, ? extends Savable> map,
+ String name, Map<String, ? extends Savable> defVal)
+ throws IOException {
+ if (map == defVal)
+ return;
+ writeAlias(name, BinaryClassField.STRING_SAVABLE_MAP);
+ writeStringSavableMap(map);
+ }
+
+ public void writeIntSavableMap(IntMap<? extends Savable> map,
+ String name, IntMap<? extends Savable> defVal)
+ throws IOException {
+ if (map == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT_SAVABLE_MAP);
+ writeIntSavableMap(map);
+ }
+
+ protected void writeAlias(String name, byte fieldType) throws IOException {
+ if (cObj.nameFields.get(name) == null)
+ generateAlias(name, fieldType);
+
+ byte alias = cObj.nameFields.get(name).alias;
+ write(alias);
+ }
+
+ // XXX: The generation of aliases is limited to 256 possible values.
+ // If we run into classes with more than 256 fields, we need to expand this.
+ // But I mean, come on...
+ protected void generateAlias(String name, byte type) {
+ byte alias = (byte) cObj.nameFields.size();
+ cObj.nameFields.put(name, new BinaryClassField(name, alias, type));
+ }
+
+ @Override
+ public boolean equals(Object arg0) {
+ if (!(arg0 instanceof BinaryOutputCapsule))
+ return false;
+
+ byte[] other = ((BinaryOutputCapsule) arg0).bytes;
+ if (bytes.length != other.length)
+ return false;
+ return Arrays.equals(bytes, other);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 23 * hash + Arrays.hashCode(this.bytes);
+ return hash;
+ }
+
+ public void finish() {
+ // renamed to finish as 'finalize' in java.lang.Object should not be
+ // overridden like this
+ // - finalize should not be called directly but is called by garbage
+ // collection!!!
+ bytes = baos.toByteArray();
+ baos = null;
+ }
+
+ // byte primitive
+
+ protected void write(byte value) throws IOException {
+ baos.write(value);
+ }
+
+ protected void writeForBuffer(byte value) throws IOException {
+ baos.write(value);
+ }
+
+ protected void write(byte[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ baos.write(value);
+ }
+
+ protected void write(byte[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // int primitive
+
+ protected void write(int value) throws IOException {
+ baos.write(deflate(ByteUtils.convertToBytes(value)));
+ }
+
+ protected void writeForBuffer(int value) throws IOException {
+ byte[] byteArray = new byte[4];
+ byteArray[0] = (byte) value;
+ byteArray[1] = (byte) (value >> 8);
+ byteArray[2] = (byte) (value >> 16);
+ byteArray[3] = (byte) (value >> 24);
+ baos.write(byteArray);
+ }
+
+ protected void write(int[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(int[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // float primitive
+
+ protected void write(float value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void writeForBuffer(float value) throws IOException {
+ int integer = Float.floatToIntBits(value);
+ writeForBuffer(integer);
+ }
+
+ protected void write(float[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(float[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // double primitive
+
+ protected void write(double value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void write(double[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(double[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // long primitive
+
+ protected void write(long value) throws IOException {
+ baos.write(deflate(ByteUtils.convertToBytes(value)));
+ }
+
+ protected void write(long[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(long[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // short primitive
+
+ protected void write(short value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void writeForBuffer(short value) throws IOException {
+ byte[] byteArray = new byte[2];
+ byteArray[0] = (byte) value;
+ byteArray[1] = (byte) (value >> 8);
+ baos.write(byteArray);
+ }
+
+ protected void write(short[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(short[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // boolean primitive
+
+ protected void write(boolean value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void write(boolean[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(boolean[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // String
+
+ protected void write(String value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ // write our output as UTF-8. Java misspells UTF-8 as UTF8 for official use in java.lang
+ byte[] bytes = value.getBytes("UTF8");
+ write(bytes.length);
+ baos.write(bytes);
+ }
+
+ protected void write(String[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(String[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // BitSet
+
+ protected void write(BitSet value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.size());
+ // TODO: MAKE THIS SMALLER
+ for (int x = 0, max = value.size(); x < max; x++)
+ write(value.get(x));
+ }
+
+ // DEFLATOR for int and long
+
+ protected static byte[] deflate(byte[] bytes) {
+ int size = bytes.length;
+ if (size == 4) {
+ int possibleMagic = ByteUtils.convertIntFromBytes(bytes);
+ if (possibleMagic == NULL_OBJECT)
+ return NULL_BYTES;
+ else if (possibleMagic == DEFAULT_OBJECT)
+ return DEFAULT_BYTES;
+ }
+ for (int x = 0; x < bytes.length; x++) {
+ if (bytes[x] != 0)
+ break;
+ size--;
+ }
+ if (size == 0)
+ return new byte[1];
+
+ byte[] rVal = new byte[1 + size];
+ rVal[0] = (byte) size;
+ for (int x = 1; x < rVal.length; x++)
+ rVal[x] = bytes[bytes.length - size - 1 + x];
+
+ return rVal;
+ }
+
+ // BinarySavable
+
+ protected void write(Savable object) throws IOException {
+ if (object == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ int id = exporter.processBinarySavable(object);
+ write(id);
+ }
+
+ // BinarySavable array
+
+ protected void write(Savable[] objects) throws IOException {
+ if (objects == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(objects.length);
+ for (int x = 0; x < objects.length; x++) {
+ write(objects[x]);
+ }
+ }
+
+ protected void write(Savable[][] objects) throws IOException {
+ if (objects == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(objects.length);
+ for (int x = 0; x < objects.length; x++) {
+ write(objects[x]);
+ }
+ }
+
+ // ArrayList<BinarySavable>
+
+ protected void writeSavableArrayList(ArrayList array) throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (Object bs : array) {
+ write((Savable) bs);
+ }
+ }
+
+ protected void writeSavableArrayListArray(ArrayList[] array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.length);
+ for (ArrayList bs : array) {
+ writeSavableArrayList(bs);
+ }
+ }
+
+ protected void writeSavableArrayListArray2D(ArrayList[][] array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.length);
+ for (ArrayList[] bs : array) {
+ writeSavableArrayListArray(bs);
+ }
+ }
+
+ // Map<BinarySavable, BinarySavable>
+
+ protected void writeSavableMap(
+ Map<? extends Savable, ? extends Savable> array) throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (Savable key : array.keySet()) {
+ write(new Savable[] { key, array.get(key) });
+ }
+ }
+
+ protected void writeStringSavableMap(Map<String, ? extends Savable> array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+
+ // write String array for keys
+ String[] keys = array.keySet().toArray(new String[] {});
+ write(keys);
+
+ // write Savable array for values
+ Savable[] values = array.values().toArray(new Savable[] {});
+ write(values);
+ }
+
+ protected void writeIntSavableMap(IntMap<? extends Savable> array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+
+ int[] keys = new int[array.size()];
+ Savable[] values = new Savable[keys.length];
+ int i = 0;
+ for (Entry<? extends Savable> entry : array){
+ keys[i] = entry.getKey();
+ values[i] = entry.getValue();
+ i++;
+ }
+
+ // write String array for keys
+ write(keys);
+
+ // write Savable array for values
+ write(values);
+ }
+
+ // ArrayList<FloatBuffer>
+
+ protected void writeFloatBufferArrayList(ArrayList<FloatBuffer> array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (FloatBuffer buf : array) {
+ write(buf);
+ }
+ }
+
+ // ArrayList<FloatBuffer>
+
+ protected void writeByteBufferArrayList(ArrayList<ByteBuffer> array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (ByteBuffer buf : array) {
+ write(buf);
+ }
+ }
+
+ // NIO BUFFERS
+ // float buffer
+
+ protected void write(FloatBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ // int buffer
+
+ protected void write(IntBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ // byte buffer
+
+ protected void write(ByteBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ // short buffer
+
+ protected void write(ShortBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ public void write(Enum value, String name, Enum defVal) throws IOException {
+ if (value == defVal)
+ return;
+ if (value == null) {
+ return;
+ } else {
+ write(value.name(), name, null);
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java b/engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java
new file mode 100644
index 0000000..bb835d0
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * <code>ByteUtils</code> is a helper class for converting numeric primitives
+ * to and from byte representations.
+ *
+ * @author Joshua Slack
+ */
+public class ByteUtils {
+
+ /**
+ * Takes an InputStream and returns the complete byte content of it
+ *
+ * @param inputStream
+ * The input stream to read from
+ * @return The byte array containing the data from the input stream
+ * @throws java.io.IOException
+ * thrown if there is a problem reading from the input stream
+ * provided
+ */
+ public static byte[] getByteContent(InputStream inputStream)
+ throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(
+ 16 * 1024);
+ byte[] buffer = new byte[1024];
+ int byteCount = -1;
+ byte[] data = null;
+
+ // Read the byte content into the output stream first
+ while ((byteCount = inputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, byteCount);
+ }
+
+ // Set data with byte content from stream
+ data = outputStream.toByteArray();
+
+ // Release resources
+ outputStream.close();
+
+ return data;
+ }
+
+
+ // ********** byte <> short METHODS **********
+
+ /**
+ * Writes a short out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the short will be written to
+ * @param value
+ * The short to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeShort(OutputStream outputStream, short value)
+ throws IOException {
+ byte[] byteArray = convertToBytes(value);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(short value) {
+ byte[] byteArray = new byte[2];
+
+ byteArray[0] = (byte) (value >> 8);
+ byteArray[1] = (byte) value;
+ return byteArray;
+ }
+
+ /**
+ * Read in a short from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the short
+ * @return A short, which is the next 2 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static short readShort(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[2];
+
+ // Read in the next 2 bytes
+ inputStream.read(byteArray);
+
+ short number = convertShortFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static short convertShortFromBytes(byte[] byteArray) {
+ return convertShortFromBytes(byteArray, 0);
+ }
+
+ public static short convertShortFromBytes(byte[] byteArray, int offset) {
+ // Convert it to a short
+ short number = (short) ((byteArray[offset+1] & 0xFF) + ((byteArray[offset+0] & 0xFF) << 8));
+ return number;
+ }
+
+
+ // ********** byte <> int METHODS **********
+
+ /**
+ * Writes an integer out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the integer will be written to
+ * @param integer
+ * The integer to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeInt(OutputStream outputStream, int integer)
+ throws IOException {
+ byte[] byteArray = convertToBytes(integer);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(int integer) {
+ byte[] byteArray = new byte[4];
+
+ byteArray[0] = (byte) (integer >> 24);
+ byteArray[1] = (byte) (integer >> 16);
+ byteArray[2] = (byte) (integer >> 8);
+ byteArray[3] = (byte) integer;
+ return byteArray;
+ }
+
+ /**
+ * Read in an integer from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the integer
+ * @return An int, which is the next 4 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static int readInt(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[4];
+
+ // Read in the next 4 bytes
+ inputStream.read(byteArray);
+
+ int number = convertIntFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static int convertIntFromBytes(byte[] byteArray) {
+ return convertIntFromBytes(byteArray, 0);
+ }
+
+ public static int convertIntFromBytes(byte[] byteArray, int offset) {
+ // Convert it to an int
+ int number = ((byteArray[offset] & 0xFF) << 24)
+ + ((byteArray[offset+1] & 0xFF) << 16) + ((byteArray[offset+2] & 0xFF) << 8)
+ + (byteArray[offset+3] & 0xFF);
+ return number;
+ }
+
+
+ // ********** byte <> long METHODS **********
+
+ /**
+ * Writes a long out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the long will be written to
+ * @param value
+ * The long to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeLong(OutputStream outputStream, long value)
+ throws IOException {
+ byte[] byteArray = convertToBytes(value);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(long n) {
+ byte[] bytes = new byte[8];
+
+ bytes[7] = (byte) (n);
+ n >>>= 8;
+ bytes[6] = (byte) (n);
+ n >>>= 8;
+ bytes[5] = (byte) (n);
+ n >>>= 8;
+ bytes[4] = (byte) (n);
+ n >>>= 8;
+ bytes[3] = (byte) (n);
+ n >>>= 8;
+ bytes[2] = (byte) (n);
+ n >>>= 8;
+ bytes[1] = (byte) (n);
+ n >>>= 8;
+ bytes[0] = (byte) (n);
+
+ return bytes;
+ }
+
+ /**
+ * Read in a long from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the long
+ * @return A long, which is the next 8 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static long readLong(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[8];
+
+ // Read in the next 8 bytes
+ inputStream.read(byteArray);
+
+ long number = convertLongFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static long convertLongFromBytes(byte[] bytes) {
+ return convertLongFromBytes(bytes, 0);
+ }
+
+ public static long convertLongFromBytes(byte[] bytes, int offset) {
+ // Convert it to an long
+ return ((((long) bytes[offset+7]) & 0xFF)
+ + ((((long) bytes[offset+6]) & 0xFF) << 8)
+ + ((((long) bytes[offset+5]) & 0xFF) << 16)
+ + ((((long) bytes[offset+4]) & 0xFF) << 24)
+ + ((((long) bytes[offset+3]) & 0xFF) << 32)
+ + ((((long) bytes[offset+2]) & 0xFF) << 40)
+ + ((((long) bytes[offset+1]) & 0xFF) << 48)
+ + ((((long) bytes[offset+0]) & 0xFF) << 56));
+ }
+
+
+ // ********** byte <> double METHODS **********
+
+ /**
+ * Writes a double out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the double will be written to
+ * @param value
+ * The double to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeDouble(OutputStream outputStream, double value)
+ throws IOException {
+ byte[] byteArray = convertToBytes(value);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(double n) {
+ long bits = Double.doubleToLongBits(n);
+ return convertToBytes(bits);
+ }
+
+ /**
+ * Read in a double from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the double
+ * @return A double, which is the next 8 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static double readDouble(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[8];
+
+ // Read in the next 8 bytes
+ inputStream.read(byteArray);
+
+ double number = convertDoubleFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static double convertDoubleFromBytes(byte[] bytes) {
+ return convertDoubleFromBytes(bytes, 0);
+ }
+
+ public static double convertDoubleFromBytes(byte[] bytes, int offset) {
+ // Convert it to a double
+ long bits = convertLongFromBytes(bytes, offset);
+ return Double.longBitsToDouble(bits);
+ }
+
+ // ********** byte <> float METHODS **********
+
+ /**
+ * Writes an float out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the float will be written to
+ * @param fVal
+ * The float to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeFloat(OutputStream outputStream, float fVal)
+ throws IOException {
+ byte[] byteArray = convertToBytes(fVal);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(float f) {
+ int temp = Float.floatToIntBits(f);
+ return convertToBytes(temp);
+ }
+
+ /**
+ * Read in a float from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the float
+ * @return A float, which is the next 4 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static float readFloat(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[4];
+
+ // Read in the next 4 bytes
+ inputStream.read(byteArray);
+
+ float number = convertFloatFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static float convertFloatFromBytes(byte[] byteArray) {
+ return convertFloatFromBytes(byteArray, 0);
+ }
+ public static float convertFloatFromBytes(byte[] byteArray, int offset) {
+ // Convert it to an int
+ int number = convertIntFromBytes(byteArray, offset);
+ return Float.intBitsToFloat(number);
+ }
+
+
+
+ // ********** byte <> boolean METHODS **********
+
+ /**
+ * Writes a boolean out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the boolean will be written to
+ * @param bVal
+ * The boolean to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeBoolean(OutputStream outputStream, boolean bVal)
+ throws IOException {
+ byte[] byteArray = convertToBytes(bVal);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(boolean b) {
+ byte[] rVal = new byte[1];
+ rVal[0] = b ? (byte)1 : (byte)0;
+ return rVal;
+ }
+
+ /**
+ * Read in a boolean from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the boolean
+ * @return A boolean, which is the next byte converted from the InputStream (iow, byte != 0)
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static boolean readBoolean(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[1];
+
+ // Read in the next byte
+ inputStream.read(byteArray);
+
+ return convertBooleanFromBytes(byteArray);
+ }
+
+ public static boolean convertBooleanFromBytes(byte[] byteArray) {
+ return convertBooleanFromBytes(byteArray, 0);
+ }
+ public static boolean convertBooleanFromBytes(byte[] byteArray, int offset) {
+ return byteArray[offset] != 0;
+ }
+
+
+ /**
+ * Properly reads in data from the given stream until the specified number
+ * of bytes have been read.
+ *
+ * @param store
+ * the byte array to store in. Should have a length > bytes
+ * @param bytes
+ * the number of bytes to read.
+ * @param is
+ * the stream to read from
+ * @return the store array for chaining purposes
+ * @throws IOException
+ * if an error occurs while reading from the stream
+ * @throws ArrayIndexOutOfBoundsException
+ * if bytes greater than the length of the store.
+ */
+ public static byte[] readData(byte[] store, int bytes, InputStream is) throws IOException {
+ for (int i = 0; i < bytes; i++) {
+ store[i] = (byte)is.read();
+ }
+ return store;
+ }
+
+ public static byte[] rightAlignBytes(byte[] bytes, int width) {
+ if (bytes.length != width) {
+ byte[] rVal = new byte[width];
+ for (int x = width - bytes.length; x < width; x++) {
+ rVal[x] = bytes[x - (width - bytes.length)];
+ }
+ return rVal;
+ }
+
+ return bytes;
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java b/engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java
new file mode 100644
index 0000000..688627d
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.font.BitmapCharacter;
+import com.jme3.font.BitmapCharacterSet;
+import com.jme3.font.BitmapFont;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.texture.Texture;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class BitmapFontLoader implements AssetLoader {
+
+ private BitmapFont load(AssetManager assetManager, String folder, InputStream in) throws IOException{
+ MaterialDef spriteMat =
+ (MaterialDef) assetManager.loadAsset(new AssetKey("Common/MatDefs/Misc/Unshaded.j3md"));
+
+ BitmapCharacterSet charSet = new BitmapCharacterSet();
+ Material[] matPages = null;
+ BitmapFont font = new BitmapFont();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ String regex = "[\\s=]+";
+
+ font.setCharSet(charSet);
+ while (reader.ready()){
+ String line = reader.readLine();
+ String[] tokens = line.split(regex);
+ if (tokens[0].equals("info")){
+ // Get rendered size
+ for (int i = 1; i < tokens.length; i++){
+ if (tokens[i].equals("size")){
+ charSet.setRenderedSize(Integer.parseInt(tokens[i + 1]));
+ }
+ }
+ }else if (tokens[0].equals("common")){
+ // Fill out BitmapCharacterSet fields
+ for (int i = 1; i < tokens.length; i++){
+ String token = tokens[i];
+ if (token.equals("lineHeight")){
+ charSet.setLineHeight(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("base")){
+ charSet.setBase(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("scaleW")){
+ charSet.setWidth(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("scaleH")){
+ charSet.setHeight(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("pages")){
+ // number of texture pages
+ matPages = new Material[Integer.parseInt(tokens[i + 1])];
+ font.setPages(matPages);
+ }
+ }
+ }else if (tokens[0].equals("page")){
+ int index = -1;
+ Texture tex = null;
+
+ for (int i = 1; i < tokens.length; i++){
+ String token = tokens[i];
+ if (token.equals("id")){
+ index = Integer.parseInt(tokens[i + 1]);
+ }else if (token.equals("file")){
+ String file = tokens[i + 1];
+ if (file.startsWith("\"")){
+ file = file.substring(1, file.length()-1);
+ }
+ TextureKey key = new TextureKey(folder + file, true);
+ key.setGenerateMips(false);
+ tex = assetManager.loadTexture(key);
+ tex.setMagFilter(Texture.MagFilter.Bilinear);
+ tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
+ }
+ }
+ // set page
+ if (index >= 0 && tex != null){
+ Material mat = new Material(spriteMat);
+ mat.setTexture("ColorMap", tex);
+ mat.setBoolean("VertexColor", true);
+ mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ matPages[index] = mat;
+ }
+ }else if (tokens[0].equals("char")){
+ // New BitmapCharacter
+ BitmapCharacter ch = null;
+ for (int i = 1; i < tokens.length; i++){
+ String token = tokens[i];
+ if (token.equals("id")){
+ int index = Integer.parseInt(tokens[i + 1]);
+ ch = new BitmapCharacter();
+ charSet.addCharacter(index, ch);
+ }else if (token.equals("x")){
+ ch.setX(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("y")){
+ ch.setY(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("width")){
+ ch.setWidth(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("height")){
+ ch.setHeight(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("xoffset")){
+ ch.setXOffset(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("yoffset")){
+ ch.setYOffset(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("xadvance")){
+ ch.setXAdvance(Integer.parseInt(tokens[i + 1]));
+ } else if (token.equals("page")) {
+ ch.setPage(Integer.parseInt(tokens[i + 1]));
+ }
+ }
+ }else if (tokens[0].equals("kerning")){
+ // Build kerning list
+ int index = 0;
+ int second = 0;
+ int amount = 0;
+
+ for (int i = 1; i < tokens.length; i++){
+ if (tokens[i].equals("first")){
+ index = Integer.parseInt(tokens[i + 1]);
+ }else if (tokens[i].equals("second")){
+ second = Integer.parseInt(tokens[i + 1]);
+ }else if (tokens[i].equals("amount")){
+ amount = Integer.parseInt(tokens[i + 1]);
+ }
+ }
+
+ BitmapCharacter ch = charSet.getCharacter(index);
+ ch.addKerning(second, amount);
+ }
+ }
+ return font;
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ BitmapFont font = load(info.getManager(), info.getKey().getFolder(), in);
+ return font;
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java b/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java
new file mode 100644
index 0000000..e07d3e4
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.material.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.material.*;
+import com.jme3.material.TechniqueDef.LightMode;
+import com.jme3.material.TechniqueDef.ShadowMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.blockparser.BlockLanguageParser;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class J3MLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(J3MLoader.class.getName());
+
+ private AssetManager assetManager;
+ private AssetKey key;
+
+ private MaterialDef materialDef;
+ private Material material;
+ private TechniqueDef technique;
+ private RenderState renderState;
+
+ private String shaderLang;
+ private String vertName;
+ private String fragName;
+
+ private static final String whitespacePattern = "\\p{javaWhitespace}+";
+
+ public J3MLoader(){
+ }
+
+ private void throwIfNequal(String expected, String got) throws IOException {
+ if (expected == null)
+ throw new IOException("Expected a statement, got '"+got+"'!");
+
+ if (!expected.equals(got))
+ throw new IOException("Expected '"+expected+"', got '"+got+"'!");
+ }
+
+ // <TYPE> <LANG> : <SOURCE>
+ private void readShaderStatement(String statement) throws IOException {
+ String[] split = statement.split(":");
+ if (split.length != 2){
+ throw new IOException("Shader statement syntax incorrect" + statement);
+ }
+ String[] typeAndLang = split[0].split(whitespacePattern);
+ if (typeAndLang.length != 2){
+ throw new IOException("Shader statement syntax incorrect: " + statement);
+ }
+ shaderLang = typeAndLang[1];
+ if (typeAndLang[0].equals("VertexShader")){
+ vertName = split[1].trim();
+ }else if (typeAndLang[0].equals("FragmentShader")){
+ fragName = split[1].trim();
+ }
+ }
+
+ // LightMode <MODE>
+ private void readLightMode(String statement) throws IOException{
+ String[] split = statement.split(whitespacePattern);
+ if (split.length != 2){
+ throw new IOException("LightMode statement syntax incorrect");
+ }
+ LightMode lm = LightMode.valueOf(split[1]);
+ technique.setLightMode(lm);
+ }
+
+ // ShadowMode <MODE>
+ private void readShadowMode(String statement) throws IOException{
+ String[] split = statement.split(whitespacePattern);
+ if (split.length != 2){
+ throw new IOException("ShadowMode statement syntax incorrect");
+ }
+ ShadowMode sm = ShadowMode.valueOf(split[1]);
+ technique.setShadowMode(sm);
+ }
+
+ private Object readValue(VarType type, String value) throws IOException{
+ if (type.isTextureType()){
+// String texturePath = readString("[\n;(//)(\\})]");
+ String texturePath = value.trim();
+ boolean flipY = false;
+ boolean repeat = false;
+ if (texturePath.startsWith("Flip Repeat ")){
+ texturePath = texturePath.substring(12).trim();
+ flipY = true;
+ repeat = true;
+ }else if (texturePath.startsWith("Flip ")){
+ texturePath = texturePath.substring(5).trim();
+ flipY = true;
+ }else if (texturePath.startsWith("Repeat ")){
+ texturePath = texturePath.substring(7).trim();
+ repeat = true;
+ }
+
+ TextureKey texKey = new TextureKey(texturePath, flipY);
+ texKey.setAsCube(type == VarType.TextureCubeMap);
+ texKey.setGenerateMips(true);
+
+ Texture tex;
+ try {
+ tex = assetManager.loadTexture(texKey);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key});
+ tex = null;
+ }
+ if (tex != null){
+ if (repeat){
+ tex.setWrap(WrapMode.Repeat);
+ }
+ }else{
+ tex = new Texture2D(PlaceholderAssets.getPlaceholderImage());
+ }
+ return tex;
+ }else{
+ String[] split = value.trim().split(whitespacePattern);
+ switch (type){
+ case Float:
+ if (split.length != 1){
+ throw new IOException("Float value parameter must have 1 entry: " + value);
+ }
+ return Float.parseFloat(split[0]);
+ case Vector2:
+ if (split.length != 2){
+ throw new IOException("Vector2 value parameter must have 2 entries: " + value);
+ }
+ return new Vector2f(Float.parseFloat(split[0]),
+ Float.parseFloat(split[1]));
+ case Vector3:
+ if (split.length != 3){
+ throw new IOException("Vector3 value parameter must have 3 entries: " + value);
+ }
+ return new Vector3f(Float.parseFloat(split[0]),
+ Float.parseFloat(split[1]),
+ Float.parseFloat(split[2]));
+ case Vector4:
+ if (split.length != 4){
+ throw new IOException("Vector4 value parameter must have 4 entries: " + value);
+ }
+ return new ColorRGBA(Float.parseFloat(split[0]),
+ Float.parseFloat(split[1]),
+ Float.parseFloat(split[2]),
+ Float.parseFloat(split[3]));
+ case Int:
+ if (split.length != 1){
+ throw new IOException("Int value parameter must have 1 entry: " + value);
+ }
+ return Integer.parseInt(split[0]);
+ case Boolean:
+ if (split.length != 1){
+ throw new IOException("Boolean value parameter must have 1 entry: " + value);
+ }
+ return Boolean.parseBoolean(split[0]);
+ default:
+ throw new UnsupportedOperationException("Unknown type: "+type);
+ }
+ }
+ }
+
+ // <TYPE> <NAME> [ "(" <FFBINDING> ")" ] [ ":" <DEFAULTVAL> ]
+ private void readParam(String statement) throws IOException{
+ String name;
+ String defaultVal = null;
+ FixedFuncBinding ffBinding = null;
+
+ String[] split = statement.split(":");
+
+ // Parse default val
+ if (split.length == 1){
+ // Doesn't contain default value
+ }else{
+ if (split.length != 2){
+ throw new IOException("Parameter statement syntax incorrect");
+ }
+ statement = split[0].trim();
+ defaultVal = split[1].trim();
+ }
+
+ // Parse ffbinding
+ int startParen = statement.indexOf("(");
+ if (startParen != -1){
+ // get content inside parentheses
+ int endParen = statement.indexOf(")", startParen);
+ String bindingStr = statement.substring(startParen+1, endParen).trim();
+ try {
+ ffBinding = FixedFuncBinding.valueOf(bindingStr);
+ } catch (IllegalArgumentException ex){
+ throw new IOException("FixedFuncBinding '" +
+ split[1] + "' does not exist!");
+ }
+ statement = statement.substring(0, startParen);
+ }
+
+ // Parse type + name
+ split = statement.split(whitespacePattern);
+ if (split.length != 2){
+ throw new IOException("Parameter statement syntax incorrect");
+ }
+
+ VarType type;
+ if (split[0].equals("Color")){
+ type = VarType.Vector4;
+ }else{
+ type = VarType.valueOf(split[0]);
+ }
+
+ name = split[1];
+
+ Object defaultValObj = null;
+ if (defaultVal != null){
+ defaultValObj = readValue(type, defaultVal);
+ }
+
+ materialDef.addMaterialParam(type, name, defaultValObj, ffBinding);
+ }
+
+ private void readValueParam(String statement) throws IOException{
+ // Use limit=1 incase filename contains colons
+ String[] split = statement.split(":", 2);
+ if (split.length != 2){
+ throw new IOException("Value parameter statement syntax incorrect");
+ }
+ String name = split[0].trim();
+
+ // parse value
+ MatParam p = material.getMaterialDef().getMaterialParam(name);
+ if (p == null){
+ throw new IOException("The material parameter: "+name+" is undefined.");
+ }
+
+ Object valueObj = readValue(p.getVarType(), split[1]);
+ if (p.getVarType().isTextureType()){
+ material.setTextureParam(name, p.getVarType(), (Texture) valueObj);
+ }else{
+ material.setParam(name, p.getVarType(), valueObj);
+ }
+ }
+
+ private void readMaterialParams(List<Statement> paramsList) throws IOException{
+ for (Statement statement : paramsList){
+ readParam(statement.getLine());
+ }
+ }
+
+ private void readExtendingMaterialParams(List<Statement> paramsList) throws IOException{
+ for (Statement statement : paramsList){
+ readValueParam(statement.getLine());
+ }
+ }
+
+ private void readWorldParams(List<Statement> worldParams) throws IOException{
+ for (Statement statement : worldParams){
+ technique.addWorldParam(statement.getLine());
+ }
+ }
+
+ private boolean parseBoolean(String word){
+ return word != null && word.equals("On");
+ }
+
+ private void readRenderStateStatement(String statement) throws IOException{
+ String[] split = statement.split(whitespacePattern);
+ if (split[0].equals("Wireframe")){
+ renderState.setWireframe(parseBoolean(split[1]));
+ }else if (split[0].equals("FaceCull")){
+ renderState.setFaceCullMode(FaceCullMode.valueOf(split[1]));
+ }else if (split[0].equals("DepthWrite")){
+ renderState.setDepthWrite(parseBoolean(split[1]));
+ }else if (split[0].equals("DepthTest")){
+ renderState.setDepthTest(parseBoolean(split[1]));
+ }else if (split[0].equals("Blend")){
+ renderState.setBlendMode(BlendMode.valueOf(split[1]));
+ }else if (split[0].equals("AlphaTestFalloff")){
+ renderState.setAlphaTest(true);
+ renderState.setAlphaFallOff(Float.parseFloat(split[1]));
+ }else if (split[0].equals("PolyOffset")){
+ float factor = Float.parseFloat(split[1]);
+ float units = Float.parseFloat(split[2]);
+ renderState.setPolyOffset(factor, units);
+ }else if (split[0].equals("ColorWrite")){
+ renderState.setColorWrite(parseBoolean(split[1]));
+ }else if (split[0].equals("PointSprite")){
+ renderState.setPointSprite(parseBoolean(split[1]));
+ }else{
+ throwIfNequal(null, split[0]);
+ }
+ }
+
+ private void readAdditionalRenderState(List<Statement> renderStates) throws IOException{
+ renderState = material.getAdditionalRenderState();
+ for (Statement statement : renderStates){
+ readRenderStateStatement(statement.getLine());
+ }
+ renderState = null;
+ }
+
+ private void readRenderState(List<Statement> renderStates) throws IOException{
+ renderState = new RenderState();
+ for (Statement statement : renderStates){
+ readRenderStateStatement(statement.getLine());
+ }
+ technique.setRenderState(renderState);
+ renderState = null;
+ }
+
+ // <DEFINENAME> [ ":" <PARAMNAME> ]
+ private void readDefine(String statement) throws IOException{
+ String[] split = statement.split(":");
+ if (split.length == 1){
+ // add preset define
+ technique.addShaderPresetDefine(split[0].trim(), VarType.Boolean, true);
+ }else if (split.length == 2){
+ technique.addShaderParamDefine(split[1].trim(), split[0].trim());
+ }else{
+ throw new IOException("Define syntax incorrect");
+ }
+ }
+
+ private void readDefines(List<Statement> defineList) throws IOException{
+ for (Statement statement : defineList){
+ readDefine(statement.getLine());
+ }
+
+ }
+
+ private void readTechniqueStatement(Statement statement) throws IOException{
+ String[] split = statement.getLine().split("[ \\{]");
+ if (split[0].equals("VertexShader") ||
+ split[0].equals("FragmentShader")){
+ readShaderStatement(statement.getLine());
+ }else if (split[0].equals("LightMode")){
+ readLightMode(statement.getLine());
+ }else if (split[0].equals("ShadowMode")){
+ readShadowMode(statement.getLine());
+ }else if (split[0].equals("WorldParameters")){
+ readWorldParams(statement.getContents());
+ }else if (split[0].equals("RenderState")){
+ readRenderState(statement.getContents());
+ }else if (split[0].equals("Defines")){
+ readDefines(statement.getContents());
+ }else{
+ throwIfNequal(null, split[0]);
+ }
+ }
+
+ private void readTransparentStatement(String statement) throws IOException{
+ String[] split = statement.split(whitespacePattern);
+ if (split.length != 2){
+ throw new IOException("Transparent statement syntax incorrect");
+ }
+ material.setTransparent(parseBoolean(split[1]));
+ }
+
+ private void readTechnique(Statement techStat) throws IOException{
+ String[] split = techStat.getLine().split(whitespacePattern);
+ if (split.length == 1){
+ technique = new TechniqueDef(null);
+ }else if (split.length == 2){
+ technique = new TechniqueDef(split[1]);
+ }else{
+ throw new IOException("Technique statement syntax incorrect");
+ }
+
+ for (Statement statement : techStat.getContents()){
+ readTechniqueStatement(statement);
+ }
+
+ if (vertName != null && fragName != null){
+ technique.setShaderFile(vertName, fragName, shaderLang);
+ }
+
+ materialDef.addTechniqueDef(technique);
+ technique = null;
+ vertName = null;
+ fragName = null;
+ shaderLang = null;
+ }
+
+ private void loadFromRoot(List<Statement> roots) throws IOException{
+ if (roots.size() == 2){
+ Statement exception = roots.get(0);
+ String line = exception.getLine();
+ if (line.startsWith("Exception")){
+ throw new AssetLoadException(line.substring("Exception ".length()));
+ }else{
+ throw new IOException("In multiroot material, expected first statement to be 'Exception'");
+ }
+ }else if (roots.size() != 1){
+ throw new IOException("Too many roots in J3M/J3MD file");
+ }
+
+ boolean extending = false;
+ Statement materialStat = roots.get(0);
+ String materialName = materialStat.getLine();
+ if (materialName.startsWith("MaterialDef")){
+ materialName = materialName.substring("MaterialDef ".length()).trim();
+ extending = false;
+ }else if (materialName.startsWith("Material")){
+ materialName = materialName.substring("Material ".length()).trim();
+ extending = true;
+ }else{
+ throw new IOException("Specified file is not a Material file");
+ }
+
+ String[] split = materialName.split(":", 2);
+
+ if (materialName.equals("")){
+ throw new IOException("Material name cannot be empty");
+ }
+
+ if (split.length == 2){
+ if (!extending){
+ throw new IOException("Must use 'Material' when extending.");
+ }
+
+ String extendedMat = split[1].trim();
+
+ MaterialDef def = (MaterialDef) assetManager.loadAsset(new AssetKey(extendedMat));
+ if (def == null)
+ throw new IOException("Extended material "+extendedMat+" cannot be found.");
+
+ material = new Material(def);
+ material.setKey(key);
+// material.setAssetName(fileName);
+ }else if (split.length == 1){
+ if (extending){
+ throw new IOException("Expected ':', got '{'");
+ }
+ materialDef = new MaterialDef(assetManager, materialName);
+ // NOTE: pass file name for defs so they can be loaded later
+ materialDef.setAssetName(key.getName());
+ }else{
+ throw new IOException("Cannot use colon in material name/path");
+ }
+
+ for (Statement statement : materialStat.getContents()){
+ split = statement.getLine().split("[ \\{]");
+ String statType = split[0];
+ if (extending){
+ if (statType.equals("MaterialParameters")){
+ readExtendingMaterialParams(statement.getContents());
+ }else if (statType.equals("AdditionalRenderState")){
+ readAdditionalRenderState(statement.getContents());
+ }else if (statType.equals("Transparent")){
+ readTransparentStatement(statement.getLine());
+ }
+ }else{
+ if (statType.equals("Technique")){
+ readTechnique(statement);
+ }else if (statType.equals("MaterialParameters")){
+ readMaterialParams(statement.getContents());
+ }else{
+ throw new IOException("Expected material statement, got '"+statType+"'");
+ }
+ }
+ }
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ this.assetManager = info.getManager();
+
+ InputStream in = info.openStream();
+ try {
+ key = info.getKey();
+ loadFromRoot(BlockLanguageParser.parse(in));
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+
+ if (material != null){
+ if (!(info.getKey() instanceof MaterialKey)){
+ throw new IOException("Material instances must be loaded via MaterialKey");
+ }
+ // material implementation
+ return material;
+ }else{
+ // material definition
+ return materialDef;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java
new file mode 100644
index 0000000..1f701cd
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.PlaceholderAssets;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class MTLLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(MTLLoader.class.getName());
+
+ protected Scanner scan;
+ protected MaterialList matList;
+ //protected Material material;
+ protected AssetManager assetManager;
+ protected String folderName;
+ protected AssetKey key;
+
+ protected Texture diffuseMap, normalMap, specularMap, alphaMap;
+ protected ColorRGBA ambient = new ColorRGBA();
+ protected ColorRGBA diffuse = new ColorRGBA();
+ protected ColorRGBA specular = new ColorRGBA();
+ protected float shininess = 16;
+ protected boolean shadeless;
+ protected String matName;
+ protected float alpha = 1;
+ protected boolean transparent = false;
+ protected boolean disallowTransparency = false;
+ protected boolean disallowAmbient = false;
+ protected boolean disallowSpecular = false;
+
+ public void reset(){
+ scan = null;
+ matList = null;
+// material = null;
+
+ resetMaterial();
+ }
+
+ protected ColorRGBA readColor(){
+ ColorRGBA v = new ColorRGBA();
+ v.set(scan.nextFloat(), scan.nextFloat(), scan.nextFloat(), 1.0f);
+ return v;
+ }
+
+ protected String nextStatement(){
+ scan.useDelimiter("\n");
+ String result = scan.next();
+ scan.useDelimiter("\\p{javaWhitespace}+");
+ return result;
+ }
+
+ protected boolean skipLine(){
+ try {
+ scan.skip(".*\r{0,1}\n");
+ return true;
+ } catch (NoSuchElementException ex){
+ // EOF
+ return false;
+ }
+ }
+
+ protected void resetMaterial(){
+ ambient.set(ColorRGBA.DarkGray);
+ diffuse.set(ColorRGBA.LightGray);
+ specular.set(ColorRGBA.Black);
+ shininess = 16;
+ disallowTransparency = false;
+ disallowAmbient = false;
+ disallowSpecular = false;
+ shadeless = false;
+ transparent = false;
+ matName = null;
+ diffuseMap = null;
+ specularMap = null;
+ normalMap = null;
+ alphaMap = null;
+ alpha = 1;
+ }
+
+ protected void createMaterial(){
+ Material material;
+
+ if (alpha < 1f && transparent && !disallowTransparency){
+ diffuse.a = alpha;
+ }
+
+ if (shadeless){
+ material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setColor("Color", diffuse.clone());
+ material.setTexture("ColorMap", diffuseMap);
+ // TODO: Add handling for alpha map?
+ }else{
+ material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ material.setBoolean("UseMaterialColors", true);
+ material.setColor("Ambient", ambient.clone());
+ material.setColor("Diffuse", diffuse.clone());
+ material.setColor("Specular", specular.clone());
+ material.setFloat("Shininess", shininess); // prevents "premature culling" bug
+
+ if (diffuseMap != null) material.setTexture("DiffuseMap", diffuseMap);
+ if (specularMap != null) material.setTexture("SpecularMap", specularMap);
+ if (normalMap != null) material.setTexture("NormalMap", normalMap);
+ if (alphaMap != null) material.setTexture("AlphaMap", alphaMap);
+ }
+
+ if (transparent && !disallowTransparency){
+ material.setTransparent(true);
+ material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ material.getAdditionalRenderState().setAlphaTest(true);
+ material.getAdditionalRenderState().setAlphaFallOff(0.01f);
+ }
+
+ matList.put(matName, material);
+ }
+
+ protected void startMaterial(String name){
+ if (matName != null){
+ // material is already in cache, generate it
+ createMaterial();
+ }
+
+ // now, reset the params and set the name to start a new material
+ resetMaterial();
+ matName = name;
+ }
+
+ protected Texture loadTexture(String path){
+ String[] split = path.trim().split("\\p{javaWhitespace}+");
+
+ // will crash if path is an empty string
+ path = split[split.length-1];
+
+ String name = new File(path).getName();
+ TextureKey texKey = new TextureKey(folderName + name);
+ texKey.setGenerateMips(true);
+ Texture texture;
+ try {
+ texture = assetManager.loadTexture(texKey);
+ texture.setWrap(WrapMode.Repeat);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key});
+ texture = new Texture2D(PlaceholderAssets.getPlaceholderImage());
+ }
+ return texture;
+ }
+
+ protected boolean readLine(){
+ if (!scan.hasNext()){
+ return false;
+ }
+
+ String cmd = scan.next().toLowerCase();
+ if (cmd.startsWith("#")){
+ // skip entire comment until next line
+ return skipLine();
+ }else if (cmd.equals("newmtl")){
+ String name = scan.next();
+ startMaterial(name);
+ }else if (cmd.equals("ka")){
+ ambient.set(readColor());
+ }else if (cmd.equals("kd")){
+ diffuse.set(readColor());
+ }else if (cmd.equals("ks")){
+ specular.set(readColor());
+ }else if (cmd.equals("ns")){
+ float shiny = scan.nextFloat();
+ if (shiny >= 1){
+ shininess = shiny; /* (128f / 1000f)*/
+ if (specular.equals(ColorRGBA.Black)){
+ specular.set(ColorRGBA.White);
+ }
+ }else{
+ // For some reason blender likes to export Ns 0 statements
+ // Ignore Ns 0 instead of setting it
+ }
+
+ }else if (cmd.equals("d") || cmd.equals("tr")){
+ alpha = scan.nextFloat();
+ transparent = true;
+ }else if (cmd.equals("map_ka")){
+ // ignore it for now
+ return skipLine();
+ }else if (cmd.equals("map_kd")){
+ String path = nextStatement();
+ diffuseMap = loadTexture(path);
+ }else if (cmd.equals("map_bump") || cmd.equals("bump")){
+ if (normalMap == null){
+ String path = nextStatement();
+ normalMap = loadTexture(path);
+ }
+ }else if (cmd.equals("map_ks")){
+ String path = nextStatement();
+ specularMap = loadTexture(path);
+ if (specularMap != null){
+ // NOTE: since specular color is modulated with specmap
+ // make sure we have it set
+ if (specular.equals(ColorRGBA.Black)){
+ specular.set(ColorRGBA.White);
+ }
+ }
+ }else if (cmd.equals("map_d")){
+ String path = scan.next();
+ alphaMap = loadTexture(path);
+ transparent = true;
+ }else if (cmd.equals("illum")){
+ int mode = scan.nextInt();
+
+ switch (mode){
+ case 0:
+ // no lighting
+ shadeless = true;
+ disallowTransparency = true;
+ break;
+ case 1:
+ disallowSpecular = true;
+ disallowTransparency = true;
+ break;
+ case 2:
+ case 3:
+ case 5:
+ case 8:
+ disallowTransparency = true;
+ break;
+ case 4:
+ case 6:
+ case 7:
+ case 9:
+ // Enable transparency
+ // Works best if diffuse map has an alpha channel
+ transparent = true;
+ break;
+ }
+ }else if (cmd.equals("ke") || cmd.equals("ni")){
+ // Ni: index of refraction - unsupported in jME
+ // Ke: emission color
+ return skipLine();
+ }else{
+ logger.log(Level.WARNING, "Unknown statement in MTL! {0}", cmd);
+ return skipLine();
+ }
+
+ return true;
+ }
+
+ @SuppressWarnings("empty-statement")
+ public Object load(AssetInfo info) throws IOException{
+ reset();
+
+ this.key = info.getKey();
+ this.assetManager = info.getManager();
+ folderName = info.getKey().getFolder();
+ matList = new MaterialList();
+
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ scan = new Scanner(in);
+ scan.useLocale(Locale.US);
+
+ while (readLine());
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+
+ if (matName != null){
+ // still have a material in the vars
+ createMaterial();
+ resetMaterial();
+ }
+
+ MaterialList list = matList;
+
+
+
+ return list;
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
new file mode 100644
index 0000000..3ce7f52
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.scene.mesh.IndexIntBuffer;
+import com.jme3.scene.mesh.IndexShortBuffer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.Map.Entry;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Reads OBJ format models.
+ */
+public final class OBJLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(OBJLoader.class.getName());
+
+ protected final ArrayList<Vector3f> verts = new ArrayList<Vector3f>();
+ protected final ArrayList<Vector2f> texCoords = new ArrayList<Vector2f>();
+ protected final ArrayList<Vector3f> norms = new ArrayList<Vector3f>();
+
+ protected final ArrayList<Face> faces = new ArrayList<Face>();
+ protected final HashMap<String, ArrayList<Face>> matFaces = new HashMap<String, ArrayList<Face>>();
+
+ protected String currentMatName;
+ protected String currentObjectName;
+
+ protected final HashMap<Vertex, Integer> vertIndexMap = new HashMap<Vertex, Integer>(100);
+ protected final IntMap<Vertex> indexVertMap = new IntMap<Vertex>(100);
+ protected int curIndex = 0;
+ protected int objectIndex = 0;
+ protected int geomIndex = 0;
+
+ protected Scanner scan;
+ protected ModelKey key;
+ protected AssetManager assetManager;
+ protected MaterialList matList;
+
+ protected String objName;
+ protected Node objNode;
+
+ protected static class Vertex {
+
+ Vector3f v;
+ Vector2f vt;
+ Vector3f vn;
+ int index;
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Vertex other = (Vertex) obj;
+ if (this.v != other.v && (this.v == null || !this.v.equals(other.v))) {
+ return false;
+ }
+ if (this.vt != other.vt && (this.vt == null || !this.vt.equals(other.vt))) {
+ return false;
+ }
+ if (this.vn != other.vn && (this.vn == null || !this.vn.equals(other.vn))) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 53 * hash + (this.v != null ? this.v.hashCode() : 0);
+ hash = 53 * hash + (this.vt != null ? this.vt.hashCode() : 0);
+ hash = 53 * hash + (this.vn != null ? this.vn.hashCode() : 0);
+ return hash;
+ }
+ }
+
+ protected static class Face {
+ Vertex[] verticies;
+ }
+
+ protected class ObjectGroup {
+
+ final String objectName;
+
+ public ObjectGroup(String objectName){
+ this.objectName = objectName;
+ }
+
+ public Spatial createGeometry(){
+ Node groupNode = new Node(objectName);
+
+// if (matFaces.size() > 0){
+// for (Entry<String, ArrayList<Face>> entry : matFaces.entrySet()){
+// ArrayList<Face> materialFaces = entry.getValue();
+// if (materialFaces.size() > 0){
+// Geometry geom = createGeometry(materialFaces, entry.getKey());
+// objNode.attachChild(geom);
+// }
+// }
+// }else if (faces.size() > 0){
+// // generate final geometry
+// Geometry geom = createGeometry(faces, null);
+// objNode.attachChild(geom);
+// }
+
+ return groupNode;
+ }
+ }
+
+ public void reset(){
+ verts.clear();
+ texCoords.clear();
+ norms.clear();
+ faces.clear();
+ matFaces.clear();
+
+ vertIndexMap.clear();
+ indexVertMap.clear();
+
+ currentMatName = null;
+ matList = null;
+ curIndex = 0;
+ geomIndex = 0;
+ scan = null;
+ }
+
+ protected void findVertexIndex(Vertex vert){
+ Integer index = vertIndexMap.get(vert);
+ if (index != null){
+ vert.index = index.intValue();
+ }else{
+ vert.index = curIndex++;
+ vertIndexMap.put(vert, vert.index);
+ indexVertMap.put(vert.index, vert);
+ }
+ }
+
+ protected Face[] quadToTriangle(Face f){
+ assert f.verticies.length == 4;
+
+ Face[] t = new Face[]{ new Face(), new Face() };
+ t[0].verticies = new Vertex[3];
+ t[1].verticies = new Vertex[3];
+
+ Vertex v0 = f.verticies[0];
+ Vertex v1 = f.verticies[1];
+ Vertex v2 = f.verticies[2];
+ Vertex v3 = f.verticies[3];
+
+ // find the pair of verticies that is closest to each over
+ // v0 and v2
+ // OR
+ // v1 and v3
+ float d1 = v0.v.distanceSquared(v2.v);
+ float d2 = v1.v.distanceSquared(v3.v);
+ if (d1 < d2){
+ // put an edge in v0, v2
+ t[0].verticies[0] = v0;
+ t[0].verticies[1] = v1;
+ t[0].verticies[2] = v3;
+
+ t[1].verticies[0] = v1;
+ t[1].verticies[1] = v2;
+ t[1].verticies[2] = v3;
+ }else{
+ // put an edge in v1, v3
+ t[0].verticies[0] = v0;
+ t[0].verticies[1] = v1;
+ t[0].verticies[2] = v2;
+
+ t[1].verticies[0] = v0;
+ t[1].verticies[1] = v2;
+ t[1].verticies[2] = v3;
+ }
+
+ return t;
+ }
+
+ private ArrayList<Vertex> vertList = new ArrayList<Vertex>();
+
+ protected void readFace(){
+ Face f = new Face();
+ vertList.clear();
+
+ String line = scan.nextLine().trim();
+ String[] verticies = line.split("\\s");
+ for (String vertex : verticies){
+ int v = 0;
+ int vt = 0;
+ int vn = 0;
+
+ String[] split = vertex.split("/");
+ if (split.length == 1){
+ v = Integer.parseInt(split[0].trim());
+ }else if (split.length == 2){
+ v = Integer.parseInt(split[0].trim());
+ vt = Integer.parseInt(split[1].trim());
+ }else if (split.length == 3 && !split[1].equals("")){
+ v = Integer.parseInt(split[0].trim());
+ vt = Integer.parseInt(split[1].trim());
+ vn = Integer.parseInt(split[2].trim());
+ }else if (split.length == 3){
+ v = Integer.parseInt(split[0].trim());
+ vn = Integer.parseInt(split[2].trim());
+ }
+
+ Vertex vx = new Vertex();
+ vx.v = verts.get(v - 1);
+
+ if (vt > 0)
+ vx.vt = texCoords.get(vt - 1);
+
+ if (vn > 0)
+ vx.vn = norms.get(vn - 1);
+
+ vertList.add(vx);
+ }
+
+ if (vertList.size() > 4 || vertList.size() <= 2)
+ logger.warning("Edge or polygon detected in OBJ. Ignored.");
+
+ f.verticies = new Vertex[vertList.size()];
+ for (int i = 0; i < vertList.size(); i++){
+ f.verticies[i] = vertList.get(i);
+ }
+
+ if (matList != null && matFaces.containsKey(currentMatName)){
+ matFaces.get(currentMatName).add(f);
+ }else{
+ faces.add(f); // faces that belong to the default material
+ }
+ }
+
+ protected Vector3f readVector3(){
+ Vector3f v = new Vector3f();
+
+ v.set(Float.parseFloat(scan.next()),
+ Float.parseFloat(scan.next()),
+ Float.parseFloat(scan.next()));
+
+ return v;
+ }
+
+ protected Vector2f readVector2(){
+ Vector2f v = new Vector2f();
+
+ String line = scan.nextLine().trim();
+ String[] split = line.split("\\s");
+ v.setX( Float.parseFloat(split[0].trim()) );
+ v.setY( Float.parseFloat(split[1].trim()) );
+
+// v.setX(scan.nextFloat());
+// if (scan.hasNextFloat()){
+// v.setY(scan.nextFloat());
+// if (scan.hasNextFloat()){
+// scan.nextFloat(); // ignore
+// }
+// }
+
+ return v;
+ }
+
+ protected void loadMtlLib(String name) throws IOException{
+ if (!name.toLowerCase().endsWith(".mtl"))
+ throw new IOException("Expected .mtl file! Got: " + name);
+
+ // NOTE: Cut off any relative/absolute paths
+ name = new File(name).getName();
+ AssetKey mtlKey = new AssetKey(key.getFolder() + name);
+ try {
+ matList = (MaterialList) assetManager.loadAsset(mtlKey);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{name, key});
+ }
+
+ if (matList != null){
+ // create face lists for every material
+ for (String matName : matList.keySet()){
+ matFaces.put(matName, new ArrayList<Face>());
+ }
+ }
+ }
+
+ protected boolean nextStatement(){
+ try {
+ scan.skip(".*\r{0,1}\n");
+ return true;
+ } catch (NoSuchElementException ex){
+ // EOF
+ return false;
+ }
+ }
+
+ protected boolean readLine() throws IOException{
+ if (!scan.hasNext()){
+ return false;
+ }
+
+ String cmd = scan.next();
+ if (cmd.startsWith("#")){
+ // skip entire comment until next line
+ return nextStatement();
+ }else if (cmd.equals("v")){
+ // vertex position
+ verts.add(readVector3());
+ }else if (cmd.equals("vn")){
+ // vertex normal
+ norms.add(readVector3());
+ }else if (cmd.equals("vt")){
+ // texture coordinate
+ texCoords.add(readVector2());
+ }else if (cmd.equals("f")){
+ // face, can be triangle, quad, or polygon (unsupported)
+ readFace();
+ }else if (cmd.equals("usemtl")){
+ // use material from MTL lib for the following faces
+ currentMatName = scan.next();
+// if (!matList.containsKey(currentMatName))
+// throw new IOException("Cannot locate material " + currentMatName + " in MTL file!");
+
+ }else if (cmd.equals("mtllib")){
+ // specify MTL lib to use for this OBJ file
+ String mtllib = scan.nextLine().trim();
+ loadMtlLib(mtllib);
+ }else if (cmd.equals("s") || cmd.equals("g")){
+ return nextStatement();
+ }else{
+ // skip entire command until next line
+ logger.log(Level.WARNING, "Unknown statement in OBJ! {0}", cmd);
+ return nextStatement();
+ }
+
+ return true;
+ }
+
+ protected Geometry createGeometry(ArrayList<Face> faceList, String matName) throws IOException{
+ if (faceList.isEmpty())
+ throw new IOException("No geometry data to generate mesh");
+
+ // Create mesh from the faces
+ Mesh mesh = constructMesh(faceList);
+
+ Geometry geom = new Geometry(objName + "-geom-" + (geomIndex++), mesh);
+
+ Material material = null;
+ if (matName != null && matList != null){
+ // Get material from material list
+ material = matList.get(matName);
+ }
+ if (material == null){
+ // create default material
+ material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ material.setFloat("Shininess", 64);
+ }
+ geom.setMaterial(material);
+ if (material.isTransparent())
+ geom.setQueueBucket(Bucket.Transparent);
+ else
+ geom.setQueueBucket(Bucket.Opaque);
+
+ if (material.getMaterialDef().getName().contains("Lighting")
+ && mesh.getFloatBuffer(Type.Normal) == null){
+ logger.log(Level.WARNING, "OBJ mesh {0} doesn't contain normals! "
+ + "It might not display correctly", geom.getName());
+ }
+
+ return geom;
+ }
+
+ protected Mesh constructMesh(ArrayList<Face> faceList){
+ Mesh m = new Mesh();
+ m.setMode(Mode.Triangles);
+
+ boolean hasTexCoord = false;
+ boolean hasNormals = false;
+
+ ArrayList<Face> newFaces = new ArrayList<Face>(faceList.size());
+ for (int i = 0; i < faceList.size(); i++){
+ Face f = faceList.get(i);
+
+ for (Vertex v : f.verticies){
+ findVertexIndex(v);
+
+ if (!hasTexCoord && v.vt != null)
+ hasTexCoord = true;
+ if (!hasNormals && v.vn != null)
+ hasNormals = true;
+ }
+
+ if (f.verticies.length == 4){
+ Face[] t = quadToTriangle(f);
+ newFaces.add(t[0]);
+ newFaces.add(t[1]);
+ }else{
+ newFaces.add(f);
+ }
+ }
+
+ FloatBuffer posBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
+ FloatBuffer normBuf = null;
+ FloatBuffer tcBuf = null;
+
+ if (hasNormals){
+ normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
+ }
+ if (hasTexCoord){
+ tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2);
+ }
+
+ IndexBuffer indexBuf = null;
+ if (vertIndexMap.size() >= 65536){
+ // too many verticies: use intbuffer instead of shortbuffer
+ IntBuffer ib = BufferUtils.createIntBuffer(newFaces.size() * 3);
+ m.setBuffer(VertexBuffer.Type.Index, 3, ib);
+ indexBuf = new IndexIntBuffer(ib);
+ }else{
+ ShortBuffer sb = BufferUtils.createShortBuffer(newFaces.size() * 3);
+ m.setBuffer(VertexBuffer.Type.Index, 3, sb);
+ indexBuf = new IndexShortBuffer(sb);
+ }
+
+ int numFaces = newFaces.size();
+ for (int i = 0; i < numFaces; i++){
+ Face f = newFaces.get(i);
+ if (f.verticies.length != 3)
+ continue;
+
+ Vertex v0 = f.verticies[0];
+ Vertex v1 = f.verticies[1];
+ Vertex v2 = f.verticies[2];
+
+ posBuf.position(v0.index * 3);
+ posBuf.put(v0.v.x).put(v0.v.y).put(v0.v.z);
+ posBuf.position(v1.index * 3);
+ posBuf.put(v1.v.x).put(v1.v.y).put(v1.v.z);
+ posBuf.position(v2.index * 3);
+ posBuf.put(v2.v.x).put(v2.v.y).put(v2.v.z);
+
+ if (normBuf != null){
+ if (v0.vn != null){
+ normBuf.position(v0.index * 3);
+ normBuf.put(v0.vn.x).put(v0.vn.y).put(v0.vn.z);
+ normBuf.position(v1.index * 3);
+ normBuf.put(v1.vn.x).put(v1.vn.y).put(v1.vn.z);
+ normBuf.position(v2.index * 3);
+ normBuf.put(v2.vn.x).put(v2.vn.y).put(v2.vn.z);
+ }
+ }
+
+ if (tcBuf != null){
+ if (v0.vt != null){
+ tcBuf.position(v0.index * 2);
+ tcBuf.put(v0.vt.x).put(v0.vt.y);
+ tcBuf.position(v1.index * 2);
+ tcBuf.put(v1.vt.x).put(v1.vt.y);
+ tcBuf.position(v2.index * 2);
+ tcBuf.put(v2.vt.x).put(v2.vt.y);
+ }
+ }
+
+ int index = i * 3; // current face * 3 = current index
+ indexBuf.put(index, v0.index);
+ indexBuf.put(index+1, v1.index);
+ indexBuf.put(index+2, v2.index);
+ }
+
+ m.setBuffer(VertexBuffer.Type.Position, 3, posBuf);
+ m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);
+ m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);
+ // index buffer was set on creation
+
+ m.setStatic();
+ m.updateBound();
+ m.updateCounts();
+ //m.setInterleaved();
+
+ // clear data generated face statements
+ // to prepare for next mesh
+ vertIndexMap.clear();
+ indexVertMap.clear();
+ curIndex = 0;
+
+ return m;
+ }
+
+ @SuppressWarnings("empty-statement")
+ public Object load(AssetInfo info) throws IOException{
+ reset();
+
+ key = (ModelKey) info.getKey();
+ assetManager = info.getManager();
+ objName = key.getName();
+
+ String folderName = key.getFolder();
+ String ext = key.getExtension();
+ objName = objName.substring(0, objName.length() - ext.length() - 1);
+ if (folderName != null && folderName.length() > 0){
+ objName = objName.substring(folderName.length());
+ }
+
+ objNode = new Node(objName + "-objnode");
+
+ if (!(info.getKey() instanceof ModelKey))
+ throw new IllegalArgumentException("Model assets must be loaded using a ModelKey");
+
+ InputStream in = null;
+ try {
+ in = info.openStream();
+
+ scan = new Scanner(in);
+ scan.useLocale(Locale.US);
+
+ while (readLine());
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+
+ if (matFaces.size() > 0){
+ for (Entry<String, ArrayList<Face>> entry : matFaces.entrySet()){
+ ArrayList<Face> materialFaces = entry.getValue();
+ if (materialFaces.size() > 0){
+ Geometry geom = createGeometry(materialFaces, entry.getKey());
+ objNode.attachChild(geom);
+ }
+ }
+ }else if (faces.size() > 0){
+ // generate final geometry
+ Geometry geom = createGeometry(faces, null);
+ objNode.attachChild(geom);
+ }
+
+ if (objNode.getQuantity() == 1)
+ // only 1 geometry, so no need to send node
+ return objNode.getChild(0);
+ else
+ return objNode;
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/shader/plugins/GLSLLoader.java b/engine/src/core-plugins/com/jme3/shader/plugins/GLSLLoader.java
new file mode 100644
index 0000000..f9073b7
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/shader/plugins/GLSLLoader.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+
+/**
+ * GLSL File parser that supports #import pre-processor statement
+ */
+public class GLSLLoader implements AssetLoader {
+
+ private AssetManager owner;
+ private Map<String, DependencyNode> dependCache = new HashMap<String, DependencyNode>();
+
+ private class DependencyNode {
+
+ private String shaderSource;
+ private String shaderName;
+
+ private final Set<DependencyNode> dependsOn = new HashSet<DependencyNode>();
+ private final Set<DependencyNode> dependOnMe = new HashSet<DependencyNode>();
+
+ public DependencyNode(String shaderName){
+ this.shaderName = shaderName;
+ }
+
+ public void setSource(String source){
+ this.shaderSource = source;
+ }
+
+ public void addDependency(DependencyNode node){
+ if (this.dependsOn.contains(node))
+ return; // already contains dependency
+
+// System.out.println(shaderName + " depend on "+node.shaderName);
+ this.dependsOn.add(node);
+ node.dependOnMe.add(this);
+ }
+
+ }
+
+ private class GlslDependKey extends AssetKey<InputStream> {
+ public GlslDependKey(String name){
+ super(name);
+ }
+ @Override
+ public boolean shouldCache(){
+ return false;
+ }
+ }
+
+ private DependencyNode loadNode(InputStream in, String nodeName) throws IOException{
+ DependencyNode node = new DependencyNode(nodeName);
+ if (in == null)
+ throw new IOException("Dependency "+nodeName+" cannot be found.");
+
+ StringBuilder sb = new StringBuilder();
+ BufferedReader r = new BufferedReader(new InputStreamReader(in));
+ while (r.ready()){
+ String ln = r.readLine();
+ if (ln.startsWith("#import ")){
+ ln = ln.substring(8).trim();
+ if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3){
+ // import user code
+ // remove quotes to get filename
+ ln = ln.substring(1, ln.length()-1);
+ if (ln.equals(nodeName))
+ throw new IOException("Node depends on itself.");
+
+ // check cache first
+ DependencyNode dependNode = dependCache.get(ln);
+ if (dependNode == null){
+ GlslDependKey key = new GlslDependKey(ln);
+ // make sure not to register an input stream with
+ // the cache..
+ InputStream stream = (InputStream) owner.loadAsset(key);
+ dependNode = loadNode(stream, ln);
+ }
+ node.addDependency(dependNode);
+ }
+// }else if (ln.startsWith("uniform") || ln.startsWith("varying") || ln.startsWith("attribute")){
+// // these variables are included as dependencies as well
+// DependencyNode dependNode = dependCache.get(ln);
+// if (dependNode == null){
+// // the source and name are the same for variable dependencies
+// dependNode = new DependencyNode(ln);
+// dependNode.setSource(ln);
+// dependCache.put(ln, dependNode);
+// }
+// node.addDependency(dependNode);
+ }else{
+ sb.append(ln).append('\n');
+ }
+ }
+ r.close();
+
+ node.setSource(sb.toString());
+ dependCache.put(nodeName, node);
+ return node;
+ }
+
+ private DependencyNode nextIndependentNode(List<DependencyNode> checkedNodes){
+ Collection<DependencyNode> allNodes = dependCache.values();
+ if (allNodes == null || allNodes.isEmpty())
+ return null;
+
+ for (DependencyNode node : allNodes){
+ if (node.dependsOn.isEmpty()){
+ return node;
+ }
+ }
+
+ // circular dependency found..
+ for (DependencyNode node : allNodes){
+ System.out.println(node.shaderName);
+ }
+ throw new RuntimeException("Circular dependency.");
+ }
+
+ private String resolveDependencies(DependencyNode root){
+ StringBuilder sb = new StringBuilder();
+
+ List<DependencyNode> checkedNodes = new ArrayList<DependencyNode>();
+ checkedNodes.add(root);
+ while (true){
+ DependencyNode indepnNode = nextIndependentNode(checkedNodes);
+ if (indepnNode == null)
+ break;
+
+ sb.append(indepnNode.shaderSource).append('\n');
+ dependCache.remove(indepnNode.shaderName);
+
+ // take out this dependency
+ for (Iterator<DependencyNode> iter = indepnNode.dependOnMe.iterator();
+ iter.hasNext();){
+ DependencyNode dependNode = iter.next();
+ iter.remove();
+ dependNode.dependsOn.remove(indepnNode);
+ }
+ }
+
+// System.out.println(sb.toString());
+// System.out.println("--------------------------------------------------");
+
+ return sb.toString();
+ }
+
+ /**
+ *
+ * @param owner
+ * @param in
+ * @param extension
+ * @param key
+ * @return
+ * @throws java.io.IOException
+ */
+ public Object load(AssetInfo info) throws IOException {
+ // The input stream provided is for the vertex shader,
+ // to retrieve the fragment shader, use the content manager
+ this.owner = info.getManager();
+ if (info.getKey().getExtension().equals("glsllib")){
+ // NOTE: Loopback, GLSLLIB is loaded by this loader
+ // and needs data as InputStream
+ return info.openStream();
+ }else{
+ // GLSLLoader wants result as String for
+ // fragment shader
+ DependencyNode rootNode = loadNode(info.openStream(), "[main]");
+ String code = resolveDependencies(rootNode);
+ dependCache.clear();
+ return code;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java
new file mode 100644
index 0000000..897c9eb
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture.Type;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.LittleEndien;
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * <code>DDSLoader</code> is an image loader that reads in a DirectX DDS file.
+ * Supports DXT1, DXT3, DXT5, RGB, RGBA, Grayscale, Alpha pixel formats.
+ * 2D images, mipmapped 2D images, and cubemaps.
+ *
+ * @author Gareth Jenkins-Jones
+ * @author Kirill Vainer
+ * @version $Id: DDSLoader.java,v 2.0 2008/8/15
+ */
+public class DDSLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(DDSLoader.class.getName());
+ private static final boolean forceRGBA = false;
+ private static final int DDSD_MANDATORY = 0x1007;
+ private static final int DDSD_MANDATORY_DX10 = 0x6;
+ private static final int DDSD_MIPMAPCOUNT = 0x20000;
+ private static final int DDSD_LINEARSIZE = 0x80000;
+ private static final int DDSD_DEPTH = 0x800000;
+ private static final int DDPF_ALPHAPIXELS = 0x1;
+ private static final int DDPF_FOURCC = 0x4;
+ private static final int DDPF_RGB = 0x40;
+ // used by compressonator to mark grayscale images, red channel mask is used for data and bitcount is 8
+ private static final int DDPF_GRAYSCALE = 0x20000;
+ // used by compressonator to mark alpha images, alpha channel mask is used for data and bitcount is 8
+ private static final int DDPF_ALPHA = 0x2;
+ // used by NVTextureTools to mark normal images.
+ private static final int DDPF_NORMAL = 0x80000000;
+ private static final int SWIZZLE_xGxR = 0x78477852;
+ private static final int DDSCAPS_COMPLEX = 0x8;
+ private static final int DDSCAPS_TEXTURE = 0x1000;
+ private static final int DDSCAPS_MIPMAP = 0x400000;
+ private static final int DDSCAPS2_CUBEMAP = 0x200;
+ private static final int DDSCAPS2_VOLUME = 0x200000;
+ private static final int PF_DXT1 = 0x31545844;
+ private static final int PF_DXT3 = 0x33545844;
+ private static final int PF_DXT5 = 0x35545844;
+ private static final int PF_ATI1 = 0x31495441;
+ private static final int PF_ATI2 = 0x32495441; // 0x41544932;
+ private static final int PF_DX10 = 0x30315844; // a DX10 format
+ private static final int DX10DIM_BUFFER = 0x1,
+ DX10DIM_TEXTURE1D = 0x2,
+ DX10DIM_TEXTURE2D = 0x3,
+ DX10DIM_TEXTURE3D = 0x4;
+ private static final int DX10MISC_GENERATE_MIPS = 0x1,
+ DX10MISC_TEXTURECUBE = 0x4;
+ private static final double LOG2 = Math.log(2);
+ private int width;
+ private int height;
+ private int depth;
+ private int flags;
+ private int pitchOrSize;
+ private int mipMapCount;
+ private int caps1;
+ private int caps2;
+ private boolean directx10;
+ private boolean compressed;
+ private boolean texture3D;
+ private boolean grayscaleOrAlpha;
+ private boolean normal;
+ private Format pixelFormat;
+ private int bpp;
+ private int[] sizes;
+ private int redMask, greenMask, blueMask, alphaMask;
+ private DataInput in;
+
+ public DDSLoader() {
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (!(info.getKey() instanceof TextureKey)) {
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+ }
+
+ InputStream stream = null;
+ try {
+ stream = info.openStream();
+ in = new LittleEndien(stream);
+ loadHeader();
+ if (texture3D) {
+ ((TextureKey) info.getKey()).setTextureTypeHint(Type.ThreeDimensional);
+ } else if (depth > 1) {
+ ((TextureKey) info.getKey()).setTextureTypeHint(Type.CubeMap);
+ }
+ ArrayList<ByteBuffer> data = readData(((TextureKey) info.getKey()).isFlipY());
+ return new Image(pixelFormat, width, height, depth, data, sizes);
+ } finally {
+ if (stream != null){
+ stream.close();
+ }
+ }
+ }
+
+ public Image load(InputStream stream) throws IOException {
+ in = new LittleEndien(stream);
+ loadHeader();
+ ArrayList<ByteBuffer> data = readData(false);
+ return new Image(pixelFormat, width, height, depth, data, sizes);
+ }
+
+ private void loadDX10Header() throws IOException {
+ int dxgiFormat = in.readInt();
+ if (dxgiFormat != 83) {
+ throw new IOException("Only DXGI_FORMAT_BC5_UNORM "
+ + "is supported for DirectX10 DDS! Got: " + dxgiFormat);
+ }
+ pixelFormat = Format.LATC;
+ bpp = 8;
+ compressed = true;
+
+ int resDim = in.readInt();
+ if (resDim == DX10DIM_TEXTURE3D) {
+ texture3D = true;
+ }
+ int miscFlag = in.readInt();
+ int arraySize = in.readInt();
+ if (is(miscFlag, DX10MISC_TEXTURECUBE)) {
+ // mark texture as cube
+ if (arraySize != 6) {
+ throw new IOException("Cubemaps should consist of 6 images!");
+ }
+ }
+
+ in.skipBytes(4); // skip reserved value
+ }
+
+ /**
+ * Reads the header (first 128 bytes) of a DDS File
+ */
+ private void loadHeader() throws IOException {
+ if (in.readInt() != 0x20534444 || in.readInt() != 124) {
+ throw new IOException("Not a DDS file");
+ }
+
+ flags = in.readInt();
+
+ if (!is(flags, DDSD_MANDATORY) && !is(flags, DDSD_MANDATORY_DX10)) {
+ throw new IOException("Mandatory flags missing");
+ }
+
+ height = in.readInt();
+ width = in.readInt();
+ pitchOrSize = in.readInt();
+ depth = in.readInt();
+ mipMapCount = in.readInt();
+ in.skipBytes(44);
+ pixelFormat = null;
+ directx10 = false;
+ readPixelFormat();
+ caps1 = in.readInt();
+ caps2 = in.readInt();
+ in.skipBytes(12);
+ texture3D = false;
+
+ if (!directx10) {
+ if (!is(caps1, DDSCAPS_TEXTURE)) {
+ throw new IOException("File is not a texture");
+ }
+
+ if (depth <= 0) {
+ depth = 1;
+ }
+
+ if (is(caps2, DDSCAPS2_CUBEMAP)) {
+ depth = 6; // somewhat of a hack, force loading 6 textures if a cubemap
+ }
+
+ if (is(caps2, DDSCAPS2_VOLUME)) {
+ texture3D = true;
+ }
+ }
+
+ int expectedMipmaps = 1 + (int) Math.ceil(Math.log(Math.max(height, width)) / LOG2);
+
+ if (is(caps1, DDSCAPS_MIPMAP)) {
+ if (!is(flags, DDSD_MIPMAPCOUNT)) {
+ mipMapCount = expectedMipmaps;
+ } else if (mipMapCount != expectedMipmaps) {
+ // changed to warning- images often do not have the required amount,
+ // or specify that they have mipmaps but include only the top level..
+ logger.log(Level.WARNING, "Got {0} mipmaps, expected {1}",
+ new Object[]{mipMapCount, expectedMipmaps});
+ }
+ } else {
+ mipMapCount = 1;
+ }
+
+ if (directx10) {
+ loadDX10Header();
+ }
+
+ loadSizes();
+ }
+
+ /**
+ * Reads the PixelFormat structure in a DDS file
+ */
+ private void readPixelFormat() throws IOException {
+ int pfSize = in.readInt();
+ if (pfSize != 32) {
+ throw new IOException("Pixel format size is " + pfSize + ", not 32");
+ }
+
+ int pfFlags = in.readInt();
+ normal = is(pfFlags, DDPF_NORMAL);
+
+ if (is(pfFlags, DDPF_FOURCC)) {
+ compressed = true;
+ int fourcc = in.readInt();
+ int swizzle = in.readInt();
+ in.skipBytes(16);
+
+ switch (fourcc) {
+ case PF_DXT1:
+ bpp = 4;
+ if (is(pfFlags, DDPF_ALPHAPIXELS)) {
+ pixelFormat = Image.Format.DXT1A;
+ } else {
+ pixelFormat = Image.Format.DXT1;
+ }
+ break;
+ case PF_DXT3:
+ bpp = 8;
+ pixelFormat = Image.Format.DXT3;
+ break;
+ case PF_DXT5:
+ bpp = 8;
+ pixelFormat = Image.Format.DXT5;
+ if (swizzle == SWIZZLE_xGxR) {
+ normal = true;
+ }
+ break;
+ case PF_ATI1:
+ bpp = 4;
+ pixelFormat = Image.Format.LTC;
+ break;
+ case PF_ATI2:
+ bpp = 8;
+ pixelFormat = Image.Format.LATC;
+ break;
+ case PF_DX10:
+ compressed = false;
+ directx10 = true;
+ // exit here, the rest of the structure is not valid
+ // the real format will be available in the DX10 header
+ return;
+ default:
+ throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc));
+ }
+
+ int size = ((width + 3) / 4) * ((height + 3) / 4) * bpp * 2;
+
+ if (is(flags, DDSD_LINEARSIZE)) {
+ if (pitchOrSize == 0) {
+ logger.warning("Must use linear size with fourcc");
+ pitchOrSize = size;
+ } else if (pitchOrSize != size) {
+ logger.log(Level.WARNING, "Expected size = {0}, real = {1}",
+ new Object[]{size, pitchOrSize});
+ }
+ } else {
+ pitchOrSize = size;
+ }
+ } else {
+ compressed = false;
+
+ // skip fourCC
+ in.readInt();
+
+ bpp = in.readInt();
+ redMask = in.readInt();
+ greenMask = in.readInt();
+ blueMask = in.readInt();
+ alphaMask = in.readInt();
+
+ if (is(pfFlags, DDPF_RGB)) {
+ if (is(pfFlags, DDPF_ALPHAPIXELS)) {
+ pixelFormat = Format.RGBA8;
+ } else {
+ pixelFormat = Format.RGB8;
+ }
+ } else if (is(pfFlags, DDPF_GRAYSCALE) && is(pfFlags, DDPF_ALPHAPIXELS)) {
+ switch (bpp) {
+ case 16:
+ pixelFormat = Format.Luminance8Alpha8;
+ break;
+ case 32:
+ pixelFormat = Format.Luminance16Alpha16;
+ break;
+ default:
+ throw new IOException("Unsupported GrayscaleAlpha BPP: " + bpp);
+ }
+ grayscaleOrAlpha = true;
+ } else if (is(pfFlags, DDPF_GRAYSCALE)) {
+ switch (bpp) {
+ case 8:
+ pixelFormat = Format.Luminance8;
+ break;
+ case 16:
+ pixelFormat = Format.Luminance16;
+ break;
+ default:
+ throw new IOException("Unsupported Grayscale BPP: " + bpp);
+ }
+ grayscaleOrAlpha = true;
+ } else if (is(pfFlags, DDPF_ALPHA)) {
+ switch (bpp) {
+ case 8:
+ pixelFormat = Format.Alpha8;
+ break;
+ case 16:
+ pixelFormat = Format.Alpha16;
+ break;
+ default:
+ throw new IOException("Unsupported Alpha BPP: " + bpp);
+ }
+ grayscaleOrAlpha = true;
+ } else {
+ throw new IOException("Unknown PixelFormat in DDS file");
+ }
+
+ int size = (bpp / 8 * width);
+
+ if (is(flags, DDSD_LINEARSIZE)) {
+ if (pitchOrSize == 0) {
+ logger.warning("Linear size said to contain valid value but does not");
+ pitchOrSize = size;
+ } else if (pitchOrSize != size) {
+ logger.log(Level.WARNING, "Expected size = {0}, real = {1}",
+ new Object[]{size, pitchOrSize});
+ }
+ } else {
+ pitchOrSize = size;
+ }
+ }
+ }
+
+ /**
+ * Computes the sizes of each mipmap level in bytes, and stores it in sizes_[].
+ */
+ private void loadSizes() {
+ int mipWidth = width;
+ int mipHeight = height;
+
+ sizes = new int[mipMapCount];
+ int outBpp = pixelFormat.getBitsPerPixel();
+ for (int i = 0; i < mipMapCount; i++) {
+ int size;
+ if (compressed) {
+ size = ((mipWidth + 3) / 4) * ((mipHeight + 3) / 4) * outBpp * 2;
+ } else {
+ size = mipWidth * mipHeight * outBpp / 8;
+ }
+
+ sizes[i] = ((size + 3) / 4) * 4;
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ }
+
+ /**
+ * Flips the given image data on the Y axis.
+ * @param data Data array containing image data (without mipmaps)
+ * @param scanlineSize Size of a single scanline = width * bytesPerPixel
+ * @param height Height of the image in pixels
+ * @return The new data flipped by the Y axis
+ */
+ public byte[] flipData(byte[] data, int scanlineSize, int height) {
+ byte[] newData = new byte[data.length];
+
+ for (int y = 0; y < height; y++) {
+ System.arraycopy(data, y * scanlineSize,
+ newData, (height - y - 1) * scanlineSize,
+ scanlineSize);
+ }
+
+ return newData;
+ }
+
+ /**
+ * Reads a grayscale image with mipmaps from the InputStream
+ * @param flip Flip the loaded image by Y axis
+ * @param totalSize Total size of the image in bytes including the mipmaps
+ * @return A ByteBuffer containing the grayscale image data with mips.
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readGrayscale2D(boolean flip, int totalSize) throws IOException {
+ ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
+
+ if (bpp == 8) {
+ logger.finest("Source image format: R8");
+ }
+
+ assert bpp == pixelFormat.getBitsPerPixel();
+
+ int mipWidth = width;
+ int mipHeight = height;
+
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ if (flip) {
+ data = flipData(data, mipWidth * bpp / 8, mipHeight);
+ }
+ buffer.put(data);
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Reads an uncompressed RGB or RGBA image.
+ *
+ * @param flip Flip the image on the Y axis
+ * @param totalSize Size of the image in bytes including mipmaps
+ * @return ByteBuffer containing image data with mipmaps in the format specified by pixelFormat_
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readRGB2D(boolean flip, int totalSize) throws IOException {
+ int redCount = count(redMask),
+ blueCount = count(blueMask),
+ greenCount = count(greenMask),
+ alphaCount = count(alphaMask);
+
+ if (redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF) {
+ if (alphaMask == 0xFF000000 && bpp == 32) {
+ logger.finest("Data source format: BGRA8");
+ } else if (bpp == 24) {
+ logger.finest("Data source format: BGR8");
+ }
+ }
+
+ int sourcebytesPP = bpp / 8;
+ int targetBytesPP = pixelFormat.getBitsPerPixel() / 8;
+
+ ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize);
+
+ int mipWidth = width;
+ int mipHeight = height;
+
+ int offset = 0;
+ byte[] b = new byte[sourcebytesPP];
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ for (int y = 0; y < mipHeight; y++) {
+ for (int x = 0; x < mipWidth; x++) {
+ in.readFully(b);
+
+ int i = byte2int(b);
+
+ byte red = (byte) (((i & redMask) >> redCount));
+ byte green = (byte) (((i & greenMask) >> greenCount));
+ byte blue = (byte) (((i & blueMask) >> blueCount));
+ byte alpha = (byte) (((i & alphaMask) >> alphaCount));
+
+ if (flip) {
+ dataBuffer.position(offset + ((mipHeight - y - 1) * mipWidth + x) * targetBytesPP);
+ }
+ //else
+ // dataBuffer.position(offset + (y * width + x) * targetBytesPP);
+
+ if (alphaMask == 0) {
+ dataBuffer.put(red).put(green).put(blue);
+ } else {
+ dataBuffer.put(red).put(green).put(blue).put(alpha);
+ }
+ }
+ }
+
+ offset += mipWidth * mipHeight * targetBytesPP;
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+
+ return dataBuffer;
+ }
+
+ /**
+ * Reads a DXT compressed image from the InputStream
+ *
+ * @param totalSize Total size of the image in bytes, including mipmaps
+ * @return ByteBuffer containing compressed DXT image in the format specified by pixelFormat_
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readDXT2D(boolean flip, int totalSize) throws IOException {
+ logger.finest("Source image format: DXT");
+
+ ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
+
+ int mipWidth = width;
+ int mipHeight = height;
+
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ if (flip) {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ ByteBuffer wrapped = ByteBuffer.wrap(data);
+ wrapped.rewind();
+ ByteBuffer flipped = DXTFlipper.flipDXT(wrapped, mipWidth, mipHeight, pixelFormat);
+ buffer.put(flipped);
+ } else {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ buffer.put(data);
+ }
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ buffer.rewind();
+
+ return buffer;
+ }
+
+ /**
+ * Reads a grayscale image with mipmaps from the InputStream
+ * @param flip Flip the loaded image by Y axis
+ * @param totalSize Total size of the image in bytes including the mipmaps
+ * @return A ByteBuffer containing the grayscale image data with mips.
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readGrayscale3D(boolean flip, int totalSize) throws IOException {
+ ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize * depth);
+
+ if (bpp == 8) {
+ logger.finest("Source image format: R8");
+ }
+
+ assert bpp == pixelFormat.getBitsPerPixel();
+
+
+ for (int i = 0; i < depth; i++) {
+ int mipWidth = width;
+ int mipHeight = height;
+
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ if (flip) {
+ data = flipData(data, mipWidth * bpp / 8, mipHeight);
+ }
+ buffer.put(data);
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ }
+ buffer.rewind();
+ return buffer;
+ }
+
+ /**
+ * Reads an uncompressed RGB or RGBA image.
+ *
+ * @param flip Flip the image on the Y axis
+ * @param totalSize Size of the image in bytes including mipmaps
+ * @return ByteBuffer containing image data with mipmaps in the format specified by pixelFormat_
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readRGB3D(boolean flip, int totalSize) throws IOException {
+ int redCount = count(redMask),
+ blueCount = count(blueMask),
+ greenCount = count(greenMask),
+ alphaCount = count(alphaMask);
+
+ if (redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF) {
+ if (alphaMask == 0xFF000000 && bpp == 32) {
+ logger.finest("Data source format: BGRA8");
+ } else if (bpp == 24) {
+ logger.finest("Data source format: BGR8");
+ }
+ }
+
+ int sourcebytesPP = bpp / 8;
+ int targetBytesPP = pixelFormat.getBitsPerPixel() / 8;
+
+ ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize * depth);
+
+ for (int k = 0; k < depth; k++) {
+ // ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize);
+ int mipWidth = width;
+ int mipHeight = height;
+ int offset = k * totalSize;
+ byte[] b = new byte[sourcebytesPP];
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ for (int y = 0; y < mipHeight; y++) {
+ for (int x = 0; x < mipWidth; x++) {
+ in.readFully(b);
+
+ int i = byte2int(b);
+
+ byte red = (byte) (((i & redMask) >> redCount));
+ byte green = (byte) (((i & greenMask) >> greenCount));
+ byte blue = (byte) (((i & blueMask) >> blueCount));
+ byte alpha = (byte) (((i & alphaMask) >> alphaCount));
+
+ if (flip) {
+ dataBuffer.position(offset + ((mipHeight - y - 1) * mipWidth + x) * targetBytesPP);
+ }
+ //else
+ // dataBuffer.position(offset + (y * width + x) * targetBytesPP);
+
+ if (alphaMask == 0) {
+ dataBuffer.put(red).put(green).put(blue);
+ } else {
+ dataBuffer.put(red).put(green).put(blue).put(alpha);
+ }
+ }
+ }
+
+ offset += (mipWidth * mipHeight * targetBytesPP);
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ }
+ dataBuffer.rewind();
+ return dataBuffer;
+ }
+
+ /**
+ * Reads a DXT compressed image from the InputStream
+ *
+ * @param totalSize Total size of the image in bytes, including mipmaps
+ * @return ByteBuffer containing compressed DXT image in the format specified by pixelFormat_
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readDXT3D(boolean flip, int totalSize) throws IOException {
+ logger.finest("Source image format: DXT");
+
+ ByteBuffer bufferAll = BufferUtils.createByteBuffer(totalSize * depth);
+
+ for (int i = 0; i < depth; i++) {
+ ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
+ int mipWidth = width;
+ int mipHeight = height;
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ if (flip) {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ ByteBuffer wrapped = ByteBuffer.wrap(data);
+ wrapped.rewind();
+ ByteBuffer flipped = DXTFlipper.flipDXT(wrapped, mipWidth, mipHeight, pixelFormat);
+ flipped.rewind();
+ buffer.put(flipped);
+ } else {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ buffer.put(data);
+ }
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ buffer.rewind();
+ bufferAll.put(buffer);
+ }
+
+ return bufferAll;
+ }
+
+ /**
+ * Reads the image data from the InputStream in the required format.
+ * If the file contains a cubemap image, it is loaded as 6 ByteBuffers
+ * (potentially containing mipmaps if they were specified), otherwise
+ * a single ByteBuffer is returned for a 2D image.
+ *
+ * @param flip Flip the image data or not.
+ * For cubemaps, each of the cubemap faces is flipped individually.
+ * If the image is DXT compressed, no flipping is done.
+ * @return An ArrayList containing a single ByteBuffer for a 2D image, or 6 ByteBuffers for a cubemap.
+ * The cubemap ByteBuffer order is PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ.
+ *
+ * @throws java.io.IOException If an error occured while reading from the stream.
+ */
+ public ArrayList<ByteBuffer> readData(boolean flip) throws IOException {
+ int totalSize = 0;
+
+ for (int i = 0; i < sizes.length; i++) {
+ totalSize += sizes[i];
+ }
+
+ ArrayList<ByteBuffer> allMaps = new ArrayList<ByteBuffer>();
+ if (depth > 1 && !texture3D) {
+ for (int i = 0; i < depth; i++) {
+ if (compressed) {
+ allMaps.add(readDXT2D(flip, totalSize));
+ } else if (grayscaleOrAlpha) {
+ allMaps.add(readGrayscale2D(flip, totalSize));
+ } else {
+ allMaps.add(readRGB2D(flip, totalSize));
+ }
+ }
+ } else if (texture3D) {
+ if (compressed) {
+ allMaps.add(readDXT3D(flip, totalSize));
+ } else if (grayscaleOrAlpha) {
+ allMaps.add(readGrayscale3D(flip, totalSize));
+ } else {
+ allMaps.add(readRGB3D(flip, totalSize));
+ }
+
+ } else {
+ if (compressed) {
+ allMaps.add(readDXT2D(flip, totalSize));
+ } else if (grayscaleOrAlpha) {
+ allMaps.add(readGrayscale2D(flip, totalSize));
+ } else {
+ allMaps.add(readRGB2D(flip, totalSize));
+ }
+ }
+
+ return allMaps;
+ }
+
+ /**
+ * Checks if flags contains the specified mask
+ */
+ private static boolean is(int flags, int mask) {
+ return (flags & mask) == mask;
+ }
+
+ /**
+ * Counts the amount of bits needed to shift till bitmask n is at zero
+ * @param n Bitmask to test
+ */
+ private static int count(int n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ int i = 0;
+ while ((n & 0x1) == 0) {
+ n = n >> 1;
+ i++;
+ if (i > 32) {
+ throw new RuntimeException(Integer.toHexString(n));
+ }
+ }
+
+ return i;
+ }
+
+ /**
+ * Converts a 1 to 4 sized byte array to an integer
+ */
+ private static int byte2int(byte[] b) {
+ if (b.length == 1) {
+ return b[0] & 0xFF;
+ } else if (b.length == 2) {
+ return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8);
+ } else if (b.length == 3) {
+ return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16);
+ } else if (b.length == 4) {
+ return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16) | ((b[3] & 0xFF) << 24);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Converts a int representing a FourCC into a String
+ */
+ private static String string(int value) {
+ StringBuilder buf = new StringBuilder();
+
+ buf.append((char) (value & 0xFF));
+ buf.append((char) ((value & 0xFF00) >> 8));
+ buf.append((char) ((value & 0xFF0000) >> 16));
+ buf.append((char) ((value & 0xFF00000) >> 24));
+
+ return buf.toString();
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java b/engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java
new file mode 100644
index 0000000..ff90f03
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.texture.plugins;
+
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * DXTFlipper is a utility class used to flip along Y axis DXT compressed textures.
+ *
+ * @author Kirill Vainer
+ */
+public class DXTFlipper {
+
+ private static final ByteBuffer bb = ByteBuffer.allocate(8);
+
+ static {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ private static long readCode5(long data, int x, int y){
+ long shift = (4 * y + x) * 3;
+ long mask = 0x7;
+ mask <<= shift;
+ long code = data & mask;
+ code >>= shift;
+ return code;
+ }
+
+ private static long writeCode5(long data, int x, int y, long code){
+ long shift = (4 * y + x) * 3;
+ long mask = 0x7;
+ code = (code & mask) << shift;
+ mask <<= shift;
+ mask = ~mask;
+ data &= mask;
+ data |= code; // write new code
+ return data;
+ }
+
+ private static void flipDXT5Block(byte[] block, int h){
+ if (h == 1)
+ return;
+
+ byte c0 = block[0];
+ byte c1 = block[1];
+
+ bb.clear();
+ bb.put(block, 2, 6).flip();
+ bb.clear();
+ long l = bb.getLong();
+ long n = l;
+
+ if (h == 2){
+ n = writeCode5(n, 0, 0, readCode5(l, 0, 1));
+ n = writeCode5(n, 1, 0, readCode5(l, 1, 1));
+ n = writeCode5(n, 2, 0, readCode5(l, 2, 1));
+ n = writeCode5(n, 3, 0, readCode5(l, 3, 1));
+
+ n = writeCode5(n, 0, 1, readCode5(l, 0, 0));
+ n = writeCode5(n, 1, 1, readCode5(l, 1, 0));
+ n = writeCode5(n, 2, 1, readCode5(l, 2, 0));
+ n = writeCode5(n, 3, 1, readCode5(l, 3, 0));
+ }else{
+ n = writeCode5(n, 0, 0, readCode5(l, 0, 3));
+ n = writeCode5(n, 1, 0, readCode5(l, 1, 3));
+ n = writeCode5(n, 2, 0, readCode5(l, 2, 3));
+ n = writeCode5(n, 3, 0, readCode5(l, 3, 3));
+
+ n = writeCode5(n, 0, 1, readCode5(l, 0, 2));
+ n = writeCode5(n, 1, 1, readCode5(l, 1, 2));
+ n = writeCode5(n, 2, 1, readCode5(l, 2, 2));
+ n = writeCode5(n, 3, 1, readCode5(l, 3, 2));
+
+ n = writeCode5(n, 0, 2, readCode5(l, 0, 1));
+ n = writeCode5(n, 1, 2, readCode5(l, 1, 1));
+ n = writeCode5(n, 2, 2, readCode5(l, 2, 1));
+ n = writeCode5(n, 3, 2, readCode5(l, 3, 1));
+
+ n = writeCode5(n, 0, 3, readCode5(l, 0, 0));
+ n = writeCode5(n, 1, 3, readCode5(l, 1, 0));
+ n = writeCode5(n, 2, 3, readCode5(l, 2, 0));
+ n = writeCode5(n, 3, 3, readCode5(l, 3, 0));
+ }
+
+ bb.clear();
+ bb.putLong(n);
+ bb.clear();
+ bb.get(block, 2, 6).flip();
+
+ assert c0 == block[0] && c1 == block[1];
+ }
+
+ private static void flipDXT3Block(byte[] block, int h){
+ if (h == 1)
+ return;
+
+ // first row
+ byte tmp0 = block[0];
+ byte tmp1 = block[1];
+
+ if (h == 2){
+ block[0] = block[2];
+ block[1] = block[3];
+
+ block[2] = tmp0;
+ block[3] = tmp1;
+ }else{
+ // write last row to first row
+ block[0] = block[6];
+ block[1] = block[7];
+
+ // write first row to last row
+ block[6] = tmp0;
+ block[7] = tmp1;
+
+ // 2nd row
+ tmp0 = block[2];
+ tmp1 = block[3];
+
+ // write 3rd row to 2nd
+ block[2] = block[4];
+ block[3] = block[5];
+
+ // write 2nd row to 3rd
+ block[4] = tmp0;
+ block[5] = tmp1;
+ }
+ }
+
+ /**
+ * Flips a DXT color block or a DXT3 alpha block
+ * @param block
+ * @param h
+ */
+ private static void flipDXT1orDXTA3Block(byte[] block, int h){
+ byte tmp;
+ switch (h){
+ case 1:
+ return;
+ case 2:
+ // keep header intact (the two colors)
+ // header takes 4 bytes
+
+ // flip only two top rows
+ tmp = block[4+1];
+ block[4+1] = block[4+0];
+ block[4+0] = tmp;
+ return;
+ default:
+ // keep header intact (the two colors)
+ // header takes 4 bytes
+
+ // flip first & fourth row
+ tmp = block[4+3];
+ block[4+3] = block[4+0];
+ block[4+0] = tmp;
+
+ // flip second and third row
+ tmp = block[4+2];
+ block[4+2] = block[4+1];
+ block[4+1] = tmp;
+ return;
+ }
+ }
+
+ public static ByteBuffer flipDXT(ByteBuffer img, int w, int h, Format format){
+ int blocksX = (int) FastMath.ceil((float)w / 4f);
+ int blocksY = (int) FastMath.ceil((float)h / 4f);
+
+ int type;
+ switch (format){
+ case DXT1:
+ case DXT1A:
+ type = 1;
+ break;
+ case DXT3:
+ type = 2;
+ break;
+ case DXT5:
+ type = 3;
+ break;
+ case LATC:
+ type = 4;
+ break;
+ case LTC:
+ type = 5;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ // DXT1 uses 8 bytes per block,
+ // DXT3, DXT5, LATC use 16 bytes per block
+ int bpb = type == 1 || type == 5 ? 8 : 16;
+
+ ByteBuffer retImg = BufferUtils.createByteBuffer(blocksX * blocksY * bpb);
+
+ if (h == 1){
+ retImg.put(img);
+ retImg.rewind();
+ return retImg;
+ }else if (h == 2){
+ byte[] colorBlock = new byte[8];
+ byte[] alphaBlock = type != 1 && type != 5 ? new byte[8] : null;
+ for (int x = 0; x < blocksX; x++){
+ // prepeare for block reading
+ int blockByteOffset = x * bpb;
+ img.position(blockByteOffset);
+ img.limit(blockByteOffset + bpb);
+
+ img.get(colorBlock);
+ if (type == 4 || type == 5)
+ flipDXT5Block(colorBlock, h);
+ else
+ flipDXT1orDXTA3Block(colorBlock, h);
+
+ // write block (no need to flip block indexes, only pixels
+ // inside block
+ retImg.put(colorBlock);
+
+ if (alphaBlock != null){
+ img.get(alphaBlock);
+ switch (type){
+ case 2:
+ flipDXT3Block(alphaBlock, h); break;
+ case 3:
+ case 4:
+ flipDXT5Block(alphaBlock, h);
+ break;
+ }
+ retImg.put(alphaBlock);
+ }
+ }
+ retImg.rewind();
+ return retImg;
+ }else if (h >= 4){
+ byte[] colorBlock = new byte[8];
+ byte[] alphaBlock = type != 1 && type != 5 ? new byte[8] : null;
+ for (int y = 0; y < blocksY; y++){
+ for (int x = 0; x < blocksX; x++){
+ // prepeare for block reading
+ int blockIdx = y * blocksX + x;
+ int blockByteOffset = blockIdx * bpb;
+
+ img.position(blockByteOffset);
+ img.limit(blockByteOffset + bpb);
+
+ blockIdx = (blocksY - y - 1) * blocksX + x;
+ blockByteOffset = blockIdx * bpb;
+
+ retImg.position(blockByteOffset);
+ retImg.limit(blockByteOffset + bpb);
+
+ if (alphaBlock != null){
+ img.get(alphaBlock);
+ switch (type){
+ case 2:
+ flipDXT3Block(alphaBlock, h);
+ break;
+ case 3:
+ case 4:
+ flipDXT5Block(alphaBlock, h);
+ break;
+ }
+ retImg.put(alphaBlock);
+ }
+
+ img.get(colorBlock);
+ if (type == 4 || type == 5)
+ flipDXT5Block(colorBlock, h);
+ else
+ flipDXT1orDXTA3Block(colorBlock, h);
+
+ retImg.put(colorBlock);
+ }
+ }
+ retImg.limit(retImg.capacity());
+ retImg.position(0);
+ return retImg;
+ }else{
+ return null;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java
new file mode 100644
index 0000000..4758ecf
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class HDRLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(HDRLoader.class.getName());
+
+ private boolean writeRGBE = false;
+ private ByteBuffer rleTempBuffer;
+ private ByteBuffer dataStore;
+ private final float[] tempF = new float[3];
+
+ public HDRLoader(boolean writeRGBE){
+ this.writeRGBE = writeRGBE;
+ }
+
+ public HDRLoader(){
+ }
+
+ public static void convertFloatToRGBE(byte[] rgbe, float red, float green, float blue){
+ double max = red;
+ if (green > max) max = green;
+ if (blue > max) max = blue;
+ if (max < 1.0e-32){
+ rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
+ }else{
+ double exp = Math.ceil( Math.log10(max) / Math.log10(2) );
+ double divider = Math.pow(2.0, exp);
+ rgbe[0] = (byte) ((red / divider) * 255.0);
+ rgbe[1] = (byte) ((green / divider) * 255.0);
+ rgbe[2] = (byte) ((blue / divider) * 255.0);
+ rgbe[3] = (byte) (exp + 128.0);
+ }
+ }
+
+ public static void convertRGBEtoFloat(byte[] rgbe, float[] rgbf){
+ int R = rgbe[0] & 0xFF,
+ G = rgbe[1] & 0xFF,
+ B = rgbe[2] & 0xFF,
+ E = rgbe[3] & 0xFF;
+
+ float e = (float) Math.pow(2f, E - (128 + 8) );
+ rgbf[0] = R * e;
+ rgbf[1] = G * e;
+ rgbf[2] = B * e;
+ }
+
+ public static void convertRGBEtoFloat2(byte[] rgbe, float[] rgbf){
+ int R = rgbe[0] & 0xFF,
+ G = rgbe[1] & 0xFF,
+ B = rgbe[2] & 0xFF,
+ E = rgbe[3] & 0xFF;
+
+ float e = (float) Math.pow(2f, E - 128);
+ rgbf[0] = (R / 256.0f) * e;
+ rgbf[1] = (G / 256.0f) * e;
+ rgbf[2] = (B / 256.0f) * e;
+ }
+
+ public static void convertRGBEtoFloat3(byte[] rgbe, float[] rgbf){
+ int R = rgbe[0] & 0xFF,
+ G = rgbe[1] & 0xFF,
+ B = rgbe[2] & 0xFF,
+ E = rgbe[3] & 0xFF;
+
+ float e = (float) Math.pow(2f, E - (128 + 8) );
+ rgbf[0] = R * e;
+ rgbf[1] = G * e;
+ rgbf[2] = B * e;
+ }
+
+ private short flip(int in){
+ return (short) ((in << 8 & 0xFF00) | (in >> 8));
+ }
+
+ private void writeRGBE(byte[] rgbe){
+ if (writeRGBE){
+ dataStore.put(rgbe);
+ }else{
+ convertRGBEtoFloat(rgbe, tempF);
+ dataStore.putShort(FastMath.convertFloatToHalf(tempF[0]))
+ .putShort(FastMath.convertFloatToHalf(tempF[1])).
+ putShort(FastMath.convertFloatToHalf(tempF[2]));
+ }
+ }
+
+ private String readString(InputStream is) throws IOException{
+ StringBuilder sb = new StringBuilder();
+ while (true){
+ int i = is.read();
+ if (i == 0x0a || i == -1) // new line or EOF
+ return sb.toString();
+
+ sb.append((char)i);
+ }
+ }
+
+ private boolean decodeScanlineRLE(InputStream in, int width) throws IOException{
+ // must deocde RLE data into temp buffer before converting to float
+ if (rleTempBuffer == null){
+ rleTempBuffer = BufferUtils.createByteBuffer(width * 4);
+ }else{
+ rleTempBuffer.clear();
+ if (rleTempBuffer.remaining() < width * 4)
+ rleTempBuffer = BufferUtils.createByteBuffer(width * 4);
+ }
+
+ // read each component seperately
+ for (int i = 0; i < 4; i++) {
+ // read WIDTH bytes for the channel
+ for (int j = 0; j < width;) {
+ int code = in.read();
+ if (code > 128) { // run
+ code -= 128;
+ int val = in.read();
+ while ((code--) != 0) {
+ rleTempBuffer.put( (j++) * 4 + i , (byte)val);
+ //scanline[j++][i] = val;
+ }
+ } else { // non-run
+ while ((code--) != 0) {
+ int val = in.read();
+ rleTempBuffer.put( (j++) * 4 + i, (byte)val);
+ //scanline[j++][i] = in.read();
+ }
+ }
+ }
+ }
+
+ rleTempBuffer.rewind();
+ byte[] rgbe = new byte[4];
+// float[] temp = new float[3];
+
+ // decode temp buffer into float data
+ for (int i = 0; i < width; i++){
+ rleTempBuffer.get(rgbe);
+ writeRGBE(rgbe);
+ }
+
+ return true;
+ }
+
+ private boolean decodeScanlineUncompressed(InputStream in, int width) throws IOException{
+ byte[] rgbe = new byte[4];
+
+ for (int i = 0; i < width; i+=3){
+ if (in.read(rgbe) < 1)
+ return false;
+
+ writeRGBE(rgbe);
+ }
+ return true;
+ }
+
+ private void decodeScanline(InputStream in, int width) throws IOException{
+ if (width < 8 || width > 0x7fff){
+ // too short/long for RLE compression
+ decodeScanlineUncompressed(in, width);
+ }
+
+ // check format
+ byte[] data = new byte[4];
+ in.read(data);
+ if (data[0] != 0x02 || data[1] != 0x02 || (data[2] & 0x80) != 0){
+ // not RLE data
+ decodeScanlineUncompressed(in, width-1);
+ }else{
+ // check scanline width
+ int readWidth = (data[2] & 0xFF) << 8 | (data[3] & 0xFF);
+ if (readWidth != width)
+ throw new IOException("Illegal scanline width in HDR file: "+width+" != "+readWidth);
+
+ // RLE data
+ decodeScanlineRLE(in, width);
+ }
+ }
+
+ public Image load(InputStream in, boolean flipY) throws IOException{
+ float gamma = -1f;
+ float exposure = -1f;
+ float[] colorcorr = new float[]{ -1f, -1f, -1f };
+
+ int width = -1, height = -1;
+ boolean verifiedFormat = false;
+
+ while (true){
+ String ln = readString(in);
+ ln = ln.trim();
+ if (ln.startsWith("#") || ln.equals("")){
+ if (ln.equals("#?RADIANCE") || ln.equals("#?RGBE"))
+ verifiedFormat = true;
+
+ continue; // comment or empty statement
+ } else if (ln.startsWith("+") || ln.startsWith("-")){
+ // + or - mark image resolution and start of data
+ String[] resData = ln.split("\\s");
+ if (resData.length != 4){
+ throw new IOException("Invalid resolution string in HDR file");
+ }
+
+ if (!resData[0].equals("-Y") || !resData[2].equals("+X")){
+ logger.warning("Flipping/Rotating attributes ignored!");
+ }
+
+ //if (resData[0].endsWith("X")){
+ // first width then height
+ // width = Integer.parseInt(resData[1]);
+ // height = Integer.parseInt(resData[3]);
+ //}else{
+ width = Integer.parseInt(resData[3]);
+ height = Integer.parseInt(resData[1]);
+ //}
+
+ break;
+ } else {
+ // regular command
+ int index = ln.indexOf("=");
+ if (index < 1){
+ logger.log(Level.FINE, "Ignored string: {0}", ln);
+ continue;
+ }
+
+ String var = ln.substring(0, index).trim().toLowerCase();
+ String value = ln.substring(index+1).trim().toLowerCase();
+ if (var.equals("format")){
+ if (!value.equals("32-bit_rle_rgbe") && !value.equals("32-bit_rle_xyze")){
+ throw new IOException("Unsupported format in HDR picture");
+ }
+ }else if (var.equals("exposure")){
+ exposure = Float.parseFloat(value);
+ }else if (var.equals("gamma")){
+ gamma = Float.parseFloat(value);
+ }else{
+ logger.log(Level.WARNING, "HDR Command ignored: {0}", ln);
+ }
+ }
+ }
+
+ assert width != -1 && height != -1;
+
+ if (!verifiedFormat)
+ logger.warning("Unsure if specified image is Radiance HDR");
+
+ // some HDR images can get pretty big
+ System.gc();
+
+ // each pixel times size of component times # of components
+ Format pixelFormat;
+ if (writeRGBE){
+ pixelFormat = Format.RGBA8;
+ }else{
+ pixelFormat = Format.RGB16F;
+ }
+
+ dataStore = BufferUtils.createByteBuffer(width * height * pixelFormat.getBitsPerPixel());
+
+ int bytesPerPixel = pixelFormat.getBitsPerPixel() / 8;
+ int scanLineBytes = bytesPerPixel * width;
+ for (int y = height - 1; y >= 0; y--) {
+ if (flipY)
+ dataStore.position(scanLineBytes * y);
+
+ decodeScanline(in, width);
+ }
+ in.close();
+
+ dataStore.rewind();
+ return new Image(pixelFormat, width, height, dataStore);
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ boolean flip = ((TextureKey) info.getKey()).isFlipY();
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ Image img = load(in, flip);
+ return img;
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/ImageFlipper.java b/engine/src/core-plugins/com/jme3/texture/plugins/ImageFlipper.java
new file mode 100644
index 0000000..53978e9
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/ImageFlipper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.texture.Image;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+
+/**
+ * ImageFlipper is a utility class used to flip images across the Y axis.
+ * Due to the standard of where the image origin is between OpenGL and
+ * other software, this class is required.
+ *
+ * @author Kirill Vainer
+ */
+public class ImageFlipper {
+
+ public static void flipImage(Image img, int index){
+ if (img.getFormat().isCompressed())
+ throw new UnsupportedOperationException("Flipping compressed " +
+ "images is unsupported.");
+
+ int w = img.getWidth();
+ int h = img.getHeight();
+ int halfH = h / 2;
+
+ // bytes per pixel
+ int bpp = img.getFormat().getBitsPerPixel() / 8;
+ int scanline = w * bpp;
+
+ ByteBuffer data = img.getData(index);
+ ByteBuffer temp = BufferUtils.createByteBuffer(scanline);
+
+ data.rewind();
+ for (int y = 0; y < halfH; y++){
+ int oppY = h - y - 1;
+ // read in scanline
+ data.position(y * scanline);
+ data.limit(data.position() + scanline);
+
+ temp.rewind();
+ temp.put(data);
+
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java
new file mode 100644
index 0000000..082dc83
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.logging.Logger;
+
+public class PFMLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(PFMLoader.class.getName());
+
+ private String readString(InputStream is) throws IOException{
+ StringBuilder sb = new StringBuilder();
+ while (true){
+ int i = is.read();
+ if (i == 0x0a || i == -1) // new line or EOF
+ return sb.toString();
+
+ sb.append((char)i);
+ }
+ }
+
+ private void flipScanline(byte[] scanline){
+ for (int i = 0; i < scanline.length; i+=4){
+ // flip first and fourth bytes
+ byte tmp = scanline[i+3];
+ scanline[i+3] = scanline[i+0];
+ scanline[i+0] = tmp;
+
+ // flip second and third bytes
+ tmp = scanline[i+2];
+ scanline[i+2] = scanline[i+1];
+ scanline[i+1] = tmp;
+ }
+ }
+
+ private Image load(InputStream in, boolean needYFlip) throws IOException{
+ Format format = null;
+
+ String fmtStr = readString(in);
+ if (fmtStr.equals("PF")){
+ format = Format.RGB32F;
+ }else if (fmtStr.equals("Pf")){
+ format = Format.Luminance32F;
+ }else{
+ throw new IOException("File is not PFM format");
+ }
+
+ String sizeStr = readString(in);
+ int spaceIdx = sizeStr.indexOf(" ");
+ if (spaceIdx <= 0 || spaceIdx >= sizeStr.length() - 1)
+ throw new IOException("Invalid size syntax in PFM file");
+
+ int width = Integer.parseInt(sizeStr.substring(0,spaceIdx));
+ int height = Integer.parseInt(sizeStr.substring(spaceIdx+1));
+
+ if (width <= 0 || height <= 0)
+ throw new IOException("Invalid size specified in PFM file");
+
+ String scaleStr = readString(in);
+ float scale = Float.parseFloat(scaleStr);
+ ByteOrder order = scale < 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
+ boolean needEndienFlip = order != ByteOrder.nativeOrder();
+
+ // make sure all unneccessary stuff gets deleted from heap
+ // before allocating large amount of memory
+ System.gc();
+
+ int bytesPerPixel = format.getBitsPerPixel() / 8;
+ int scanLineBytes = bytesPerPixel * width;
+
+ ByteBuffer imageData = BufferUtils.createByteBuffer(width * height * bytesPerPixel);
+ byte[] scanline = new byte[width * bytesPerPixel];
+
+ for (int y = height - 1; y >= 0; y--) {
+ if (!needYFlip)
+ imageData.position(scanLineBytes * y);
+
+ int read = 0;
+ int off = 0;
+ do {
+ read = in.read(scanline, off, scanline.length - off);
+ off += read;
+ } while (read > 0);
+
+ if (needEndienFlip){
+ flipScanline(scanline);
+ }
+
+ imageData.put(scanline);
+ }
+ imageData.rewind();
+
+ return new Image(format, width, height, imageData);
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ return load(in, ((TextureKey)info.getKey()).isFlipY());
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java
new file mode 100644
index 0000000..bbd51d6
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * <code>TextureManager</code> provides static methods for building a
+ * <code>Texture</code> object. Typically, the information supplied is the
+ * filename and the texture properties.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack - cleaned, commented, added ability to read 16bit true color and color-mapped TGAs.
+ * @author Kirill Vainer - ported to jME3
+ * @version $Id: TGALoader.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ */
+public final class TGALoader implements AssetLoader {
+
+ // 0 - no image data in file
+ public static final int TYPE_NO_IMAGE = 0;
+
+ // 1 - uncompressed, color-mapped image
+ public static final int TYPE_COLORMAPPED = 1;
+
+ // 2 - uncompressed, true-color image
+ public static final int TYPE_TRUECOLOR = 2;
+
+ // 3 - uncompressed, black and white image
+ public static final int TYPE_BLACKANDWHITE = 3;
+
+ // 9 - run-length encoded, color-mapped image
+ public static final int TYPE_COLORMAPPED_RLE = 9;
+
+ // 10 - run-length encoded, true-color image
+ public static final int TYPE_TRUECOLOR_RLE = 10;
+
+ // 11 - run-length encoded, black and white image
+ public static final int TYPE_BLACKANDWHITE_RLE = 11;
+
+ public Object load(AssetInfo info) throws IOException{
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ boolean flip = ((TextureKey)info.getKey()).isFlipY();
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ Image img = load(in, flip);
+ return img;
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+
+ /**
+ * <code>loadImage</code> is a manual image loader which is entirely
+ * independent of AWT. OUT: RGB888 or RGBA8888 Image object
+ *
+ * @return <code>Image</code> object that contains the
+ * image, either as a RGB888 or RGBA8888
+ * @param flip
+ * Flip the image vertically
+ * @param exp32
+ * Add a dummy Alpha channel to 24b RGB image.
+ * @param fis
+ * InputStream of an uncompressed 24b RGB or 32b RGBA TGA
+ * @throws java.io.IOException
+ */
+ public static Image load(InputStream in, boolean flip) throws IOException {
+ boolean flipH = false;
+
+ // open a stream to the file
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(in));
+
+ // ---------- Start Reading the TGA header ---------- //
+ // length of the image id (1 byte)
+ int idLength = dis.readUnsignedByte();
+
+ // Type of color map (if any) included with the image
+ // 0 - no color map data is included
+ // 1 - a color map is included
+ int colorMapType = dis.readUnsignedByte();
+
+ // Type of image being read:
+ int imageType = dis.readUnsignedByte();
+
+ // Read Color Map Specification (5 bytes)
+ // Index of first color map entry (if we want to use it, uncomment and remove extra read.)
+// short cMapStart = flipEndian(dis.readShort());
+ dis.readShort();
+ // number of entries in the color map
+ short cMapLength = flipEndian(dis.readShort());
+ // number of bits per color map entry
+ int cMapDepth = dis.readUnsignedByte();
+
+ // Read Image Specification (10 bytes)
+ // horizontal coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.)
+// int xOffset = flipEndian(dis.readShort());
+ dis.readShort();
+ // vertical coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.)
+// int yOffset = flipEndian(dis.readShort());
+ dis.readShort();
+ // width of image - in pixels
+ int width = flipEndian(dis.readShort());
+ // height of image - in pixels
+ int height = flipEndian(dis.readShort());
+ // bits per pixel in image.
+ int pixelDepth = dis.readUnsignedByte();
+ int imageDescriptor = dis.readUnsignedByte();
+ if ((imageDescriptor & 32) != 0) // bit 5 : if 1, flip top/bottom ordering
+ flip = !flip;
+ if ((imageDescriptor & 16) != 0) // bit 4 : if 1, flip left/right ordering
+ flipH = !flipH;
+
+ // ---------- Done Reading the TGA header ---------- //
+
+ // Skip image ID
+ if (idLength > 0)
+ in.skip(idLength);
+
+ ColorMapEntry[] cMapEntries = null;
+ if (colorMapType != 0) {
+ // read the color map.
+ int bytesInColorMap = (cMapDepth * cMapLength) >> 3;
+ int bitsPerColor = Math.min(cMapDepth/3 , 8);
+
+ byte[] cMapData = new byte[bytesInColorMap];
+ in.read(cMapData);
+
+ // Only go to the trouble of constructing the color map
+ // table if this is declared a color mapped image.
+ if (imageType == TYPE_COLORMAPPED || imageType == TYPE_COLORMAPPED_RLE) {
+ cMapEntries = new ColorMapEntry[cMapLength];
+ int alphaSize = cMapDepth - (3*bitsPerColor);
+ float scalar = 255f / (FastMath.pow(2, bitsPerColor) - 1);
+ float alphaScalar = 255f / (FastMath.pow(2, alphaSize) - 1);
+ for (int i = 0; i < cMapLength; i++) {
+ ColorMapEntry entry = new ColorMapEntry();
+ int offset = cMapDepth * i;
+ entry.red = (byte)(int)(getBitsAsByte(cMapData, offset, bitsPerColor) * scalar);
+ entry.green = (byte)(int)(getBitsAsByte(cMapData, offset+bitsPerColor, bitsPerColor) * scalar);
+ entry.blue = (byte)(int)(getBitsAsByte(cMapData, offset+(2*bitsPerColor), bitsPerColor) * scalar);
+ if (alphaSize <= 0)
+ entry.alpha = (byte)255;
+ else
+ entry.alpha = (byte)(int)(getBitsAsByte(cMapData, offset+(3*bitsPerColor), alphaSize) * alphaScalar);
+
+ cMapEntries[i] = entry;
+ }
+ }
+ }
+
+
+ // Allocate image data array
+ Format format;
+ byte[] rawData = null;
+ int dl;
+ if (pixelDepth == 32) {
+ rawData = new byte[width * height * 4];
+ dl = 4;
+ } else {
+ rawData = new byte[width * height * 3];
+ dl = 3;
+ }
+ int rawDataIndex = 0;
+
+ if (imageType == TYPE_TRUECOLOR) {
+ byte red = 0;
+ byte green = 0;
+ byte blue = 0;
+ byte alpha = 0;
+
+ // Faster than doing a 16-or-24-or-32 check on each individual pixel,
+ // just make a seperate loop for each.
+ if (pixelDepth == 16) {
+ byte[] data = new byte[2];
+ float scalar = 255f/31f;
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+ for (int j = 0; j < width; j++) {
+ data[1] = dis.readByte();
+ data[0] = dis.readByte();
+ rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 1, 5) * scalar);
+ rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 6, 5) * scalar);
+ rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 11, 5) * scalar);
+ if (dl == 4) {
+ // create an alpha channel
+ alpha = getBitsAsByte(data, 0, 1);
+ if (alpha == 1) alpha = (byte)255;
+ rawData[rawDataIndex++] = alpha;
+ }
+ }
+ }
+
+ format = dl == 4 ? Format.RGBA8 : Format.RGB8;
+ } else if (pixelDepth == 24){
+ for (int y = 0; y < height; y++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - y) * width * dl;
+ else
+ rawDataIndex = y * width * dl;
+
+ dis.readFully(rawData, rawDataIndex, width * dl);
+// for (int x = 0; x < width; x++) {
+ //read scanline
+// blue = dis.readByte();
+// green = dis.readByte();
+// red = dis.readByte();
+// rawData[rawDataIndex++] = red;
+// rawData[rawDataIndex++] = green;
+// rawData[rawDataIndex++] = blue;
+// }
+ }
+ format = Format.BGR8;
+ } else if (pixelDepth == 32){
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+
+ for (int j = 0; j < width; j++) {
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ alpha = dis.readByte();
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ rawData[rawDataIndex++] = alpha;
+ }
+ }
+ format = Format.RGBA8;
+ }else{
+ throw new IOException("Unsupported TGA true color depth: "+pixelDepth);
+ }
+ } else if( imageType == TYPE_TRUECOLOR_RLE ) {
+ byte red = 0;
+ byte green = 0;
+ byte blue = 0;
+ byte alpha = 0;
+ // Faster than doing a 16-or-24-or-32 check on each individual pixel,
+ // just make a seperate loop for each.
+ if( pixelDepth == 32 ){
+ for( int i = 0; i <= ( height - 1 ); ++i ){
+ if( !flip ){
+ rawDataIndex = ( height - 1 - i ) * width * dl;
+ }
+
+ for( int j = 0; j < width; ++j ){
+ // Get the number of pixels the next chunk covers (either packed or unpacked)
+ int count = dis.readByte();
+ if( ( count & 0x80 ) != 0 ){
+ // Its an RLE packed block - use the following 1 pixel for the next <count> pixels
+ count &= 0x07f;
+ j += count;
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ alpha = dis.readByte();
+ while( count-- >= 0 ){
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ rawData[rawDataIndex++] = alpha;
+ }
+ } else{
+ // Its not RLE packed, but the next <count> pixels are raw.
+ j += count;
+ while( count-- >= 0 ){
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ alpha = dis.readByte();
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ rawData[rawDataIndex++] = alpha;
+ }
+ }
+ }
+ }
+ format = Format.RGBA8;
+ } else if( pixelDepth == 24 ){
+ for( int i = 0; i <= ( height - 1 ); i++ ){
+ if( !flip ){
+ rawDataIndex = ( height - 1 - i ) * width * dl;
+ }
+ for( int j = 0; j < width; ++j ){
+ // Get the number of pixels the next chunk covers (either packed or unpacked)
+ int count = dis.readByte();
+ if( ( count & 0x80 ) != 0 ){
+ // Its an RLE packed block - use the following 1 pixel for the next <count> pixels
+ count &= 0x07f;
+ j += count;
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ while( count-- >= 0 ){
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ } else{
+ // Its not RLE packed, but the next <count> pixels are raw.
+ j += count;
+ while( count-- >= 0 ){
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ }
+ }
+ }
+ format = Format.RGB8;
+ } else if( pixelDepth == 16 ){
+ byte[] data = new byte[ 2 ];
+ float scalar = 255f / 31f;
+ for( int i = 0; i <= ( height - 1 ); i++ ){
+ if( !flip ){
+ rawDataIndex = ( height - 1 - i ) * width * dl;
+ }
+ for( int j = 0; j < width; j++ ){
+ // Get the number of pixels the next chunk covers (either packed or unpacked)
+ int count = dis.readByte();
+ if( ( count & 0x80 ) != 0 ){
+ // Its an RLE packed block - use the following 1 pixel for the next <count> pixels
+ count &= 0x07f;
+ j += count;
+ data[1] = dis.readByte();
+ data[0] = dis.readByte();
+ blue = (byte) (int) ( getBitsAsByte( data, 1, 5 ) * scalar );
+ green = (byte) (int) ( getBitsAsByte( data, 6, 5 ) * scalar );
+ red = (byte) (int) ( getBitsAsByte( data, 11, 5 ) * scalar );
+ while( count-- >= 0 ){
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ } else{
+ // Its not RLE packed, but the next <count> pixels are raw.
+ j += count;
+ while( count-- >= 0 ){
+ data[1] = dis.readByte();
+ data[0] = dis.readByte();
+ blue = (byte) (int) ( getBitsAsByte( data, 1, 5 ) * scalar );
+ green = (byte) (int) ( getBitsAsByte( data, 6, 5 ) * scalar );
+ red = (byte) (int) ( getBitsAsByte( data, 11, 5 ) * scalar );
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ }
+ }
+ }
+ format = Format.RGB8;
+ } else{
+ throw new IOException( "Unsupported TGA true color depth: " + pixelDepth );
+ }
+
+ } else if( imageType == TYPE_COLORMAPPED ){
+ int bytesPerIndex = pixelDepth / 8;
+
+ if (bytesPerIndex == 1) {
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+ for (int j = 0; j < width; j++) {
+ int index = dis.readUnsignedByte();
+ if (index >= cMapEntries.length || index < 0)
+ throw new IOException("TGA: Invalid color map entry referenced: "+index);
+
+ ColorMapEntry entry = cMapEntries[index];
+ rawData[rawDataIndex++] = entry.red;
+ rawData[rawDataIndex++] = entry.green;
+ rawData[rawDataIndex++] = entry.blue;
+ if (dl == 4) {
+ rawData[rawDataIndex++] = entry.alpha;
+ }
+
+ }
+ }
+ } else if (bytesPerIndex == 2) {
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+ for (int j = 0; j < width; j++) {
+ int index = flipEndian(dis.readShort());
+ if (index >= cMapEntries.length || index < 0)
+ throw new IOException("TGA: Invalid color map entry referenced: "+index);
+
+ ColorMapEntry entry = cMapEntries[index];
+ rawData[rawDataIndex++] = entry.red;
+ rawData[rawDataIndex++] = entry.green;
+ rawData[rawDataIndex++] = entry.blue;
+ if (dl == 4) {
+ rawData[rawDataIndex++] = entry.alpha;
+ }
+ }
+ }
+ } else {
+ throw new IOException("TGA: unknown colormap indexing size used: "+bytesPerIndex);
+ }
+
+ format = dl == 4 ? Format.RGBA8 : Format.RGB8;
+ } else {
+ throw new IOException("Grayscale TGA not supported");
+ }
+
+
+ in.close();
+ // Get a pointer to the image memory
+ ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length);
+ scratch.clear();
+ scratch.put(rawData);
+ scratch.rewind();
+ // Create the Image object
+ Image textureImage = new Image();
+ textureImage.setFormat(format);
+ textureImage.setWidth(width);
+ textureImage.setHeight(height);
+ textureImage.setData(scratch);
+ return textureImage;
+ }
+
+ private static byte getBitsAsByte(byte[] data, int offset, int length) {
+ int offsetBytes = offset / 8;
+ int indexBits = offset % 8;
+ int rVal = 0;
+
+ // start at data[offsetBytes]... spill into next byte as needed.
+ for (int i = length; --i >=0;) {
+ byte b = data[offsetBytes];
+ int test = indexBits == 7 ? 1 : 2 << (6-indexBits);
+ if ((b & test) != 0) {
+ if (i == 0)
+ rVal++;
+ else
+ rVal += (2 << i-1);
+ }
+ indexBits++;
+ if (indexBits == 8) {
+ indexBits = 0;
+ offsetBytes++;
+ }
+ }
+
+ return (byte)rVal;
+ }
+
+ /**
+ * <code>flipEndian</code> is used to flip the endian bit of the header
+ * file.
+ *
+ * @param signedShort
+ * the bit to flip.
+ * @return the flipped bit.
+ */
+ private static short flipEndian(short signedShort) {
+ int input = signedShort & 0xFFFF;
+ return (short) (input << 8 | (input & 0xFF00) >>> 8);
+ }
+
+ static class ColorMapEntry {
+ byte red, green, blue, alpha;
+
+ @Override
+ public String toString() {
+ return "entry: "+red+","+green+","+blue+","+alpha;
+ }
+ }
+}
diff --git a/engine/src/core/checkers/quals/DefaultLocation.java b/engine/src/core/checkers/quals/DefaultLocation.java
new file mode 100644
index 0000000..90dc78a
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultLocation.java
@@ -0,0 +1,25 @@
+package checkers.quals;
+
+/**
+ * Specifies the locations to which a {@link DefaultQualifier} annotation applies.
+ *
+ * @see DefaultQualifier
+ */
+public enum DefaultLocation {
+
+ /** Apply default annotations to all unannotated types. */
+ ALL,
+
+ /** Apply default annotations to all unannotated types except the raw types
+ * of locals. */
+ ALL_EXCEPT_LOCALS,
+
+ /** Apply default annotations to unannotated upper bounds: both
+ * explicit ones in <tt>extends</tt> clauses, and implicit upper bounds
+ * when no explicit <tt>extends</tt> or <tt>super</tt> clause is
+ * present. */
+ // Especially useful for parameterized classes that provide a lot of
+ // static methods with the same generic parameters as the class.
+ UPPER_BOUNDS;
+
+}
diff --git a/engine/src/core/checkers/quals/DefaultQualifier.java b/engine/src/core/checkers/quals/DefaultQualifier.java
new file mode 100644
index 0000000..5caf5da
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultQualifier.java
@@ -0,0 +1,44 @@
+package checkers.quals;
+
+import java.lang.annotation.Documented;
+import static java.lang.annotation.ElementType.*;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Applied to a declaration of a package, type, method, variable, etc.,
+ * specifies that the given annotation should be the default. The default is
+ * applied to all types within the declaration for which no other
+ * annotation is explicitly written.
+ * If multiple DefaultQualifier annotations are in scope, the innermost one
+ * takes precedence.
+ * DefaultQualifier takes precedence over {@link DefaultQualifierInHierarchy}.
+ * <p>
+ *
+ * If you wish to write multiple @DefaultQualifier annotations (for
+ * unrelated type systems, or with different {@code locations} fields) at
+ * the same location, use {@link DefaultQualifiers}.
+ *
+ * @see DefaultLocation
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({CONSTRUCTOR, METHOD, FIELD, LOCAL_VARIABLE, PARAMETER, TYPE})
+public @interface DefaultQualifier {
+
+ /**
+ * The name of the default annotation. It may be a short name like
+ * "NonNull", if an appropriate import statement exists. Otherwise, it
+ * should be fully-qualified, like "checkers.nullness.quals.NonNull".
+ * <p>
+ *
+ * To prevent affecting other type systems, always specify an annotation
+ * in your own type hierarchy. (For example, do not set
+ * "checkers.quals.Unqualified" as the default.)
+ */
+ String value();
+
+ /** @return the locations to which the annotation should be applied */
+ DefaultLocation[] locations() default {DefaultLocation.ALL};
+}
diff --git a/engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java b/engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java
new file mode 100644
index 0000000..949846d
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java
@@ -0,0 +1,29 @@
+package checkers.quals;
+
+import java.lang.annotation.Documented;
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the annotated qualifier is the default qualifier in the
+ * qualifier hierarchy: it applies if the programmer writes no explicit
+ * qualifier.
+ * <p>
+ *
+ * The {@link DefaultQualifier} annotation, which targets Java code elements,
+ * takes precedence over {@code DefaultQualifierInHierarchy}.
+ * <p>
+ *
+ * Each type qualifier hierarchy may have at most one qualifier marked as
+ * {@code DefaultQualifierInHierarchy}.
+ *
+ * @see checkers.quals.DefaultQualifier
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ANNOTATION_TYPE)
+public @interface DefaultQualifierInHierarchy {
+
+}
diff --git a/engine/src/core/checkers/quals/DefaultQualifiers.java b/engine/src/core/checkers/quals/DefaultQualifiers.java
new file mode 100644
index 0000000..e483e63
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultQualifiers.java
@@ -0,0 +1,35 @@
+package checkers.quals;
+
+import java.lang.annotation.Documented;
+import static java.lang.annotation.ElementType.*;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies the annotations to be included in a type without having to provide
+ * them explicitly.
+ *
+ * This annotation permits specifying multiple default qualifiers for more
+ * than one type system. It is necessary because Java forbids multiple
+ * annotations of the same name at a single location.
+ *
+ * Example:
+ * <!-- &nbsp; is a hack that prevents @ from being the first charater on the line, which confuses Javadoc -->
+ * <code><pre>
+ * &nbsp; @DefaultQualifiers({
+ * &nbsp; @DefaultQualifier("NonNull"),
+ * &nbsp; @DefaultQualifier(value = "Interned", locations = ALL_EXCEPT_LOCALS),
+ * &nbsp; @DefaultQualifier("Tainted")
+ * &nbsp; })
+ * </pre></code>
+ *
+ * @see DefaultQualifier
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({CONSTRUCTOR, METHOD, FIELD, LOCAL_VARIABLE, PARAMETER, TYPE})
+public @interface DefaultQualifiers {
+ /** The default qualifier settings */
+ DefaultQualifier[] value() default { };
+}
diff --git a/engine/src/core/checkers/quals/Dependent.java b/engine/src/core/checkers/quals/Dependent.java
new file mode 100644
index 0000000..88d3b39
--- /dev/null
+++ b/engine/src/core/checkers/quals/Dependent.java
@@ -0,0 +1,38 @@
+package checkers.quals;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Refines the qualified type of the annotated field or variable based on the
+ * qualified type of the receiver. The annotation declares a relationship
+ * between multiple type qualifier hierarchies.
+ *
+ * <p><b>Example:</b>
+ * Consider a field, {@code lock}, that is only initialized if the
+ * enclosing object (the receiver), is marked as {@code ThreadSafe}.
+ * Such a field can be declared as:
+ *
+ * <pre><code>
+ * private @Nullable @Dependent(result=NonNull.class, when=ThreadSafe.class)
+ * Lock lock;
+ * </code></pre>
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+//@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+public @interface Dependent {
+
+ /**
+ * The class of the refined qualifier to be applied.
+ */
+ Class<? extends Annotation> result();
+
+ /**
+ * The qualifier class of the receiver that causes the {@code result}
+ * qualifier to be applied.
+ */
+ Class<? extends Annotation> when();
+}
diff --git a/engine/src/core/checkers/quals/SubtypeOf.java b/engine/src/core/checkers/quals/SubtypeOf.java
new file mode 100644
index 0000000..ffc8be0
--- /dev/null
+++ b/engine/src/core/checkers/quals/SubtypeOf.java
@@ -0,0 +1,59 @@
+package checkers.quals;
+
+import java.lang.annotation.*;
+
+/**
+ * A meta-annotation to specify all the qualifiers that the given qualifier
+ * is a subtype of. This provides a declarative way to specify the type
+ * qualifier hierarchy. (Alternatively, the hierarchy can be defined
+ * procedurally by subclassing {@link checkers.types.QualifierHierarchy} or
+ * {@link checkers.types.TypeHierarchy}.)
+ *
+ * <p>
+ * Example:
+ * <pre> @SubtypeOf( { Nullable.class } )
+ * public @interface NonNull { }
+ * </pre>
+ *
+ * <p>
+ *
+ * If a qualified type is a subtype of the same type without any qualifier,
+ * then use <code>Unqualified.class</code> in place of a type qualifier
+ * class. For example, to express that <code>@Encrypted <em>C</em></code>
+ * is a subtype of <code><em>C</em></code> (for every class
+ * <code><em>C</em></code>), and likewise for <code>@Interned</code>, write:
+ *
+ * <pre> @SubtypeOf(Unqualified.class)
+ * public @interface Encrypted { }
+ *
+ * &#64;SubtypeOf(Unqualified.class)
+ * public @interface Interned { }
+ * </pre>
+ *
+ * <p>
+ *
+ * For the root type qualifier in the qualifier hierarchy (i.e., the
+ * qualifier that is a supertype of all other qualifiers in the given
+ * hierarchy), use an empty set of values:
+ *
+ * <pre> @SubtypeOf( { } )
+ * public @interface Nullable { }
+ *
+ * &#64;SubtypeOf( {} )
+ * public @interface ReadOnly { }
+ * </pre>
+ *
+ * <p>
+ * Together, all the @SubtypeOf meta-annotations fully describe the type
+ * qualifier hierarchy.
+ * No @SubtypeOf meta-annotation is needed on (or can be written on) the
+ * Unqualified pseudo-qualifier, whose position in the hierarchy is
+ * inferred from the meta-annotations on the explicit qualifiers.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface SubtypeOf {
+ /** An array of the supertype qualifiers of the annotated qualifier **/
+ Class<? extends Annotation>[] value();
+}
diff --git a/engine/src/core/checkers/quals/TypeQualifier.java b/engine/src/core/checkers/quals/TypeQualifier.java
new file mode 100644
index 0000000..25c4f7b
--- /dev/null
+++ b/engine/src/core/checkers/quals/TypeQualifier.java
@@ -0,0 +1,16 @@
+package checkers.quals;
+
+import java.lang.annotation.*;
+
+/**
+ * A meta-annotation indicating that the annotated annotation is a type
+ * qualifier.
+ *
+ * Examples of such qualifiers: {@code @ReadOnly}, {@code @NonNull}
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface TypeQualifier {
+
+}
diff --git a/engine/src/core/checkers/quals/Unqualified.java b/engine/src/core/checkers/quals/Unqualified.java
new file mode 100644
index 0000000..a210b70
--- /dev/null
+++ b/engine/src/core/checkers/quals/Unqualified.java
@@ -0,0 +1,16 @@
+package checkers.quals;
+
+import java.lang.annotation.Target;
+
+/**
+ * A special annotation intended solely for representing an unqualified type in
+ * the qualifier hierarchy, as an argument to {@link SubtypeOf#value()},
+ * in the type qualifiers declarations.
+ *
+ * <p>
+ * Programmers cannot write this in source code.
+ */
+@TypeQualifier
+@SubtypeOf({})
+@Target({}) // empty target prevents programmers from writing this in a program
+public @interface Unqualified { }
diff --git a/engine/src/core/checkers/quals/Unused.java b/engine/src/core/checkers/quals/Unused.java
new file mode 100644
index 0000000..726df8b
--- /dev/null
+++ b/engine/src/core/checkers/quals/Unused.java
@@ -0,0 +1,43 @@
+package checkers.quals;
+
+import static java.lang.annotation.ElementType.FIELD;
+import java.lang.annotation.*;
+
+/**
+ * Declares that the field may not be accessed if the receiver is of the
+ * specified qualifier type (or any supertype).
+ *
+ * This property is verified by the checker that type-checks the {@code
+ * when} element value qualifier.
+ *
+ * <p><b>Example</b>
+ * Consider a class, {@code Table}, with a locking field, {@code lock}. The
+ * lock is used when a {@code Table} instance is shared across threads. When
+ * running in a local thread, the {@code lock} field ought not to be used.
+ *
+ * You can declare this behavior in the following way:
+ *
+ * <pre><code>
+ * class Table {
+ * private @Unused(when=LocalToThread.class) final Lock lock;
+ * ...
+ * }
+ * </code></pre>
+ *
+ * The checker for {@code @LocalToThread} would issue an error for the following code:
+ *
+ * <pre> @LocalToThread Table table = ...;
+ * ... table.lock ...;
+ * </pre>
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({FIELD})
+public @interface Unused {
+ /**
+ * The field that is annotated with @Unused may not be accessed via a
+ * receiver that is annotated with the "when" annotation.
+ */
+ Class<? extends Annotation> when();
+}
diff --git a/engine/src/core/checkers/quals/package-info.java b/engine/src/core/checkers/quals/package-info.java
new file mode 100644
index 0000000..5ae80a7
--- /dev/null
+++ b/engine/src/core/checkers/quals/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Contains the basic annotations to be used by all type systems
+ * and meta-annotations to qualify annotations (qualifiers).
+ *
+ * They may serve as documentation for the type qualifiers, and aid the
+ * Checker Framework to infer the relations between the type qualifiers.
+ *
+ * @checker.framework.manual #writing-a-checker Writing a checker
+ */
+package checkers.quals;
diff --git a/engine/src/core/com/jme3/animation/AnimChannel.java b/engine/src/core/com/jme3/animation/AnimChannel.java
new file mode 100644
index 0000000..a2fae7c
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimChannel.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.math.FastMath;
+import com.jme3.util.TempVars;
+import java.util.BitSet;
+
+/**
+ * <code>AnimChannel</code> provides controls, such as play, pause,
+ * fast forward, etc, for an animation. The animation
+ * channel may influence the entire model or specific bones of the model's
+ * skeleton. A single model may have multiple animation channels influencing
+ * various parts of its body. For example, a character model may have an
+ * animation channel for its feet, and another for its torso, and
+ * the animations for each channel are controlled independently.
+ *
+ * @author Kirill Vainer
+ */
+public final class AnimChannel {
+
+ private static final float DEFAULT_BLEND_TIME = 0.15f;
+
+ private AnimControl control;
+
+ private BitSet affectedBones;
+
+ private Animation animation;
+ private Animation blendFrom;
+ private float time;
+ private float speed;
+ private float timeBlendFrom;
+ private float speedBlendFrom;
+
+ private LoopMode loopMode, loopModeBlendFrom;
+
+ private float blendAmount = 1f;
+ private float blendRate = 0;
+
+ private static float clampWrapTime(float t, float max, LoopMode loopMode){
+ if (max == Float.POSITIVE_INFINITY)
+ return t;
+
+ if (t < 0f){
+ //float tMod = -(-t % max);
+ switch (loopMode){
+ case DontLoop:
+ return 0;
+ case Cycle:
+ return t;
+ case Loop:
+ return max - t;
+ }
+ }else if (t > max){
+ switch (loopMode){
+ case DontLoop:
+ return max;
+ case Cycle:
+ return /*-max;*/-(2f * max - t);
+ case Loop:
+ return t - max;
+ }
+ }
+
+ return t;
+ }
+
+ AnimChannel(AnimControl control){
+ this.control = control;
+ }
+
+ /**
+ * Returns the parent control of this AnimChannel.
+ *
+ * @return the parent control of this AnimChannel.
+ * @see AnimControl
+ */
+ public AnimControl getControl() {
+ return control;
+ }
+
+ /**
+ * @return The name of the currently playing animation, or null if
+ * none is assigned.
+ *
+ * @see AnimChannel#setAnim(java.lang.String)
+ */
+ public String getAnimationName() {
+ return animation != null ? animation.getName() : null;
+ }
+
+ /**
+ * @return The loop mode currently set for the animation. The loop mode
+ * determines what will happen to the animation once it finishes
+ * playing.
+ *
+ * For more information, see the LoopMode enum class.
+ * @see LoopMode
+ * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode)
+ */
+ public LoopMode getLoopMode() {
+ return loopMode;
+ }
+
+ /**
+ * @param loopMode Set the loop mode for the channel. The loop mode
+ * determines what will happen to the animation once it finishes
+ * playing.
+ *
+ * For more information, see the LoopMode enum class.
+ * @see LoopMode
+ */
+ public void setLoopMode(LoopMode loopMode) {
+ this.loopMode = loopMode;
+ }
+
+ /**
+ * @return The speed that is assigned to the animation channel. The speed
+ * is a scale value starting from 0.0, at 1.0 the animation will play
+ * at its default speed.
+ *
+ * @see AnimChannel#setSpeed(float)
+ */
+ public float getSpeed() {
+ return speed;
+ }
+
+ /**
+ * @param speed Set the speed of the animation channel. The speed
+ * is a scale value starting from 0.0, at 1.0 the animation will play
+ * at its default speed.
+ */
+ public void setSpeed(float speed) {
+ this.speed = speed;
+ }
+
+ /**
+ * @return The time of the currently playing animation. The time
+ * starts at 0 and continues on until getAnimMaxTime().
+ *
+ * @see AnimChannel#setTime(float)
+ */
+ public float getTime() {
+ return time;
+ }
+
+ /**
+ * @param time Set the time of the currently playing animation, the time
+ * is clamped from 0 to {@link #getAnimMaxTime()}.
+ */
+ public void setTime(float time) {
+ this.time = FastMath.clamp(time, 0, getAnimMaxTime());
+ }
+
+ /**
+ * @return The length of the currently playing animation, or zero
+ * if no animation is playing.
+ *
+ * @see AnimChannel#getTime()
+ */
+ public float getAnimMaxTime(){
+ return animation != null ? animation.getLength() : 0f;
+ }
+
+ /**
+ * Set the current animation that is played by this AnimChannel.
+ * <p>
+ * This resets the time to zero, and optionally blends the animation
+ * over <code>blendTime</code> seconds with the currently playing animation.
+ * Notice that this method will reset the control's speed to 1.0.
+ *
+ * @param name The name of the animation to play
+ * @param blendTime The blend time over which to blend the new animation
+ * with the old one. If zero, then no blending will occur and the new
+ * animation will be applied instantly.
+ */
+ public void setAnim(String name, float blendTime){
+ if (name == null)
+ throw new NullPointerException();
+
+ if (blendTime < 0f)
+ throw new IllegalArgumentException("blendTime cannot be less than zero");
+
+ Animation anim = control.animationMap.get(name);
+ if (anim == null)
+ throw new IllegalArgumentException("Cannot find animation named: '"+name+"'");
+
+ control.notifyAnimChange(this, name);
+
+ if (animation != null && blendTime > 0f){
+ // activate blending
+ blendFrom = animation;
+ timeBlendFrom = time;
+ speedBlendFrom = speed;
+ loopModeBlendFrom = loopMode;
+ blendAmount = 0f;
+ blendRate = 1f / blendTime;
+ }
+
+ animation = anim;
+ time = 0;
+ speed = 1f;
+ loopMode = LoopMode.Loop;
+ }
+
+ /**
+ * Set the current animation that is played by this AnimChannel.
+ * <p>
+ * See {@link #setAnim(java.lang.String, float)}.
+ * The blendTime argument by default is 150 milliseconds.
+ *
+ * @param name The name of the animation to play
+ */
+ public void setAnim(String name){
+ setAnim(name, DEFAULT_BLEND_TIME);
+ }
+
+ /**
+ * Add all the bones of the model's skeleton to be
+ * influenced by this animation channel.
+ */
+ public void addAllBones() {
+ affectedBones = null;
+ }
+
+ /**
+ * Add a single bone to be influenced by this animation channel.
+ */
+ public void addBone(String name) {
+ addBone(control.getSkeleton().getBone(name));
+ }
+
+ /**
+ * Add a single bone to be influenced by this animation channel.
+ */
+ public void addBone(Bone bone) {
+ int boneIndex = control.getSkeleton().getBoneIndex(bone);
+ if(affectedBones == null) {
+ affectedBones = new BitSet(control.getSkeleton().getBoneCount());
+ }
+ affectedBones.set(boneIndex);
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel starting from the
+ * given bone name and going toward the root bone.
+ */
+ public void addToRootBone(String name) {
+ addToRootBone(control.getSkeleton().getBone(name));
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel starting from the
+ * given bone and going toward the root bone.
+ */
+ public void addToRootBone(Bone bone) {
+ addBone(bone);
+ while (bone.getParent() != null) {
+ bone = bone.getParent();
+ addBone(bone);
+ }
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel, starting
+ * from the given named bone and going toward its children.
+ */
+ public void addFromRootBone(String name) {
+ addFromRootBone(control.getSkeleton().getBone(name));
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel, starting
+ * from the given bone and going toward its children.
+ */
+ public void addFromRootBone(Bone bone) {
+ addBone(bone);
+ if (bone.getChildren() == null)
+ return;
+ for (Bone childBone : bone.getChildren()) {
+ addBone(childBone);
+ addFromRootBone(childBone);
+ }
+ }
+
+ BitSet getAffectedBones(){
+ return affectedBones;
+ }
+
+ void update(float tpf, TempVars vars) {
+ if (animation == null)
+ return;
+
+ if (blendFrom != null){
+ blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
+ //blendFrom.setTime(timeBlendFrom, control.skeleton, 1f - blendAmount, affectedBones);
+ timeBlendFrom += tpf * speedBlendFrom;
+ timeBlendFrom = clampWrapTime(timeBlendFrom,
+ blendFrom.getLength(),
+ loopModeBlendFrom);
+ if (timeBlendFrom < 0){
+ timeBlendFrom = -timeBlendFrom;
+ speedBlendFrom = -speedBlendFrom;
+ }
+
+ blendAmount += tpf * blendRate;
+ if (blendAmount > 1f){
+ blendAmount = 1f;
+ blendFrom = null;
+ }
+ }
+
+ animation.setTime(time, blendAmount, control, this, vars);
+ //animation.setTime(time, control.skeleton, blendAmount, affectedBones);
+ time += tpf * speed;
+
+ if (animation.getLength() > 0){
+ if (time >= animation.getLength()) {
+ control.notifyAnimCycleDone(this, animation.getName());
+ } else if (time < 0) {
+ control.notifyAnimCycleDone(this, animation.getName());
+ }
+ }
+
+ time = clampWrapTime(time, animation.getLength(), loopMode);
+ if (time < 0){
+ time = -time;
+ speed = -speed;
+ }
+
+
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/AnimControl.java b/engine/src/core/com/jme3/animation/AnimControl.java
new file mode 100644
index 0000000..590801c
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimControl.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * <code>AnimControl</code> is a Spatial control that allows manipulation
+ * of skeletal animation.
+ *
+ * The control currently supports:
+ * 1) Animation blending/transitions
+ * 2) Multiple animation channels
+ * 3) Multiple skins
+ * 4) Animation event listeners
+ * 5) Animated model cloning
+ * 6) Animated model binary import/export
+ *
+ * Planned:
+ * 1) Hardware skinning
+ * 2) Morph/Pose animation
+ * 3) Attachments
+ * 4) Add/remove skins
+ *
+ * @author Kirill Vainer
+ */
+public final class AnimControl extends AbstractControl implements Cloneable {
+
+ /**
+ * Skeleton object must contain corresponding data for the targets' weight buffers.
+ */
+ Skeleton skeleton;
+ /** only used for backward compatibility */
+ @Deprecated
+ private SkeletonControl skeletonControl;
+ /**
+ * List of animations
+ */
+ HashMap<String, Animation> animationMap;
+ /**
+ * Animation channels
+ */
+ private transient ArrayList<AnimChannel> channels = new ArrayList<AnimChannel>();
+ /**
+ * Animation event listeners
+ */
+ private transient ArrayList<AnimEventListener> listeners = new ArrayList<AnimEventListener>();
+
+ /**
+ * Creates a new animation control for the given skeleton.
+ * The method {@link AnimControl#setAnimations(java.util.HashMap) }
+ * must be called after initialization in order for this class to be useful.
+ *
+ * @param skeleton The skeleton to animate
+ */
+ public AnimControl(Skeleton skeleton) {
+ this.skeleton = skeleton;
+ reset();
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public AnimControl() {
+ }
+
+ /**
+ * Internal use only.
+ */
+ public Control cloneForSpatial(Spatial spatial) {
+ try {
+ AnimControl clone = (AnimControl) super.clone();
+ clone.spatial = spatial;
+ clone.channels = new ArrayList<AnimChannel>();
+ clone.listeners = new ArrayList<AnimEventListener>();
+
+ if (skeleton != null) {
+ clone.skeleton = new Skeleton(skeleton);
+ }
+
+ // animationMap is reference-copied, animation data should be shared
+ // to reduce memory usage.
+
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * @param animations Set the animations that this <code>AnimControl</code>
+ * will be capable of playing. The animations should be compatible
+ * with the skeleton given in the constructor.
+ */
+ public void setAnimations(HashMap<String, Animation> animations) {
+ animationMap = animations;
+ }
+
+ /**
+ * Retrieve an animation from the list of animations.
+ * @param name The name of the animation to retrieve.
+ * @return The animation corresponding to the given name, or null, if no
+ * such named animation exists.
+ */
+ public Animation getAnim(String name) {
+ if (animationMap == null) {
+ animationMap = new HashMap<String, Animation>();
+ }
+ return animationMap.get(name);
+ }
+
+ /**
+ * Adds an animation to be available for playing to this
+ * <code>AnimControl</code>.
+ * @param anim The animation to add.
+ */
+ public void addAnim(Animation anim) {
+ if (animationMap == null) {
+ animationMap = new HashMap<String, Animation>();
+ }
+ animationMap.put(anim.getName(), anim);
+ }
+
+ /**
+ * Remove an animation so that it is no longer available for playing.
+ * @param anim The animation to remove.
+ */
+ public void removeAnim(Animation anim) {
+ if (!animationMap.containsKey(anim.getName())) {
+ throw new IllegalArgumentException("Given animation does not exist "
+ + "in this AnimControl");
+ }
+
+ animationMap.remove(anim.getName());
+ }
+
+ /**
+ * Create a new animation channel, by default assigned to all bones
+ * in the skeleton.
+ *
+ * @return A new animation channel for this <code>AnimControl</code>.
+ */
+ public AnimChannel createChannel() {
+ AnimChannel channel = new AnimChannel(this);
+ channels.add(channel);
+ return channel;
+ }
+
+ /**
+ * Return the animation channel at the given index.
+ * @param index The index, starting at 0, to retrieve the <code>AnimChannel</code>.
+ * @return The animation channel at the given index, or throws an exception
+ * if the index is out of bounds.
+ *
+ * @throws IndexOutOfBoundsException If no channel exists at the given index.
+ */
+ public AnimChannel getChannel(int index) {
+ return channels.get(index);
+ }
+
+ /**
+ * @return The number of channels that are controlled by this
+ * <code>AnimControl</code>.
+ *
+ * @see AnimControl#createChannel()
+ */
+ public int getNumChannels() {
+ return channels.size();
+ }
+
+ /**
+ * Clears all the channels that were created.
+ *
+ * @see AnimControl#createChannel()
+ */
+ public void clearChannels() {
+ channels.clear();
+ }
+
+ /**
+ * @return The skeleton of this <code>AnimControl</code>.
+ */
+ public Skeleton getSkeleton() {
+ return skeleton;
+ }
+
+ /**
+ * Adds a new listener to receive animation related events.
+ * @param listener The listener to add.
+ */
+ public void addListener(AnimEventListener listener) {
+ if (listeners.contains(listener)) {
+ throw new IllegalArgumentException("The given listener is already "
+ + "registed at this AnimControl");
+ }
+
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from listening to events.
+ * @param listener
+ * @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
+ */
+ public void removeListener(AnimEventListener listener) {
+ if (!listeners.remove(listener)) {
+ throw new IllegalArgumentException("The given listener is not "
+ + "registed at this AnimControl");
+ }
+ }
+
+ /**
+ * Clears all the listeners added to this <code>AnimControl</code>
+ *
+ * @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
+ */
+ public void clearListeners() {
+ listeners.clear();
+ }
+
+ void notifyAnimChange(AnimChannel channel, String name) {
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onAnimChange(this, channel, name);
+ }
+ }
+
+ void notifyAnimCycleDone(AnimChannel channel, String name) {
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onAnimCycleDone(this, channel, name);
+ }
+ }
+
+ /**
+ * Internal use only.
+ */
+ @Override
+ public void setSpatial(Spatial spatial) {
+ if (spatial == null && skeletonControl != null) {
+ this.spatial.removeControl(skeletonControl);
+ }
+
+ super.setSpatial(spatial);
+
+ //Backward compatibility.
+ if (spatial != null && skeletonControl != null) {
+ spatial.addControl(skeletonControl);
+ }
+ }
+
+ final void reset() {
+ if (skeleton != null) {
+ skeleton.resetAndUpdate();
+ }
+ }
+
+ /**
+ * @return The names of all animations that this <code>AnimControl</code>
+ * can play.
+ */
+ public Collection<String> getAnimationNames() {
+ return animationMap.keySet();
+ }
+
+ /**
+ * Returns the length of the given named animation.
+ * @param name The name of the animation
+ * @return The length of time, in seconds, of the named animation.
+ */
+ public float getAnimationLength(String name) {
+ Animation a = animationMap.get(name);
+ if (a == null) {
+ throw new IllegalArgumentException("The animation " + name
+ + " does not exist in this AnimControl");
+ }
+
+ return a.getLength();
+ }
+
+ /**
+ * Internal use only.
+ */
+ @Override
+ protected void controlUpdate(float tpf) {
+ if (skeleton != null) {
+ skeleton.reset(); // reset skeleton to bind pose
+ }
+
+ TempVars vars = TempVars.get();
+ for (int i = 0; i < channels.size(); i++) {
+ channels.get(i).update(tpf, vars);
+ }
+ vars.release();
+
+ if (skeleton != null) {
+ skeleton.updateWorldVectors();
+ }
+ }
+
+ /**
+ * Internal use only.
+ */
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(skeleton, "skeleton", null);
+ oc.writeStringSavableMap(animationMap, "animations", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule in = im.getCapsule(this);
+ skeleton = (Skeleton) in.readSavable("skeleton", null);
+ animationMap = (HashMap<String, Animation>) in.readStringSavableMap("animations", null);
+
+ if (im.getFormatVersion() == 0) {
+ // Changed for backward compatibility with j3o files generated
+ // before the AnimControl/SkeletonControl split.
+
+ // If we find a target mesh array the AnimControl creates the
+ // SkeletonControl for old files and add it to the spatial.
+ // When backward compatibility won't be needed anymore this can deleted
+ Savable[] sav = in.readSavableArray("targets", null);
+ if (sav != null) {
+ Mesh[] targets = new Mesh[sav.length];
+ System.arraycopy(sav, 0, targets, 0, sav.length);
+ skeletonControl = new SkeletonControl(targets, skeleton);
+ spatial.addControl(skeletonControl);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/AnimEventListener.java b/engine/src/core/com/jme3/animation/AnimEventListener.java
new file mode 100644
index 0000000..22a0b98
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimEventListener.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+/**
+ * <code>AnimEventListener</code> allows user code to receive various
+ * events regarding an AnimControl. For example, when an animation cycle is done.
+ *
+ * @author Kirill Vainer
+ */
+public interface AnimEventListener {
+
+ /**
+ * Invoked when an animation "cycle" is done. For non-looping animations,
+ * this event is invoked when the animation is finished playing. For
+ * looping animations, this even is invoked each time the animation is restarted.
+ *
+ * @param control The control to which the listener is assigned.
+ * @param channel The channel being altered
+ * @param animName The new animation that is done.
+ */
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName);
+
+ /**
+ * Invoked when a animation is set to play by the user on the given channel.
+ *
+ * @param control The control to which the listener is assigned.
+ * @param channel The channel being altered
+ * @param animName The new animation name set.
+ */
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName);
+
+}
diff --git a/engine/src/core/com/jme3/animation/Animation.java b/engine/src/core/com/jme3/animation/Animation.java
new file mode 100644
index 0000000..0fb505a
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Animation.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * The animation class updates the animation target with the tracks of a given type.
+ *
+ * @author Kirill Vainer, Marcin Roguski (Kaelthas)
+ */
+public class Animation implements Savable, Cloneable {
+
+ /**
+ * The name of the animation.
+ */
+ private String name;
+
+ /**
+ * The length of the animation.
+ */
+ private float length;
+
+ /**
+ * The tracks of the animation.
+ */
+ private Track[] tracks;
+
+ /**
+ * Serialization-only. Do not use.
+ */
+ public Animation() {}
+
+ /**
+ * Creates a new <code>Animation</code> with the given name and length.
+ *
+ * @param name The name of the animation.
+ * @param length Length in seconds of the animation.
+ */
+ public Animation(String name, float length) {
+ this.name = name;
+ this.length = length;
+ }
+
+ /**
+ * The name of the bone animation
+ * @return name of the bone animation
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the length in seconds of this animation
+ *
+ * @return the length in seconds of this animation
+ */
+ public float getLength() {
+ return length;
+ }
+
+ /**
+ * This method sets the current time of the animation.
+ * This method behaves differently for every known track type.
+ * Override this method if you have your own type of track.
+ *
+ * @param time the time of the animation
+ * @param blendAmount the blend amount factor
+ * @param control the animation control
+ * @param channel the animation channel
+ */
+ void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel, TempVars vars) {
+ if (tracks == null)
+ return;
+
+ for (int i = 0; i < tracks.length; i++){
+ tracks[i].setTime(time, blendAmount, control, channel, vars);
+ }
+
+ /*
+ if (tracks != null && tracks.length > 0) {
+ Track<?> trackInstance = tracks[0];
+
+ if (trackInstance instanceof SpatialTrack) {
+ Spatial spatial = control.getSpatial();
+ if (spatial != null) {
+ ((SpatialTrack) tracks[0]).setTime(time, spatial, blendAmount);
+ }
+ } else if (trackInstance instanceof BoneTrack) {
+ BitSet affectedBones = channel.getAffectedBones();
+ Skeleton skeleton = control.getSkeleton();
+ for (int i = 0; i < tracks.length; ++i) {
+ if (affectedBones == null || affectedBones.get(((BoneTrack) tracks[i]).getTargetIndex())) {
+ ((BoneTrack) tracks[i]).setTime(time, skeleton, blendAmount);
+ }
+ }
+ } else if (trackInstance instanceof PoseTrack) {
+ Spatial spatial = control.getSpatial();
+ List<Mesh> meshes = new ArrayList<Mesh>();
+ this.getMeshes(spatial, meshes);
+ if (meshes.size() > 0) {
+ Mesh[] targets = meshes.toArray(new Mesh[meshes.size()]);
+ for (int i = 0; i < tracks.length; ++i) {
+ ((PoseTrack) tracks[i]).setTime(time, targets, blendAmount);
+ }
+ }
+ }
+ }
+ */
+ }
+
+ /**
+ * Set the {@link Track}s to be used by this animation.
+ * <p>
+ * The array should be organized so that the appropriate Track can
+ * be retrieved based on a bone index.
+ *
+ * @param tracks The tracks to set.
+ */
+ public void setTracks(Track[] tracks){
+ this.tracks = tracks;
+ }
+
+ /**
+ * Returns the tracks set in {@link #setTracks(com.jme3.animation.Track[]) }.
+ *
+ * @return the tracks set previously
+ */
+ public Track[] getTracks() {
+ return tracks;
+ }
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ @Override
+ public Animation clone() {
+ try {
+ Animation result = (Animation) super.clone();
+ result.tracks = tracks.clone();
+ for (int i = 0; i < tracks.length; ++i) {
+ result.tracks[i] = this.tracks[i].clone();
+ }
+ return result;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[name=" + name + ", length=" + length + ']';
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule out = ex.getCapsule(this);
+ out.write(name, "name", null);
+ out.write(length, "length", 0f);
+ out.write(tracks, "tracks", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ name = in.readString("name", null);
+ length = in.readFloat("length", 0f);
+
+ Savable[] arr = in.readSavableArray("tracks", null);
+ tracks = new Track[arr.length];
+ System.arraycopy(arr, 0, tracks, 0, arr.length);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/AnimationFactory.java b/engine/src/core/com/jme3/animation/AnimationFactory.java
new file mode 100644
index 0000000..b994ccb
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimationFactory.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+
+/**
+ * A convenience class to easily setup a spatial keyframed animation
+ * you can add some keyFrames for a given time or a given keyFrameIndex, for translation rotation and scale.
+ * The animationHelper will then generate an appropriate SpatialAnimation by interpolating values between the keyFrames.
+ * <br><br>
+ * Usage is : <br>
+ * - Create the AnimationHelper<br>
+ * - add some keyFrames<br>
+ * - call the buildAnimation() method that will retruna new Animation<br>
+ * - add the generated Animation to any existing AnimationControl<br>
+ * <br><br>
+ * Note that the first keyFrame (index 0) is defaulted with the identy transforms.
+ * If you want to change that you have to replace this keyFrame with any transform you want.
+ *
+ * @author Nehon
+ */
+public class AnimationFactory {
+
+ /**
+ * step for splitting rotation that have a n ange above PI/2
+ */
+ private final static float EULER_STEP = FastMath.QUARTER_PI * 3;
+
+ /**
+ * enum to determine the type of interpolation
+ */
+ private enum Type {
+
+ Translation, Rotation, Scale;
+ }
+
+ /**
+ * Inner Rotation type class to kep track on a rotation Euler angle
+ */
+ protected class Rotation {
+
+ /**
+ * The rotation Quaternion
+ */
+ Quaternion rotation = new Quaternion();
+ /**
+ * This rotation expressed in Euler angles
+ */
+ Vector3f eulerAngles = new Vector3f();
+ /**
+ * the index of the parent key frame is this keyFrame is a splitted rotation
+ */
+ int masterKeyFrame = -1;
+
+ public Rotation() {
+ rotation.loadIdentity();
+ }
+
+ void set(Quaternion rot) {
+ rotation.set(rot);
+ float[] a = new float[3];
+ rotation.toAngles(a);
+ eulerAngles.set(a[0], a[1], a[2]);
+ }
+
+ void set(float x, float y, float z) {
+ float[] a = {x, y, z};
+ rotation.fromAngles(a);
+ eulerAngles.set(x, y, z);
+ }
+ }
+ /**
+ * Name of the animation
+ */
+ protected String name;
+ /**
+ * frames per seconds
+ */
+ protected int fps;
+ /**
+ * Animation duration in seconds
+ */
+ protected float duration;
+ /**
+ * total number of frames
+ */
+ protected int totalFrames;
+ /**
+ * time per frame
+ */
+ protected float tpf;
+ /**
+ * Time array for this animation
+ */
+ protected float[] times;
+ /**
+ * Translation array for this animation
+ */
+ protected Vector3f[] translations;
+ /**
+ * rotation array for this animation
+ */
+ protected Quaternion[] rotations;
+ /**
+ * scales array for this animation
+ */
+ protected Vector3f[] scales;
+ /**
+ * The map of keyFrames to compute the animation. The key is the index of the frame
+ */
+ protected Vector3f[] keyFramesTranslation;
+ protected Vector3f[] keyFramesScale;
+ protected Rotation[] keyFramesRotation;
+
+ /**
+ * Creates and AnimationHelper
+ * @param duration the desired duration for the resulting animation
+ * @param name the name of the resulting animation
+ */
+ public AnimationFactory(float duration, String name) {
+ this(duration, name, 30);
+ }
+
+ /**
+ * Creates and AnimationHelper
+ * @param duration the desired duration for the resulting animation
+ * @param name the name of the resulting animation
+ * @param fps the number of frames per second for this animation (default is 30)
+ */
+ public AnimationFactory(float duration, String name, int fps) {
+ this.name = name;
+ this.duration = duration;
+ this.fps = fps;
+ totalFrames = (int) (fps * duration) + 1;
+ tpf = 1 / (float) fps;
+ times = new float[totalFrames];
+ translations = new Vector3f[totalFrames];
+ rotations = new Quaternion[totalFrames];
+ scales = new Vector3f[totalFrames];
+ keyFramesTranslation = new Vector3f[totalFrames];
+ keyFramesTranslation[0] = new Vector3f();
+ keyFramesScale = new Vector3f[totalFrames];
+ keyFramesScale[0] = new Vector3f(1, 1, 1);
+ keyFramesRotation = new Rotation[totalFrames];
+ keyFramesRotation[0] = new Rotation();
+
+ }
+
+ /**
+ * Adds a key frame for the given Transform at the given time
+ * @param time the time at which the keyFrame must be inserted
+ * @param transform the transforms to use for this keyFrame
+ */
+ public void addTimeTransform(float time, Transform transform) {
+ addKeyFrameTransform((int) (time / tpf), transform);
+ }
+
+ /**
+ * Adds a key frame for the given Transform at the given keyFrame index
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
+ * @param transform the transforms to use for this keyFrame
+ */
+ public void addKeyFrameTransform(int keyFrameIndex, Transform transform) {
+ addKeyFrameTranslation(keyFrameIndex, transform.getTranslation());
+ addKeyFrameScale(keyFrameIndex, transform.getScale());
+ addKeyFrameRotation(keyFrameIndex, transform.getRotation());
+ }
+
+ /**
+ * Adds a key frame for the given translation at the given time
+ * @param time the time at which the keyFrame must be inserted
+ * @param translation the translation to use for this keyFrame
+ */
+ public void addTimeTranslation(float time, Vector3f translation) {
+ addKeyFrameTranslation((int) (time / tpf), translation);
+ }
+
+ /**
+ * Adds a key frame for the given translation at the given keyFrame index
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
+ * @param translation the translation to use for this keyFrame
+ */
+ public void addKeyFrameTranslation(int keyFrameIndex, Vector3f translation) {
+ Vector3f t = getTranslationForFrame(keyFrameIndex);
+ t.set(translation);
+ }
+
+ /**
+ * Adds a key frame for the given rotation at the given time<br>
+ * This can't be used if the interpolated angle is higher than PI (180°)<br>
+ * Use {@link addTimeRotationAngles(float time, float x, float y, float z)} instead that uses Euler angles rotations.<br> *
+ * @param time the time at which the keyFrame must be inserted
+ * @param rotation the rotation Quaternion to use for this keyFrame
+ * @see #addTimeRotationAngles(float time, float x, float y, float z)
+ */
+ public void addTimeRotation(float time, Quaternion rotation) {
+ addKeyFrameRotation((int) (time / tpf), rotation);
+ }
+
+ /**
+ * Adds a key frame for the given rotation at the given keyFrame index<br>
+ * This can't be used if the interpolated angle is higher than PI (180°)<br>
+ * Use {@link addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z)} instead that uses Euler angles rotations.
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
+ * @param rotation the rotation Quaternion to use for this keyFrame
+ * @see #addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z)
+ */
+ public void addKeyFrameRotation(int keyFrameIndex, Quaternion rotation) {
+ Rotation r = getRotationForFrame(keyFrameIndex);
+ r.set(rotation);
+ }
+
+ /**
+ * Adds a key frame for the given rotation at the given time.<br>
+ * Rotation is expressed by Euler angles values in radians.<br>
+ * Note that the generated rotation will be stored as a quaternion and interpolated using a spherical linear interpolation (slerp)<br>
+ * Hence, this method may create intermediate keyFrames if the interpolation angle is higher than PI to ensure continuity in animation<br>
+ *
+ * @param time the time at which the keyFrame must be inserted
+ * @param x the rotation around the x axis (aka yaw) in radians
+ * @param y the rotation around the y axis (aka roll) in radians
+ * @param z the rotation around the z axis (aka pitch) in radians
+ */
+ public void addTimeRotationAngles(float time, float x, float y, float z) {
+ addKeyFrameRotationAngles((int) (time / tpf), x, y, z);
+ }
+
+ /**
+ * Adds a key frame for the given rotation at the given key frame index.<br>
+ * Rotation is expressed by Euler angles values in radians.<br>
+ * Note that the generated rotation will be stored as a quaternion and interpolated using a spherical linear interpolation (slerp)<br>
+ * Hence, this method may create intermediate keyFrames if the interpolation angle is higher than PI to ensure continuity in animation<br>
+ *
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
+ * @param x the rotation around the x axis (aka yaw) in radians
+ * @param y the rotation around the y axis (aka roll) in radians
+ * @param z the rotation around the z axis (aka pitch) in radians
+ */
+ public void addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z) {
+ Rotation r = getRotationForFrame(keyFrameIndex);
+ r.set(x, y, z);
+
+ // if the delta of euler angles is higher than PI, we create intermediate keyframes
+ // since we are using quaternions and slerp for rotation interpolation, we cannot interpolate over an angle higher than PI
+ int prev = getPreviousKeyFrame(keyFrameIndex, keyFramesRotation);
+ //previous rotation keyframe
+ Rotation prevRot = keyFramesRotation[prev];
+ //the maximum delta angle (x,y or z)
+ float delta = Math.max(Math.abs(x - prevRot.eulerAngles.x), Math.abs(y - prevRot.eulerAngles.y));
+ delta = Math.max(delta, Math.abs(z - prevRot.eulerAngles.z));
+ //if delta > PI we have to create intermediates key frames
+ if (delta >= FastMath.PI) {
+ //frames delta
+ int dF = keyFrameIndex - prev;
+ //angle per frame for x,y ,z
+ float dXAngle = (x - prevRot.eulerAngles.x) / (float) dF;
+ float dYAngle = (y - prevRot.eulerAngles.y) / (float) dF;
+ float dZAngle = (z - prevRot.eulerAngles.z) / (float) dF;
+
+ // the keyFrame step
+ int keyStep = (int) (((float) (dF)) / delta * (float) EULER_STEP);
+ // the current keyFrame
+ int cursor = prev + keyStep;
+ while (cursor < keyFrameIndex) {
+ //for each step we create a new rotation by interpolating the angles
+ Rotation dr = getRotationForFrame(cursor);
+ dr.masterKeyFrame = keyFrameIndex;
+ dr.set(prevRot.eulerAngles.x + cursor * dXAngle, prevRot.eulerAngles.y + cursor * dYAngle, prevRot.eulerAngles.z + cursor * dZAngle);
+ cursor += keyStep;
+ }
+
+ }
+
+ }
+
+ /**
+ * Adds a key frame for the given scale at the given time
+ * @param time the time at which the keyFrame must be inserted
+ * @param scale the scale to use for this keyFrame
+ */
+ public void addTimeScale(float time, Vector3f scale) {
+ addKeyFrameScale((int) (time / tpf), scale);
+ }
+
+ /**
+ * Adds a key frame for the given scale at the given keyFrame index
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
+ * @param scale the scale to use for this keyFrame
+ */
+ public void addKeyFrameScale(int keyFrameIndex, Vector3f scale) {
+ Vector3f s = getScaleForFrame(keyFrameIndex);
+ s.set(scale);
+ }
+
+ /**
+ * returns the translation for a given frame index
+ * creates the translation if it doesn't exists
+ * @param keyFrameIndex index
+ * @return the translation
+ */
+ private Vector3f getTranslationForFrame(int keyFrameIndex) {
+ if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) {
+ throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")");
+ }
+ Vector3f v = keyFramesTranslation[keyFrameIndex];
+ if (v == null) {
+ v = new Vector3f();
+ keyFramesTranslation[keyFrameIndex] = v;
+ }
+ return v;
+ }
+
+ /**
+ * returns the scale for a given frame index
+ * creates the scale if it doesn't exists
+ * @param keyFrameIndex index
+ * @return the scale
+ */
+ private Vector3f getScaleForFrame(int keyFrameIndex) {
+ if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) {
+ throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")");
+ }
+ Vector3f v = keyFramesScale[keyFrameIndex];
+ if (v == null) {
+ v = new Vector3f();
+ keyFramesScale[keyFrameIndex] = v;
+ }
+ return v;
+ }
+
+ /**
+ * returns the rotation for a given frame index
+ * creates the rotation if it doesn't exists
+ * @param keyFrameIndex index
+ * @return the rotation
+ */
+ private Rotation getRotationForFrame(int keyFrameIndex) {
+ if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) {
+ throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")");
+ }
+ Rotation v = keyFramesRotation[keyFrameIndex];
+ if (v == null) {
+ v = new Rotation();
+ keyFramesRotation[keyFrameIndex] = v;
+ }
+ return v;
+ }
+
+ /**
+ * Creates an Animation based on the keyFrames previously added to the helper.
+ * @return the generated animation
+ */
+ public Animation buildAnimation() {
+ interpolateTime();
+ interpolate(keyFramesTranslation, Type.Translation);
+ interpolate(keyFramesRotation, Type.Rotation);
+ interpolate(keyFramesScale, Type.Scale);
+
+ SpatialTrack spatialTrack = new SpatialTrack(times, translations, rotations, scales);
+
+ //creating the animation
+ Animation spatialAnimation = new Animation(name, duration);
+ spatialAnimation.setTracks(new SpatialTrack[]{spatialTrack});
+
+ return spatialAnimation;
+ }
+
+ /**
+ * interpolates time values
+ */
+ private void interpolateTime() {
+ for (int i = 0; i < totalFrames; i++) {
+ times[i] = i * tpf;
+ }
+ }
+
+ /**
+ * Interpolates over the key frames for the given keyFrame array and the given type of transform
+ * @param keyFrames the keyFrames array
+ * @param type the type of transforms
+ */
+ private void interpolate(Object[] keyFrames, Type type) {
+ int i = 0;
+ while (i < totalFrames) {
+ //fetching the next keyFrame index transform in the array
+ int key = getNextKeyFrame(i, keyFrames);
+ if (key != -1) {
+ //computing the frame span to interpolate over
+ int span = key - i;
+ //interating over the frames
+ for (int j = i; j <= key; j++) {
+ // computing interpolation value
+ float val = (float) (j - i) / (float) span;
+ //interpolationg depending on the transform type
+ switch (type) {
+ case Translation:
+ translations[j] = FastMath.interpolateLinear(val, (Vector3f) keyFrames[i], (Vector3f) keyFrames[key]);
+ break;
+ case Rotation:
+ Quaternion rot = new Quaternion();
+ rotations[j] = rot.slerp(((Rotation) keyFrames[i]).rotation, ((Rotation) keyFrames[key]).rotation, val);
+ break;
+ case Scale:
+ scales[j] = FastMath.interpolateLinear(val, (Vector3f) keyFrames[i], (Vector3f) keyFrames[key]);
+ break;
+ }
+ }
+ //jumping to the next keyFrame
+ i = key;
+ } else {
+ //No more key frame, filling the array witht he last transform computed.
+ for (int j = i; j < totalFrames; j++) {
+
+ switch (type) {
+ case Translation:
+ translations[j] = ((Vector3f) keyFrames[i]).clone();
+ break;
+ case Rotation:
+ rotations[j] = ((Quaternion) ((Rotation) keyFrames[i]).rotation).clone();
+ break;
+ case Scale:
+ scales[j] = ((Vector3f) keyFrames[i]).clone();
+ break;
+ }
+ }
+ //we're done
+ i = totalFrames;
+ }
+ }
+ }
+
+ /**
+ * Get the index of the next keyFrame that as a transform
+ * @param index the start index
+ * @param keyFrames the keyFrames array
+ * @return the index of the next keyFrame
+ */
+ private int getNextKeyFrame(int index, Object[] keyFrames) {
+ for (int i = index + 1; i < totalFrames; i++) {
+ if (keyFrames[i] != null) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get the index of the previous keyFrame that as a transform
+ * @param index the start index
+ * @param keyFrames the keyFrames array
+ * @return the index of the previous keyFrame
+ */
+ private int getPreviousKeyFrame(int index, Object[] keyFrames) {
+ for (int i = index - 1; i >= 0; i--) {
+ if (keyFrames[i] != null) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/Bone.java b/engine/src/core/com/jme3/animation/Bone.java
new file mode 100644
index 0000000..e7e9e2b
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Bone.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.*;
+import com.jme3.scene.Node;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * <code>Bone</code> describes a bone in the bone-weight skeletal animation
+ * system. A bone contains a name and an index, as well as relevant
+ * transformation data.
+ *
+ * @author Kirill Vainer
+ */
+public final class Bone implements Savable {
+
+ private String name;
+ private Bone parent;
+ private final ArrayList<Bone> children = new ArrayList<Bone>();
+ /**
+ * If enabled, user can control bone transform with setUserTransforms.
+ * Animation transforms are not applied to this bone when enabled.
+ */
+ private boolean userControl = false;
+ /**
+ * The attachment node.
+ */
+ private Node attachNode;
+ /**
+ * Initial transform is the local bind transform of this bone.
+ * PARENT SPACE -> BONE SPACE
+ */
+ private Vector3f initialPos;
+ private Quaternion initialRot;
+ private Vector3f initialScale;
+ /**
+ * The inverse world bind transform.
+ * BONE SPACE -> MODEL SPACE
+ */
+ private Vector3f worldBindInversePos;
+ private Quaternion worldBindInverseRot;
+ private Vector3f worldBindInverseScale;
+ /**
+ * The local animated transform combined with the local bind transform and parent world transform
+ */
+ private Vector3f localPos = new Vector3f();
+ private Quaternion localRot = new Quaternion();
+ private Vector3f localScale = new Vector3f(1.0f, 1.0f, 1.0f);
+ /**
+ * MODEL SPACE -> BONE SPACE (in animated state)
+ */
+ private Vector3f worldPos = new Vector3f();
+ private Quaternion worldRot = new Quaternion();
+ private Vector3f worldScale = new Vector3f();
+ //used for getCombinedTransform
+ private Transform tmpTransform = new Transform();
+
+ /**
+ * Creates a new bone with the given name.
+ *
+ * @param name Name to give to this bone
+ */
+ public Bone(String name) {
+ if (name == null)
+ throw new IllegalArgumentException("Name cannot be null");
+
+ this.name = name;
+
+ initialPos = new Vector3f();
+ initialRot = new Quaternion();
+ initialScale = new Vector3f(1, 1, 1);
+
+ worldBindInversePos = new Vector3f();
+ worldBindInverseRot = new Quaternion();
+ worldBindInverseScale = new Vector3f();
+ }
+
+ /**
+ * Special-purpose copy constructor.
+ * <p>
+ * Only copies the name and bind pose from the original.
+ * <p>
+ * WARNING: Local bind pose and world inverse bind pose transforms shallow
+ * copied. Modifying that data on the original bone will cause it to
+ * be recomputed on any cloned bones.
+ * <p>
+ * The rest of the data is <em>NOT</em> copied, as it will be
+ * generated automatically when the bone is animated.
+ *
+ * @param source The bone from which to copy the data.
+ */
+ Bone(Bone source) {
+ this.name = source.name;
+
+ userControl = source.userControl;
+
+ initialPos = source.initialPos;
+ initialRot = source.initialRot;
+ initialScale = source.initialScale;
+
+ worldBindInversePos = source.worldBindInversePos;
+ worldBindInverseRot = source.worldBindInverseRot;
+ worldBindInverseScale = source.worldBindInverseScale;
+
+ // parent and children will be assigned manually..
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Bone() {
+ }
+
+ /**
+ * Returns the name of the bone, set in the constructor.
+ *
+ * @return The name of the bone, set in the constructor.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns parent bone of this bone, or null if it is a root bone.
+ * @return The parent bone of this bone, or null if it is a root bone.
+ */
+ public Bone getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns all the children bones of this bone.
+ *
+ * @return All the children bones of this bone.
+ */
+ public ArrayList<Bone> getChildren() {
+ return children;
+ }
+
+ /**
+ * Returns the local position of the bone, relative to the parent bone.
+ *
+ * @return The local position of the bone, relative to the parent bone.
+ */
+ public Vector3f getLocalPosition() {
+ return localPos;
+ }
+
+ /**
+ * Returns the local rotation of the bone, relative to the parent bone.
+ *
+ * @return The local rotation of the bone, relative to the parent bone.
+ */
+ public Quaternion getLocalRotation() {
+ return localRot;
+ }
+
+ /**
+ * Returns the local scale of the bone, relative to the parent bone.
+ *
+ * @return The local scale of the bone, relative to the parent bone.
+ */
+ public Vector3f getLocalScale() {
+ return localScale;
+ }
+
+ /**
+ * Returns the position of the bone in model space.
+ *
+ * @return The position of the bone in model space.
+ */
+ public Vector3f getModelSpacePosition() {
+ return worldPos;
+ }
+
+ /**
+ * Returns the rotation of the bone in model space.
+ *
+ * @return The rotation of the bone in model space.
+ */
+ public Quaternion getModelSpaceRotation() {
+ return worldRot;
+ }
+
+ /**
+ * Returns the scale of the bone in model space.
+ *
+ * @return The scale of the bone in model space.
+ */
+ public Vector3f getModelSpaceScale() {
+ return worldScale;
+ }
+
+ /**
+ * Returns the inverse world bind pose position.
+ * <p>
+ * The bind pose transform of the bone is its "default"
+ * transform with no animation applied.
+ *
+ * @return the inverse world bind pose position.
+ */
+ public Vector3f getWorldBindInversePosition() {
+ return worldBindInversePos;
+ }
+
+ /**
+ * Returns the inverse world bind pose rotation.
+ * <p>
+ * The bind pose transform of the bone is its "default"
+ * transform with no animation applied.
+ *
+ * @return the inverse world bind pose rotation.
+ */
+ public Quaternion getWorldBindInverseRotation() {
+ return worldBindInverseRot;
+ }
+
+ /**
+ * Returns the inverse world bind pose scale.
+ * <p>
+ * The bind pose transform of the bone is its "default"
+ * transform with no animation applied.
+ *
+ * @return the inverse world bind pose scale.
+ */
+ public Vector3f getWorldBindInverseScale() {
+ return worldBindInverseScale;
+ }
+
+ /**
+ * Returns the world bind pose position.
+ * <p>
+ * The bind pose transform of the bone is its "default"
+ * transform with no animation applied.
+ *
+ * @return the world bind pose position.
+ */
+ public Vector3f getWorldBindPosition() {
+ return initialPos;
+ }
+
+ /**
+ * Returns the world bind pose rotation.
+ * <p>
+ * The bind pose transform of the bone is its "default"
+ * transform with no animation applied.
+ *
+ * @return the world bind pose rotation.
+ */
+ public Quaternion getWorldBindRotation() {
+ return initialRot;
+ }
+
+ /**
+ * Returns the world bind pose scale.
+ * <p>
+ * The bind pose transform of the bone is its "default"
+ * transform with no animation applied.
+ *
+ * @return the world bind pose scale.
+ */
+ public Vector3f getWorldBindScale() {
+ return initialScale;
+ }
+
+ /**
+ * If enabled, user can control bone transform with setUserTransforms.
+ * Animation transforms are not applied to this bone when enabled.
+ */
+ public void setUserControl(boolean enable) {
+ userControl = enable;
+ }
+
+ /**
+ * Add a new child to this bone. Shouldn't be used by user code.
+ * Can corrupt skeleton.
+ *
+ * @param bone The bone to add
+ */
+ public void addChild(Bone bone) {
+ children.add(bone);
+ bone.parent = this;
+ }
+
+ /**
+ * Updates the world transforms for this bone, and, possibly the attach node
+ * if not null.
+ * <p>
+ * The world transform of this bone is computed by combining the parent's
+ * world transform with this bones' local transform.
+ */
+ public final void updateWorldVectors() {
+ if (parent != null) {
+ //rotation
+ parent.worldRot.mult(localRot, worldRot);
+
+ //scale
+ //For scale parent scale is not taken into account!
+ // worldScale.set(localScale);
+ parent.worldScale.mult(localScale, worldScale);
+
+ //translation
+ //scale and rotation of parent affect bone position
+ parent.worldRot.mult(localPos, worldPos);
+ worldPos.multLocal(parent.worldScale);
+ worldPos.addLocal(parent.worldPos);
+ } else {
+ worldRot.set(localRot);
+ worldPos.set(localPos);
+ worldScale.set(localScale);
+ }
+
+ if (attachNode != null) {
+ attachNode.setLocalTranslation(worldPos);
+ attachNode.setLocalRotation(worldRot);
+ attachNode.setLocalScale(worldScale);
+ }
+ }
+
+ /**
+ * Updates world transforms for this bone and it's children.
+ */
+ final void update() {
+ this.updateWorldVectors();
+
+ for (int i = children.size() - 1; i >= 0; i--) {
+ children.get(i).update();
+ }
+ }
+
+ /**
+ * Saves the current bone state as its binding pose, including its children.
+ */
+ void setBindingPose() {
+ initialPos.set(localPos);
+ initialRot.set(localRot);
+ initialScale.set(localScale);
+
+ if (worldBindInversePos == null) {
+ worldBindInversePos = new Vector3f();
+ worldBindInverseRot = new Quaternion();
+ worldBindInverseScale = new Vector3f();
+ }
+
+ // Save inverse derived position/scale/orientation, used for calculate offset transform later
+ worldBindInversePos.set(worldPos);
+ worldBindInversePos.negateLocal();
+
+ worldBindInverseRot.set(worldRot);
+ worldBindInverseRot.inverseLocal();
+
+ worldBindInverseScale.set(Vector3f.UNIT_XYZ);
+ worldBindInverseScale.divideLocal(worldScale);
+
+ for (Bone b : children) {
+ b.setBindingPose();
+ }
+ }
+
+ /**
+ * Reset the bone and it's children to bind pose.
+ */
+ final void reset() {
+ if (!userControl) {
+ localPos.set(initialPos);
+ localRot.set(initialRot);
+ localScale.set(initialScale);
+ }
+
+ for (int i = children.size() - 1; i >= 0; i--) {
+ children.get(i).reset();
+ }
+ }
+
+ /**
+ * Stores the skinning transform in the specified Matrix4f.
+ * The skinning transform applies the animation of the bone to a vertex.
+ *
+ * This assumes that the world transforms for the entire bone hierarchy
+ * have already been computed, otherwise this method will return undefined
+ * results.
+ *
+ * @param outTransform
+ */
+ void getOffsetTransform(Matrix4f outTransform, Quaternion tmp1, Vector3f tmp2, Vector3f tmp3, Matrix3f tmp4) {
+ // Computing scale
+ Vector3f scale = worldScale.mult(worldBindInverseScale, tmp3);
+
+ // Computing rotation
+ Quaternion rotate = worldRot.mult(worldBindInverseRot, tmp1);
+
+ // Computing translation
+ // Translation depend on rotation and scale
+ Vector3f translate = worldPos.add(rotate.mult(scale.mult(worldBindInversePos, tmp2), tmp2), tmp2);
+
+ // Populating the matrix
+ outTransform.loadIdentity();
+ outTransform.setTransform(translate, scale, rotate.toRotationMatrix(tmp4));
+ }
+
+ /**
+ * Sets user transform.
+ */
+ public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
+ if (!userControl) {
+ throw new IllegalStateException("User control must be on bone to allow user transforms");
+ }
+
+ localPos.set(initialPos);
+ localRot.set(initialRot);
+ localScale.set(initialScale);
+
+ localPos.addLocal(translation);
+ localRot = localRot.mult(rotation);
+ localScale.multLocal(scale);
+ }
+
+ /**
+ * Must update all bones in skeleton for this to work.
+ * @param translation
+ * @param rotation
+ */
+ public void setUserTransformsWorld(Vector3f translation, Quaternion rotation) {
+ if (!userControl) {
+ throw new IllegalStateException("User control must be on bone to allow user transforms");
+ }
+
+ // TODO: add scale here ???
+ worldPos.set(translation);
+ worldRot.set(rotation);
+
+ //if there is an attached Node we need to set it's local transforms too.
+ if(attachNode != null){
+ attachNode.setLocalTranslation(translation);
+ attachNode.setLocalRotation(rotation);
+ }
+ }
+
+ /**
+ * Returns the local transform of this bone combined with the given position and rotation
+ * @param position a position
+ * @param rotation a rotation
+ */
+ public Transform getCombinedTransform(Vector3f position, Quaternion rotation) {
+ rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position);
+ tmpTransform.setRotation(rotation).getRotation().multLocal(localRot);
+ return tmpTransform;
+ }
+
+ /**
+ * Returns the attachment node.
+ * Attach models and effects to this node to make
+ * them follow this bone's motions.
+ */
+ Node getAttachmentsNode() {
+ if (attachNode == null) {
+ attachNode = new Node(name + "_attachnode");
+ attachNode.setUserData("AttachedBone", this);
+ }
+ return attachNode;
+ }
+
+ /**
+ * Used internally after model cloning.
+ * @param attachNode
+ */
+ void setAttachmentsNode(Node attachNode) {
+ this.attachNode = attachNode;
+ }
+
+ /**
+ * Sets the local animation transform of this bone.
+ * Bone is assumed to be in bind pose when this is called.
+ */
+ void setAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
+ if (userControl) {
+ return;
+ }
+
+// localPos.addLocal(translation);
+// localRot.multLocal(rotation);
+ //localRot = localRot.mult(rotation);
+
+ localPos.set(initialPos).addLocal(translation);
+ localRot.set(initialRot).multLocal(rotation);
+
+ if (scale != null) {
+ localScale.set(initialScale).multLocal(scale);
+ }
+ }
+
+ void blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight) {
+ if (userControl) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+// assert vars.lock();
+
+ Vector3f tmpV = vars.vect1;
+ Vector3f tmpV2 = vars.vect2;
+ Quaternion tmpQ = vars.quat1;
+
+ //location
+ tmpV.set(initialPos).addLocal(translation);
+ localPos.interpolate(tmpV, weight);
+
+ //rotation
+ tmpQ.set(initialRot).multLocal(rotation);
+ localRot.nlerp(tmpQ, weight);
+
+ //scale
+ if (scale != null) {
+ tmpV2.set(initialScale).multLocal(scale);
+ localScale.interpolate(tmpV2, weight);
+ }
+
+
+ vars.release();
+ }
+
+ /**
+ * Sets local bind transform for bone.
+ * Call setBindingPose() after all of the skeleton bones' bind transforms are set to save them.
+ */
+ public void setBindTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
+ initialPos.set(translation);
+ initialRot.set(rotation);
+ //ogre.xml can have null scale values breaking this if the check is removed
+ if (scale != null) {
+ initialScale.set(scale);
+ }
+
+ localPos.set(translation);
+ localRot.set(rotation);
+ if (scale != null) {
+ localScale.set(scale);
+ }
+ }
+
+ private String toString(int depth) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < depth; i++) {
+ sb.append('-');
+ }
+
+ sb.append(name).append(" bone\n");
+ for (Bone child : children) {
+ sb.append(child.toString(depth + 1));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return this.toString(0);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule input = im.getCapsule(this);
+
+ name = input.readString("name", null);
+ initialPos = (Vector3f) input.readSavable("initialPos", null);
+ initialRot = (Quaternion) input.readSavable("initialRot", null);
+ initialScale = (Vector3f) input.readSavable("initialScale", new Vector3f(1.0f, 1.0f, 1.0f));
+ attachNode = (Node) input.readSavable("attachNode", null);
+
+ localPos.set(initialPos);
+ localRot.set(initialRot);
+
+ ArrayList<Bone> childList = input.readSavableArrayList("children", null);
+ for (int i = childList.size() - 1; i >= 0; i--) {
+ this.addChild(childList.get(i));
+ }
+
+ // NOTE: Parent skeleton will call update() then setBindingPose()
+ // after Skeleton has been de-serialized.
+ // Therefore, worldBindInversePos and worldBindInverseRot
+ // will be reconstructed based on that information.
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule output = ex.getCapsule(this);
+
+ output.write(name, "name", null);
+ output.write(attachNode, "attachNode", null);
+ output.write(initialPos, "initialPos", null);
+ output.write(initialRot, "initialRot", null);
+ output.write(initialScale, "initialScale", new Vector3f(1.0f, 1.0f, 1.0f));
+ output.writeSavableArrayList(children, "children", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/BoneAnimation.java b/engine/src/core/com/jme3/animation/BoneAnimation.java
new file mode 100644
index 0000000..f5ffd40
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/BoneAnimation.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+/**
+ * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
+ */
+@Deprecated
+public final class BoneAnimation extends Animation {
+
+ @Deprecated
+ public BoneAnimation(String name, float length) {
+ super(name, length);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/BoneTrack.java b/engine/src/core/com/jme3/animation/BoneTrack.java
new file mode 100644
index 0000000..0d28ddc
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/BoneTrack.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.BitSet;
+
+/**
+ * Contains a list of transforms and times for each keyframe.
+ *
+ * @author Kirill Vainer
+ */
+public final class BoneTrack implements Track {
+
+ /**
+ * Bone index in the skeleton which this track effects.
+ */
+ private int targetBoneIndex;
+
+ /**
+ * Transforms and times for track.
+ */
+ private CompactVector3Array translations;
+ private CompactQuaternionArray rotations;
+ private CompactVector3Array scales;
+ private float[] times;
+
+ /**
+ * Serialization-only. Do not use.
+ */
+ public BoneTrack() {
+ }
+
+ /**
+ * Creates a bone track for the given bone index
+ * @param targetBoneIndex the bone index
+ * @param times a float array with the time of each frame
+ * @param translations the translation of the bone for each frame
+ * @param rotations the rotation of the bone for each frame
+ */
+ public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations) {
+ this.targetBoneIndex = targetBoneIndex;
+ this.setKeyframes(times, translations, rotations);
+ }
+
+ /**
+ * Creates a bone track for the given bone index
+ * @param targetBoneIndex the bone index
+ * @param times a float array with the time of each frame
+ * @param translations the translation of the bone for each frame
+ * @param rotations the rotation of the bone for each frame
+ * @param scales the scale of the bone for each frame
+ */
+ public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
+ this.targetBoneIndex = targetBoneIndex;
+ this.setKeyframes(times, translations, rotations, scales);
+ }
+
+ /**
+ * Creates a bone track for the given bone index
+ * @param targetBoneIndex the bone's index
+ */
+ public BoneTrack(int targetBoneIndex) {
+ this.targetBoneIndex = targetBoneIndex;
+ }
+
+ /**
+ * @return the bone index of this bone track.
+ */
+ public int getTargetBoneIndex() {
+ return targetBoneIndex;
+ }
+
+ /**
+ * return the array of rotations of this track
+ * @return
+ */
+ public Quaternion[] getRotations() {
+ return rotations.toObjectArray();
+ }
+
+ /**
+ * returns the array of scales for this track
+ * @return
+ */
+ public Vector3f[] getScales() {
+ return scales == null ? null : scales.toObjectArray();
+ }
+
+ /**
+ * returns the arrays of time for this track
+ * @return
+ */
+ public float[] getTimes() {
+ return times;
+ }
+
+ /**
+ * returns the array of translations of this track
+ * @return
+ */
+ public Vector3f[] getTranslations() {
+ return translations.toObjectArray();
+ }
+
+ /**
+ * Set the translations and rotations for this bone track
+ * @param times a float array with the time of each frame
+ * @param translations the translation of the bone for each frame
+ * @param rotations the rotation of the bone for each frame
+ */
+ public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations) {
+ if (times.length == 0) {
+ throw new RuntimeException("BoneTrack with no keyframes!");
+ }
+
+ assert times.length == translations.length && times.length == rotations.length;
+
+ this.times = times;
+ this.translations = new CompactVector3Array();
+ this.translations.add(translations);
+ this.translations.freeze();
+ this.rotations = new CompactQuaternionArray();
+ this.rotations.add(rotations);
+ this.rotations.freeze();
+ }
+
+ /**
+ * Set the translations, rotations and scales for this bone track
+ * @param times a float array with the time of each frame
+ * @param translations the translation of the bone for each frame
+ * @param rotations the rotation of the bone for each frame
+ * @param scales the scale of the bone for each frame
+ */
+ public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
+ this.setKeyframes(times, translations, rotations);
+ assert times.length == scales.length;
+ if (scales != null) {
+ this.scales = new CompactVector3Array();
+ this.scales.add(scales);
+ this.scales.freeze();
+ }
+ }
+
+ /**
+ *
+ * Modify the bone which this track modifies in the skeleton to contain
+ * the correct animation transforms for a given time.
+ * The transforms can be interpolated in some method from the keyframes.
+ *
+ * @param time the current time of the animation
+ * @param weight the weight of the animation
+ * @param control
+ * @param channel
+ * @param vars
+ */
+ public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
+ BitSet affectedBones = channel.getAffectedBones();
+ if (affectedBones != null && !affectedBones.get(targetBoneIndex)) {
+ return;
+ }
+
+ Bone target = control.getSkeleton().getBone(targetBoneIndex);
+
+ Vector3f tempV = vars.vect1;
+ Vector3f tempS = vars.vect2;
+ Quaternion tempQ = vars.quat1;
+ Vector3f tempV2 = vars.vect3;
+ Vector3f tempS2 = vars.vect4;
+ Quaternion tempQ2 = vars.quat2;
+
+ int lastFrame = times.length - 1;
+ if (time < 0 || lastFrame == 0) {
+ rotations.get(0, tempQ);
+ translations.get(0, tempV);
+ if (scales != null) {
+ scales.get(0, tempS);
+ }
+ } else if (time >= times[lastFrame]) {
+ rotations.get(lastFrame, tempQ);
+ translations.get(lastFrame, tempV);
+ if (scales != null) {
+ scales.get(lastFrame, tempS);
+ }
+ } else {
+ int startFrame = 0;
+ int endFrame = 1;
+ // use lastFrame so we never overflow the array
+ int i;
+ for (i = 0; i < lastFrame && times[i] < time; i++) {
+ startFrame = i;
+ endFrame = i + 1;
+ }
+
+ float blend = (time - times[startFrame])
+ / (times[endFrame] - times[startFrame]);
+
+ rotations.get(startFrame, tempQ);
+ translations.get(startFrame, tempV);
+ if (scales != null) {
+ scales.get(startFrame, tempS);
+ }
+ rotations.get(endFrame, tempQ2);
+ translations.get(endFrame, tempV2);
+ if (scales != null) {
+ scales.get(endFrame, tempS2);
+ }
+ tempQ.nlerp(tempQ2, blend);
+ tempV.interpolate(tempV2, blend);
+ tempS.interpolate(tempS2, blend);
+ }
+
+ if (weight != 1f) {
+ target.blendAnimTransforms(tempV, tempQ, scales != null ? tempS : null, weight);
+ } else {
+ target.setAnimTransforms(tempV, tempQ, scales != null ? tempS : null);
+ }
+ }
+
+ /**
+ * @return the length of the track
+ */
+ public float getLength() {
+ return times == null ? 0 : times[times.length - 1] - times[0];
+ }
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ @Override
+ public BoneTrack clone() {
+ int tablesLength = times.length;
+
+ float[] times = this.times.clone();
+ Vector3f[] sourceTranslations = this.getTranslations();
+ Quaternion[] sourceRotations = this.getRotations();
+ Vector3f[] sourceScales = this.getScales();
+
+ Vector3f[] translations = new Vector3f[tablesLength];
+ Quaternion[] rotations = new Quaternion[tablesLength];
+ Vector3f[] scales = new Vector3f[tablesLength];
+ for (int i = 0; i < tablesLength; ++i) {
+ translations[i] = sourceTranslations[i].clone();
+ rotations[i] = sourceRotations[i].clone();
+ scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f);
+ }
+
+ // Need to use the constructor here because of the final fields used in this class
+ return new BoneTrack(targetBoneIndex, times, translations, rotations, scales);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(targetBoneIndex, "boneIndex", 0);
+ oc.write(translations, "translations", null);
+ oc.write(rotations, "rotations", null);
+ oc.write(times, "times", null);
+ oc.write(scales, "scales", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ targetBoneIndex = ic.readInt("boneIndex", 0);
+
+ translations = (CompactVector3Array) ic.readSavable("translations", null);
+ rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
+ times = ic.readFloatArray("times", null);
+ scales = (CompactVector3Array) ic.readSavable("scales", null);
+
+ //Backward compatibility for old j3o files generated before revision 6807
+ if (im.getFormatVersion() == 0){
+ if (translations == null) {
+ Savable[] sav = ic.readSavableArray("translations", null);
+ if (sav != null) {
+ translations = new CompactVector3Array();
+ Vector3f[] transCopy = new Vector3f[sav.length];
+ System.arraycopy(sav, 0, transCopy, 0, sav.length);
+ translations.add(transCopy);
+ translations.freeze();
+ }
+ }
+ if (rotations == null) {
+ Savable[] sav = ic.readSavableArray("rotations", null);
+ if (sav != null) {
+ rotations = new CompactQuaternionArray();
+ Quaternion[] rotCopy = new Quaternion[sav.length];
+ System.arraycopy(sav, 0, rotCopy, 0, sav.length);
+ rotations.add(rotCopy);
+ rotations.freeze();
+ }
+ }
+ }
+ }
+
+ public void setTime(float time, float weight, AnimControl control, AnimChannel channel) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/CompactArray.java b/engine/src/core/com/jme3/animation/CompactArray.java
new file mode 100644
index 0000000..6a2a9be
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/CompactArray.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import java.lang.reflect.Array;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Object is indexed and stored in primitive float[]
+ * @author Lim, YongHoon
+ * @param <T>
+ */
+public abstract class CompactArray<T> {
+
+ private Map<T, Integer> indexPool = new HashMap<T, Integer>();
+ protected int[] index;
+ protected float[] array;
+ private boolean invalid;
+
+ /**
+ * Creates a compact array
+ */
+ public CompactArray() {
+ }
+
+ /**
+ * create array using serialized data
+ * @param compressedArray
+ * @param index
+ */
+ public CompactArray(float[] compressedArray, int[] index) {
+ this.array = compressedArray;
+ this.index = index;
+ }
+
+ /**
+ * Add objects.
+ * They are serialized automatically when get() method is called.
+ * @param objArray
+ */
+ public void add(T... objArray) {
+ if (objArray == null || objArray.length == 0) {
+ return;
+ }
+ invalid = true;
+ int base = 0;
+ if (index == null) {
+ index = new int[objArray.length];
+ } else {
+ if (indexPool.isEmpty()) {
+ throw new RuntimeException("Internal is already fixed");
+ }
+ base = index.length;
+
+ int[] tmp = new int[base + objArray.length];
+ System.arraycopy(index, 0, tmp, 0, index.length);
+ index = tmp;
+ //index = Arrays.copyOf(index, base+objArray.length);
+ }
+ for (int j = 0; j < objArray.length; j++) {
+ T obj = objArray[j];
+ if (obj == null) {
+ index[base + j] = -1;
+ } else {
+ Integer i = indexPool.get(obj);
+ if (i == null) {
+ i = indexPool.size();
+ indexPool.put(obj, i);
+ }
+ index[base + j] = i;
+ }
+ }
+ }
+
+ /**
+ * release objects.
+ * add() method call is not allowed anymore.
+ */
+ public void freeze() {
+ serialize();
+ indexPool.clear();
+ }
+
+ /**
+ * @param index
+ * @param value
+ */
+ public final void set(int index, T value) {
+ int j = getCompactIndex(index);
+ serialize(j, value);
+ }
+
+ /**
+ * returns the object for the given index
+ * @param index the index
+ * @param store an object to store the result
+ * @return
+ */
+ public final T get(int index, T store) {
+ serialize();
+ int j = getCompactIndex(index);
+ return deserialize(j, store);
+ }
+
+ /**
+ * return a float array of serialized data
+ * @return
+ */
+ public final float[] getSerializedData() {
+ serialize();
+ return array;
+ }
+
+ /**
+ * serialize this compact array
+ */
+ public final void serialize() {
+ if (invalid) {
+ int newSize = indexPool.size() * getTupleSize();
+ if (array == null || Array.getLength(array) < newSize) {
+ array = ensureCapacity(array, newSize);
+ for (Map.Entry<T, Integer> entry : indexPool.entrySet()) {
+ int i = entry.getValue();
+ T obj = entry.getKey();
+ serialize(i, obj);
+ }
+ }
+ invalid = false;
+ }
+ }
+
+ /**
+ * @return compacted array's primitive size
+ */
+ protected final int getSerializedSize() {
+ return Array.getLength(getSerializedData());
+ }
+
+ /**
+ * Ensure the capacity for the given array and the given size
+ * @param arr the array
+ * @param size the size
+ * @return
+ */
+ protected float[] ensureCapacity(float[] arr, int size) {
+ if (arr == null) {
+ return new float[size];
+ } else if (arr.length >= size) {
+ return arr;
+ } else {
+ float[] tmp = new float[size];
+ System.arraycopy(arr, 0, tmp, 0, arr.length);
+ return tmp;
+ //return Arrays.copyOf(arr, size);
+ }
+ }
+
+ /**
+ * retrun an array of indices for the given objects
+ * @param objArray
+ * @return
+ */
+ public final int[] getIndex(T... objArray) {
+ int[] index = new int[objArray.length];
+ for (int i = 0; i < index.length; i++) {
+ T obj = objArray[i];
+ index[i] = obj != null ? indexPool.get(obj) : -1;
+ }
+ return index;
+ }
+
+ /**
+ * returns the corresponding index in the compact array
+ * @param objIndex
+ * @return object index in the compacted object array
+ */
+ public int getCompactIndex(int objIndex) {
+ return index != null ? index[objIndex] : objIndex;
+ }
+
+ /**
+ * @return uncompressed object size
+ */
+ public final int getTotalObjectSize() {
+ assert getSerializedSize() % getTupleSize() == 0;
+ return index != null ? index.length : getSerializedSize() / getTupleSize();
+ }
+
+ /**
+ * @return compressed object size
+ */
+ public final int getCompactObjectSize() {
+ assert getSerializedSize() % getTupleSize() == 0;
+ return getSerializedSize() / getTupleSize();
+ }
+
+ /**
+ * decompress and return object array
+ * @return decompress and return object array
+ */
+ public final T[] toObjectArray() {
+ try {
+ T[] compactArr = (T[]) Array.newInstance(getElementClass(), getSerializedSize() / getTupleSize());
+ for (int i = 0; i < compactArr.length; i++) {
+ compactArr[i] = getElementClass().newInstance();
+ deserialize(i, compactArr[i]);
+ }
+
+ T[] objArr = (T[]) Array.newInstance(getElementClass(), getTotalObjectSize());
+ for (int i = 0; i < objArr.length; i++) {
+ int compactIndex = getCompactIndex(i);
+ objArr[i] = compactArr[compactIndex];
+ }
+ return objArr;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * serialize object
+ * @param compactIndex compacted object index
+ * @param store
+ */
+ protected abstract void serialize(int compactIndex, T store);
+
+ /**
+ * deserialize object
+ * @param compactIndex compacted object index
+ * @param store
+ */
+ protected abstract T deserialize(int compactIndex, T store);
+
+ /**
+ * serialized size of one object element
+ */
+ protected abstract int getTupleSize();
+
+ protected abstract Class<T> getElementClass();
+}
diff --git a/engine/src/core/com/jme3/animation/CompactQuaternionArray.java b/engine/src/core/com/jme3/animation/CompactQuaternionArray.java
new file mode 100644
index 0000000..7c56c41
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/CompactQuaternionArray.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.Quaternion;
+import java.io.IOException;
+
+/**
+ * Serialize and compress {@link Quaternion}[] by indexing same values
+ * It is converted to float[]
+ * @author Lim, YongHoon
+ */
+public class CompactQuaternionArray extends CompactArray<Quaternion> implements Savable {
+
+ /**
+ * creates a compact Quaternion array
+ */
+ public CompactQuaternionArray() {
+ }
+
+ /**
+ * creates a compact Quaternion array
+ * @param dataArray the data array
+ * @param index the indices array
+ */
+ public CompactQuaternionArray(float[] dataArray, int[] index) {
+ super(dataArray, index);
+ }
+
+ @Override
+ protected final int getTupleSize() {
+ return 4;
+ }
+
+ @Override
+ protected final Class<Quaternion> getElementClass() {
+ return Quaternion.class;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ serialize();
+ OutputCapsule out = ex.getCapsule(this);
+ out.write(array, "array", null);
+ out.write(index, "index", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ array = in.readFloatArray("array", null);
+ index = in.readIntArray("index", null);
+ }
+
+ @Override
+ protected void serialize(int i, Quaternion store) {
+ int j = i * getTupleSize();
+ array[j] = store.getX();
+ array[j + 1] = store.getY();
+ array[j + 2] = store.getZ();
+ array[j + 3] = store.getW();
+ }
+
+ @Override
+ protected Quaternion deserialize(int i, Quaternion store) {
+ int j = i * getTupleSize();
+ store.set(array[j], array[j + 1], array[j + 2], array[j + 3]);
+ return store;
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/CompactVector3Array.java b/engine/src/core/com/jme3/animation/CompactVector3Array.java
new file mode 100644
index 0000000..cdfc6e4
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/CompactVector3Array.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * Serialize and compress Vector3f[] by indexing same values
+ * @author Lim, YongHoon
+ */
+public class CompactVector3Array extends CompactArray<Vector3f> implements Savable {
+
+ /**
+ * Creates a compact vector array
+ */
+ public CompactVector3Array() {
+ }
+
+ /**
+ * creates a compact vector array
+ * @param dataArray the data array
+ * @param index the indices
+ */
+ public CompactVector3Array(float[] dataArray, int[] index) {
+ super(dataArray, index);
+ }
+
+ @Override
+ protected final int getTupleSize() {
+ return 3;
+ }
+
+ @Override
+ protected final Class<Vector3f> getElementClass() {
+ return Vector3f.class;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ serialize();
+ OutputCapsule out = ex.getCapsule(this);
+ out.write(array, "array", null);
+ out.write(index, "index", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ array = in.readFloatArray("array", null);
+ index = in.readIntArray("index", null);
+ }
+
+ @Override
+ protected void serialize(int i, Vector3f store) {
+ int j = i*getTupleSize();
+ array[j] = store.getX();
+ array[j+1] = store.getY();
+ array[j+2] = store.getZ();
+ }
+
+ @Override
+ protected Vector3f deserialize(int i, Vector3f store) {
+ int j = i*getTupleSize();
+ store.set(array[j], array[j+1], array[j+2]);
+ return store;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/animation/LoopMode.java b/engine/src/core/com/jme3/animation/LoopMode.java
new file mode 100644
index 0000000..b74461e
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/LoopMode.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+/**
+ * <code>LoopMode</code> determines how animations repeat, or if they
+ * do not repeat.
+ */
+public enum LoopMode {
+ /**
+ * The animation will play repeatedly, when it reaches the end
+ * the animation will play again from the beginning, and so on.
+ */
+ Loop,
+
+ /**
+ * The animation will not loop. It will play until the last frame, and then
+ * freeze at that frame. It is possible to decide to play a new animation
+ * when that happens by using a AnimEventListener.
+ */
+ DontLoop,
+
+ /**
+ * The animation will cycle back and forth. When reaching the end, the
+ * animation will play backwards from the last frame until it reaches
+ * the first frame.
+ */
+ Cycle,
+
+}
diff --git a/engine/src/core/com/jme3/animation/Pose.java b/engine/src/core/com/jme3/animation/Pose.java
new file mode 100644
index 0000000..9ff6ec8
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Pose.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.Vector3f;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * A pose is a list of offsets that say where a mesh vertices should be for this pose.
+ */
+public final class Pose implements Savable, Cloneable {
+
+ private String name;
+ private int targetMeshIndex;
+
+ private Vector3f[] offsets;
+ private int[] indices;
+
+ private transient final Vector3f tempVec = new Vector3f();
+ private transient final Vector3f tempVec2 = new Vector3f();
+
+ public Pose(String name, int targetMeshIndex, Vector3f[] offsets, int[] indices){
+ this.name = name;
+ this.targetMeshIndex = targetMeshIndex;
+ this.offsets = offsets;
+ this.indices = indices;
+ }
+
+ public int getTargetMeshIndex(){
+ return targetMeshIndex;
+ }
+
+
+ /**
+ * Applies the offsets of this pose to the vertex buffer given by the blend factor.
+ *
+ * @param blend Blend factor, 0 = no change to vertex buffer, 1 = apply full offsets
+ * @param vertbuf Vertex buffer to apply this pose to
+ */
+ public void apply(float blend, FloatBuffer vertbuf){
+ for (int i = 0; i < indices.length; i++){
+ Vector3f offset = offsets[i];
+ int vertIndex = indices[i];
+
+ tempVec.set(offset).multLocal(blend);
+
+ // acquire vertex
+ BufferUtils.populateFromBuffer(tempVec2, vertbuf, vertIndex);
+
+ // add offset multiplied by factor
+ tempVec2.addLocal(tempVec);
+
+ // write modified vertex
+ BufferUtils.setInBuffer(tempVec2, vertbuf, vertIndex);
+ }
+ }
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ public Pose clone() {
+ try {
+ Pose result = (Pose) super.clone();
+ result.indices = this.indices.clone();
+ if(this.offsets!=null) {
+ result.offsets = new Vector3f[this.offsets.length];
+ for(int i=0;i<this.offsets.length;++i) {
+ result.offsets[i] = this.offsets[i].clone();
+ }
+ }
+ return result;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(name, "name", "");
+ out.write(targetMeshIndex, "meshIndex", -1);
+ out.write(offsets, "offsets", null);
+ out.write(indices, "indices", null);
+ }
+
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ name = in.readString("name", "");
+ targetMeshIndex = in.readInt("meshIndex", -1);
+ offsets = (Vector3f[]) in.readSavableArray("offsets", null);
+ indices = in.readIntArray("indices", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/PoseTrack.java b/engine/src/core/com/jme3/animation/PoseTrack.java
new file mode 100644
index 0000000..bf1b0ab
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/PoseTrack.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * A single track of pose animation associated with a certain mesh.
+ */
+@Deprecated
+public final class PoseTrack implements Track {
+
+ private int targetMeshIndex;
+ private PoseFrame[] frames;
+ private float[] times;
+
+ public static class PoseFrame implements Savable, Cloneable {
+
+ Pose[] poses;
+ float[] weights;
+
+ public PoseFrame(Pose[] poses, float[] weights) {
+ this.poses = poses;
+ this.weights = weights;
+ }
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ @Override
+ public PoseFrame clone() {
+ try {
+ PoseFrame result = (PoseFrame) super.clone();
+ result.weights = this.weights.clone();
+ if (this.poses != null) {
+ result.poses = new Pose[this.poses.length];
+ for (int i = 0; i < this.poses.length; ++i) {
+ result.poses[i] = this.poses[i].clone();
+ }
+ }
+ return result;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(poses, "poses", null);
+ out.write(weights, "weights", null);
+ }
+
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ poses = (Pose[]) in.readSavableArray("poses", null);
+ weights = in.readFloatArray("weights", null);
+ }
+ }
+
+ public PoseTrack(int targetMeshIndex, float[] times, PoseFrame[] frames){
+ this.targetMeshIndex = targetMeshIndex;
+ this.times = times;
+ this.frames = frames;
+ }
+
+ private void applyFrame(Mesh target, int frameIndex, float weight){
+ PoseFrame frame = frames[frameIndex];
+ VertexBuffer pb = target.getBuffer(Type.Position);
+ for (int i = 0; i < frame.poses.length; i++){
+ Pose pose = frame.poses[i];
+ float poseWeight = frame.weights[i] * weight;
+
+ pose.apply(poseWeight, (FloatBuffer) pb.getData());
+ }
+
+ // force to re-upload data to gpu
+ pb.updateData(pb.getData());
+ }
+
+ public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
+ // TODO: When MeshControl is created, it will gather targets
+ // list automatically which is then retrieved here.
+
+ /*
+ Mesh target = targets[targetMeshIndex];
+ if (time < times[0]) {
+ applyFrame(target, 0, weight);
+ } else if (time > times[times.length - 1]) {
+ applyFrame(target, times.length - 1, weight);
+ } else {
+ int startFrame = 0;
+ for (int i = 0; i < times.length; i++) {
+ if (times[i] < time) {
+ startFrame = i;
+ }
+ }
+
+ int endFrame = startFrame + 1;
+ float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]);
+ applyFrame(target, startFrame, blend * weight);
+ applyFrame(target, endFrame, (1f - blend) * weight);
+ }
+ */
+ }
+
+ /**
+ * @return the length of the track
+ */
+ public float getLength() {
+ return times == null ? 0 : times[times.length - 1] - times[0];
+ }
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ @Override
+ public PoseTrack clone() {
+ try {
+ PoseTrack result = (PoseTrack) super.clone();
+ result.times = this.times.clone();
+ if (this.frames != null) {
+ result.frames = new PoseFrame[this.frames.length];
+ for (int i = 0; i < this.frames.length; ++i) {
+ result.frames[i] = this.frames[i].clone();
+ }
+ }
+ return result;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(targetMeshIndex, "meshIndex", 0);
+ out.write(frames, "frames", null);
+ out.write(times, "times", null);
+ }
+
+ @Override
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ targetMeshIndex = in.readInt("meshIndex", 0);
+ frames = (PoseFrame[]) in.readSavableArray("frames", null);
+ times = in.readFloatArray("times", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/Skeleton.java b/engine/src/core/com/jme3/animation/Skeleton.java
new file mode 100644
index 0000000..bc36542
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Skeleton.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.Matrix4f;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <code>Skeleton</code> is a convenience class for managing a bone hierarchy.
+ * Skeleton updates the world transforms to reflect the current local
+ * animated matrixes.
+ *
+ * @author Kirill Vainer
+ */
+public final class Skeleton implements Savable {
+
+ private Bone[] rootBones;
+ private Bone[] boneList;
+
+ /**
+ * Contains the skinning matrices, multiplying it by a vertex effected by a bone
+ * will cause it to go to the animated position.
+ */
+ private transient Matrix4f[] skinningMatrixes;
+
+ /**
+ * Creates a skeleton from a bone list.
+ * The root bones are found automatically.
+ * <p>
+ * Note that using this constructor will cause the bones in the list
+ * to have their bind pose recomputed based on their local transforms.
+ *
+ * @param boneList The list of bones to manage by this Skeleton
+ */
+ public Skeleton(Bone[] boneList) {
+ this.boneList = boneList;
+
+ List<Bone> rootBoneList = new ArrayList<Bone>();
+ for (int i = boneList.length - 1; i >= 0; i--) {
+ Bone b = boneList[i];
+ if (b.getParent() == null) {
+ rootBoneList.add(b);
+ }
+ }
+ rootBones = rootBoneList.toArray(new Bone[rootBoneList.size()]);
+
+ createSkinningMatrices();
+
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ Bone rootBone = rootBones[i];
+ rootBone.update();
+ rootBone.setBindingPose();
+ }
+ }
+
+ /**
+ * Special-purpose copy constructor.
+ * <p>
+ * Shallow copies bind pose data from the source skeleton, does not
+ * copy any other data.
+ *
+ * @param source The source Skeleton to copy from
+ */
+ public Skeleton(Skeleton source) {
+ Bone[] sourceList = source.boneList;
+ boneList = new Bone[sourceList.length];
+ for (int i = 0; i < sourceList.length; i++) {
+ boneList[i] = new Bone(sourceList[i]);
+ }
+
+ rootBones = new Bone[source.rootBones.length];
+ for (int i = 0; i < rootBones.length; i++) {
+ rootBones[i] = recreateBoneStructure(source.rootBones[i]);
+ }
+ createSkinningMatrices();
+
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].update();
+ }
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Skeleton() {
+ }
+
+ private void createSkinningMatrices() {
+ skinningMatrixes = new Matrix4f[boneList.length];
+ for (int i = 0; i < skinningMatrixes.length; i++) {
+ skinningMatrixes[i] = new Matrix4f();
+ }
+ }
+
+ private Bone recreateBoneStructure(Bone sourceRoot) {
+ Bone targetRoot = getBone(sourceRoot.getName());
+ List<Bone> children = sourceRoot.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ Bone sourceChild = children.get(i);
+ // find my version of the child
+ Bone targetChild = getBone(sourceChild.getName());
+ targetRoot.addChild(targetChild);
+ recreateBoneStructure(sourceChild);
+ }
+
+ return targetRoot;
+ }
+
+ /**
+ * Updates world transforms for all bones in this skeleton.
+ * Typically called after setting local animation transforms.
+ */
+ public void updateWorldVectors() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].update();
+ }
+ }
+
+ /**
+ * Saves the current skeleton state as it's binding pose.
+ */
+ public void setBindingPose() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].setBindingPose();
+ }
+ }
+
+ /**
+ * Reset the skeleton to bind pose.
+ */
+ public final void reset() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].reset();
+ }
+ }
+
+ /**
+ * Reset the skeleton to bind pose and updates the bones
+ */
+ public final void resetAndUpdate() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ Bone rootBone = rootBones[i];
+ rootBone.reset();
+ rootBone.update();
+ }
+ }
+
+ /**
+ * returns the array of all root bones of this skeleton
+ * @return
+ */
+ public Bone[] getRoots() {
+ return rootBones;
+ }
+
+ /**
+ * return a bone for the given index
+ * @param index
+ * @return
+ */
+ public Bone getBone(int index) {
+ return boneList[index];
+ }
+
+ /**
+ * returns the bone with the given name
+ * @param name
+ * @return
+ */
+ public Bone getBone(String name) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i].getName().equals(name)) {
+ return boneList[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * returns the bone index of the given bone
+ * @param bone
+ * @return
+ */
+ public int getBoneIndex(Bone bone) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i] == bone) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * returns the bone index of the bone that has the given name
+ * @param name
+ * @return
+ */
+ public int getBoneIndex(String name) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i].getName().equals(name)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Compute the skining matrices for each bone of the skeleton that would be used to transform vertices of associated meshes
+ * @return
+ */
+ public Matrix4f[] computeSkinningMatrices() {
+ TempVars vars = TempVars.get();
+ for (int i = 0; i < boneList.length; i++) {
+ boneList[i].getOffsetTransform(skinningMatrixes[i], vars.quat1, vars.vect1, vars.vect2, vars.tempMat3);
+ }
+ vars.release();
+ return skinningMatrixes;
+ }
+
+ /**
+ * returns the number of bones of this skeleton
+ * @return
+ */
+ public int getBoneCount() {
+ return boneList.length;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Skeleton - ").append(boneList.length).append(" bones, ").append(rootBones.length).append(" roots\n");
+ for (Bone rootBone : rootBones) {
+ sb.append(rootBone.toString());
+ }
+ return sb.toString();
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule input = im.getCapsule(this);
+
+ Savable[] boneRootsAsSav = input.readSavableArray("rootBones", null);
+ rootBones = new Bone[boneRootsAsSav.length];
+ System.arraycopy(boneRootsAsSav, 0, rootBones, 0, boneRootsAsSav.length);
+
+ Savable[] boneListAsSavable = input.readSavableArray("boneList", null);
+ boneList = new Bone[boneListAsSavable.length];
+ System.arraycopy(boneListAsSavable, 0, boneList, 0, boneListAsSavable.length);
+
+ createSkinningMatrices();
+
+ for (Bone rootBone : rootBones) {
+ rootBone.update();
+ rootBone.setBindingPose();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule output = ex.getCapsule(this);
+ output.write(rootBones, "rootBones", null);
+ output.write(boneList, "boneList", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/SkeletonControl.java b/engine/src/core/com/jme3/animation/SkeletonControl.java
new file mode 100644
index 0000000..3c8e117
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/SkeletonControl.java
@@ -0,0 +1,549 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix4f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+
+/**
+ * The Skeleton control deforms a model according to a skeleton,
+ * It handles the computation of the deformation matrices and performs
+ * the transformations on the mesh
+ *
+ * @author Rémy Bouquet Based on AnimControl by Kirill Vainer
+ */
+public class SkeletonControl extends AbstractControl implements Cloneable {
+
+ /**
+ * The skeleton of the model
+ */
+ private Skeleton skeleton;
+ /**
+ * List of targets which this controller effects.
+ */
+ private Mesh[] targets;
+ /**
+ * Used to track when a mesh was updated. Meshes are only updated
+ * if they are visible in at least one camera.
+ */
+ private boolean wasMeshUpdated = false;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public SkeletonControl() {
+ }
+
+ /**
+ * Creates a skeleton control.
+ * The list of targets will be acquired automatically when
+ * the control is attached to a node.
+ *
+ * @param skeleton the skeleton
+ */
+ public SkeletonControl(Skeleton skeleton) {
+ this.skeleton = skeleton;
+ }
+
+ /**
+ * Creates a skeleton control.
+ *
+ * @param targets the meshes controlled by the skeleton
+ * @param skeleton the skeleton
+ */
+ @Deprecated
+ SkeletonControl(Mesh[] targets, Skeleton skeleton) {
+ this.skeleton = skeleton;
+ this.targets = targets;
+ }
+
+ private boolean isMeshAnimated(Mesh mesh) {
+ return mesh.getBuffer(Type.BindPosePosition) != null;
+ }
+
+ private Mesh[] findTargets(Node node) {
+ Mesh sharedMesh = null;
+ ArrayList<Mesh> animatedMeshes = new ArrayList<Mesh>();
+
+ for (Spatial child : node.getChildren()) {
+ if (!(child instanceof Geometry)) {
+ continue; // could be an attachment node, ignore.
+ }
+
+ Geometry geom = (Geometry) child;
+
+ // is this geometry using a shared mesh?
+ Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
+
+ if (childSharedMesh != null) {
+ // Don't bother with non-animated shared meshes
+ if (isMeshAnimated(childSharedMesh)) {
+ // child is using shared mesh,
+ // so animate the shared mesh but ignore child
+ if (sharedMesh == null) {
+ sharedMesh = childSharedMesh;
+ } else if (sharedMesh != childSharedMesh) {
+ throw new IllegalStateException("Two conflicting shared meshes for " + node);
+ }
+ }
+ } else {
+ Mesh mesh = geom.getMesh();
+ if (isMeshAnimated(mesh)) {
+ animatedMeshes.add(mesh);
+ }
+ }
+ }
+
+ if (sharedMesh != null) {
+ animatedMeshes.add(sharedMesh);
+ }
+
+ return animatedMeshes.toArray(new Mesh[animatedMeshes.size()]);
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ super.setSpatial(spatial);
+ if (spatial != null) {
+ Node node = (Node) spatial;
+ targets = findTargets(node);
+ } else {
+ targets = null;
+ }
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ if (!wasMeshUpdated) {
+ resetToBind(); // reset morph meshes to bind pose
+
+ Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
+
+ // if hardware skinning is supported, the matrices and weight buffer
+ // will be sent by the SkinningShaderLogic object assigned to the shader
+ for (int i = 0; i < targets.length; i++) {
+ // NOTE: This assumes that code higher up
+ // Already ensured those targets are animated
+ // otherwise a crash will happen in skin update
+ //if (isMeshAnimated(targets[i])) {
+ softwareSkinUpdate(targets[i], offsetMatrices);
+ //}
+ }
+
+ wasMeshUpdated = true;
+ }
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ wasMeshUpdated = false;
+ }
+
+ void resetToBind() {
+ for (Mesh mesh : targets) {
+ if (isMeshAnimated(mesh)) {
+ VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
+ ByteBuffer bib = (ByteBuffer) bi.getData();
+ if (!bib.hasArray()) {
+ mesh.prepareForAnim(true); // prepare for software animation
+ }
+ VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
+ VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
+ VertexBuffer pos = mesh.getBuffer(Type.Position);
+ VertexBuffer norm = mesh.getBuffer(Type.Normal);
+ FloatBuffer pb = (FloatBuffer) pos.getData();
+ FloatBuffer nb = (FloatBuffer) norm.getData();
+ FloatBuffer bpb = (FloatBuffer) bindPos.getData();
+ FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
+ pb.clear();
+ nb.clear();
+ bpb.clear();
+ bnb.clear();
+
+ //reseting bind tangents if there is a bind tangent buffer
+ VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent);
+ if (bindTangents != null) {
+ VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
+ FloatBuffer tb = (FloatBuffer) tangents.getData();
+ FloatBuffer btb = (FloatBuffer) bindTangents.getData();
+ tb.clear();
+ btb.clear();
+ tb.put(btb).clear();
+ }
+
+
+ pb.put(bpb).clear();
+ nb.put(bnb).clear();
+ }
+ }
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ Node clonedNode = (Node) spatial;
+ AnimControl ctrl = spatial.getControl(AnimControl.class);
+ SkeletonControl clone = new SkeletonControl();
+ clone.setSpatial(clonedNode);
+
+ clone.skeleton = ctrl.getSkeleton();
+ // Fix animated targets for the cloned node
+ clone.targets = findTargets(clonedNode);
+
+ // Fix attachments for the cloned node
+ for (int i = 0; i < clonedNode.getQuantity(); i++) {
+ // go through attachment nodes, apply them to correct bone
+ Spatial child = clonedNode.getChild(i);
+ if (child instanceof Node) {
+ Node clonedAttachNode = (Node) child;
+ Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
+
+ if (originalBone != null) {
+ Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
+
+ clonedAttachNode.setUserData("AttachedBone", clonedBone);
+ clonedBone.setAttachmentsNode(clonedAttachNode);
+ }
+ }
+ }
+
+ return clone;
+ }
+
+ /**
+ *
+ * @param boneName the name of the bone
+ * @return the node attached to this bone
+ */
+ public Node getAttachmentsNode(String boneName) {
+ Bone b = skeleton.getBone(boneName);
+ if (b == null) {
+ throw new IllegalArgumentException("Given bone name does not exist "
+ + "in the skeleton.");
+ }
+
+ Node n = b.getAttachmentsNode();
+ Node model = (Node) spatial;
+ model.attachChild(n);
+ return n;
+ }
+
+ /**
+ * returns the skeleton of this control
+ * @return
+ */
+ public Skeleton getSkeleton() {
+ return skeleton;
+ }
+
+ /**
+ * sets the skeleton for this control
+ * @param skeleton
+ */
+// public void setSkeleton(Skeleton skeleton) {
+// this.skeleton = skeleton;
+// }
+ /**
+ * returns the targets meshes of this control
+ * @return
+ */
+ public Mesh[] getTargets() {
+ return targets;
+ }
+
+ /**
+ * sets the target meshes of this control
+ * @param targets
+ */
+// public void setTargets(Mesh[] targets) {
+// this.targets = targets;
+// }
+ /**
+ * Update the mesh according to the given transformation matrices
+ * @param mesh then mesh
+ * @param offsetMatrices the transformation matrices to apply
+ */
+ private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
+
+ VertexBuffer tb = mesh.getBuffer(Type.Tangent);
+ if (tb == null) {
+ //if there are no tangents use the classic skinning
+ applySkinning(mesh, offsetMatrices);
+ } else {
+ //if there are tangents use the skinning with tangents
+ applySkinningTangents(mesh, offsetMatrices, tb);
+ }
+
+
+ }
+
+ /**
+ * Method to apply skinning transforms to a mesh's buffers
+ * @param mesh the mesh
+ * @param offsetMatrices the offset matices to apply
+ */
+ private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
+ int maxWeightsPerVert = mesh.getMaxNumWeights();
+ if (maxWeightsPerVert <= 0) {
+ throw new IllegalStateException("Max weights per vert is incorrectly set!");
+ }
+
+ int fourMinusMaxWeights = 4 - maxWeightsPerVert;
+
+ // NOTE: This code assumes the vertex buffer is in bind pose
+ // resetToBind() has been called this frame
+ VertexBuffer vb = mesh.getBuffer(Type.Position);
+ FloatBuffer fvb = (FloatBuffer) vb.getData();
+ fvb.rewind();
+
+ VertexBuffer nb = mesh.getBuffer(Type.Normal);
+ FloatBuffer fnb = (FloatBuffer) nb.getData();
+ fnb.rewind();
+
+ // get boneIndexes and weights for mesh
+ ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+ FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+ ib.rewind();
+ wb.rewind();
+
+ float[] weights = wb.array();
+ byte[] indices = ib.array();
+ int idxWeights = 0;
+
+ TempVars vars = TempVars.get();
+
+
+ float[] posBuf = vars.skinPositions;
+ float[] normBuf = vars.skinNormals;
+
+ int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
+ int bufLength = posBuf.length;
+ for (int i = iterations - 1; i >= 0; i--) {
+ // read next set of positions and normals from native buffer
+ bufLength = Math.min(posBuf.length, fvb.remaining());
+ fvb.get(posBuf, 0, bufLength);
+ fnb.get(normBuf, 0, bufLength);
+ int verts = bufLength / 3;
+ int idxPositions = 0;
+
+ // iterate vertices and apply skinning transform for each effecting bone
+ for (int vert = verts - 1; vert >= 0; vert--) {
+ float nmx = normBuf[idxPositions];
+ float vtx = posBuf[idxPositions++];
+ float nmy = normBuf[idxPositions];
+ float vty = posBuf[idxPositions++];
+ float nmz = normBuf[idxPositions];
+ float vtz = posBuf[idxPositions++];
+
+ float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0;
+
+ for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
+ float weight = weights[idxWeights];
+ Matrix4f mat = offsetMatrices[indices[idxWeights++]];
+
+ rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
+ ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
+ rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
+
+ rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
+ rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
+ rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
+ }
+
+ idxWeights += fourMinusMaxWeights;
+
+ idxPositions -= 3;
+ normBuf[idxPositions] = rnx;
+ posBuf[idxPositions++] = rx;
+ normBuf[idxPositions] = rny;
+ posBuf[idxPositions++] = ry;
+ normBuf[idxPositions] = rnz;
+ posBuf[idxPositions++] = rz;
+ }
+
+ fvb.position(fvb.position() - bufLength);
+ fvb.put(posBuf, 0, bufLength);
+ fnb.position(fnb.position() - bufLength);
+ fnb.put(normBuf, 0, bufLength);
+ }
+
+ vars.release();
+
+ vb.updateData(fvb);
+ nb.updateData(fnb);
+
+ }
+
+ /**
+ * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation with
+ * null checks that would slow down the process even if tangents don't have to be computed.
+ * Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos and norm
+ * @param maxWeightsPerVert maximum number of weights per vertex
+ * @param mesh the mesh
+ * @param offsetMatrices the offsetMaytrices to apply
+ * @param tb the tangent vertexBuffer
+ */
+ private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
+ int maxWeightsPerVert = mesh.getMaxNumWeights();
+
+ if (maxWeightsPerVert <= 0) {
+ throw new IllegalStateException("Max weights per vert is incorrectly set!");
+ }
+
+ int fourMinusMaxWeights = 4 - maxWeightsPerVert;
+
+ // NOTE: This code assumes the vertex buffer is in bind pose
+ // resetToBind() has been called this frame
+ VertexBuffer vb = mesh.getBuffer(Type.Position);
+ FloatBuffer fvb = (FloatBuffer) vb.getData();
+ fvb.rewind();
+
+ VertexBuffer nb = mesh.getBuffer(Type.Normal);
+
+ FloatBuffer fnb = (FloatBuffer) nb.getData();
+ fnb.rewind();
+
+
+ FloatBuffer ftb = (FloatBuffer) tb.getData();
+ ftb.rewind();
+
+
+ // get boneIndexes and weights for mesh
+ ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+ FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+ ib.rewind();
+ wb.rewind();
+
+ float[] weights = wb.array();
+ byte[] indices = ib.array();
+ int idxWeights = 0;
+
+ TempVars vars = TempVars.get();
+
+
+ float[] posBuf = vars.skinPositions;
+ float[] normBuf = vars.skinNormals;
+ float[] tanBuf = vars.skinTangents;
+
+ int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
+ int bufLength = 0;
+ int tanLength = 0;
+ for (int i = iterations - 1; i >= 0; i--) {
+ // read next set of positions and normals from native buffer
+ bufLength = Math.min(posBuf.length, fvb.remaining());
+ tanLength = Math.min(tanBuf.length, ftb.remaining());
+ fvb.get(posBuf, 0, bufLength);
+ fnb.get(normBuf, 0, bufLength);
+ ftb.get(tanBuf, 0, tanLength);
+ int verts = bufLength / 3;
+ int idxPositions = 0;
+ //tangents has their own index because of the 4 components
+ int idxTangents = 0;
+
+ // iterate vertices and apply skinning transform for each effecting bone
+ for (int vert = verts - 1; vert >= 0; vert--) {
+ float nmx = normBuf[idxPositions];
+ float vtx = posBuf[idxPositions++];
+ float nmy = normBuf[idxPositions];
+ float vty = posBuf[idxPositions++];
+ float nmz = normBuf[idxPositions];
+ float vtz = posBuf[idxPositions++];
+
+ float tnx = tanBuf[idxTangents++];
+ float tny = tanBuf[idxTangents++];
+ float tnz = tanBuf[idxTangents++];
+
+ //skipping the 4th component of the tangent since it doesn't have to be transformed
+ idxTangents++;
+
+ float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0;
+
+ for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
+ float weight = weights[idxWeights];
+ Matrix4f mat = offsetMatrices[indices[idxWeights++]];
+
+ rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
+ ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
+ rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
+
+ rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
+ rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
+ rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
+
+ rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight;
+ rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight;
+ rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight;
+ }
+
+ idxWeights += fourMinusMaxWeights;
+
+ idxPositions -= 3;
+
+ normBuf[idxPositions] = rnx;
+ posBuf[idxPositions++] = rx;
+ normBuf[idxPositions] = rny;
+ posBuf[idxPositions++] = ry;
+ normBuf[idxPositions] = rnz;
+ posBuf[idxPositions++] = rz;
+
+ idxTangents -= 4;
+
+ tanBuf[idxTangents++] = rtx;
+ tanBuf[idxTangents++] = rty;
+ tanBuf[idxTangents++] = rtz;
+
+ //once again skipping the 4th component of the tangent
+ idxTangents++;
+ }
+
+ fvb.position(fvb.position() - bufLength);
+ fvb.put(posBuf, 0, bufLength);
+ fnb.position(fnb.position() - bufLength);
+ fnb.put(normBuf, 0, bufLength);
+ ftb.position(ftb.position() - tanLength);
+ ftb.put(tanBuf, 0, tanLength);
+ }
+
+ vars.release();
+
+ vb.updateData(fvb);
+ nb.updateData(fnb);
+ tb.updateData(ftb);
+
+
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(targets, "targets", null);
+ oc.write(skeleton, "skeleton", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule in = im.getCapsule(this);
+ Savable[] sav = in.readSavableArray("targets", null);
+ if (sav != null) {
+ targets = new Mesh[sav.length];
+ System.arraycopy(sav, 0, targets, 0, sav.length);
+ }
+ skeleton = (Skeleton) in.readSavable("skeleton", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/SpatialAnimation.java b/engine/src/core/com/jme3/animation/SpatialAnimation.java
new file mode 100644
index 0000000..54f5058
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/SpatialAnimation.java
@@ -0,0 +1,11 @@
+package com.jme3.animation;
+
+/**
+ * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
+ */
+@Deprecated
+public class SpatialAnimation extends Animation {
+ public SpatialAnimation(String name, float length) {
+ super(name, length);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/SpatialTrack.java b/engine/src/core/com/jme3/animation/SpatialTrack.java
new file mode 100644
index 0000000..5704317
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/SpatialTrack.java
@@ -0,0 +1,242 @@
+package com.jme3.animation;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * This class represents the track for spatial animation.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class SpatialTrack implements Track {
+
+ /**
+ * Translations of the track.
+ */
+ private CompactVector3Array translations;
+
+ /**
+ * Rotations of the track.
+ */
+ private CompactQuaternionArray rotations;
+
+ /**
+ * Scales of the track.
+ */
+ private CompactVector3Array scales;
+
+ /**
+ * The times of the animations frames.
+ */
+ private float[] times;
+
+ public SpatialTrack() {
+ }
+
+ /**
+ * Creates a spatial track for the given track data.
+ *
+ * @param times
+ * a float array with the time of each frame
+ * @param translations
+ * the translation of the bone for each frame
+ * @param rotations
+ * the rotation of the bone for each frame
+ * @param scales
+ * the scale of the bone for each frame
+ */
+ public SpatialTrack(float[] times, Vector3f[] translations,
+ Quaternion[] rotations, Vector3f[] scales) {
+ setKeyframes(times, translations, rotations, scales);
+ }
+
+ /**
+ *
+ * Modify the spatial which this track modifies.
+ *
+ * @param time
+ * the current time of the animation
+ */
+ public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
+ Spatial spatial = control.getSpatial();
+
+ Vector3f tempV = vars.vect1;
+ Vector3f tempS = vars.vect2;
+ Quaternion tempQ = vars.quat1;
+ Vector3f tempV2 = vars.vect3;
+ Vector3f tempS2 = vars.vect4;
+ Quaternion tempQ2 = vars.quat2;
+
+ int lastFrame = times.length - 1;
+ if (time < 0 || lastFrame == 0) {
+ if (rotations != null)
+ rotations.get(0, tempQ);
+ if (translations != null)
+ translations.get(0, tempV);
+ if (scales != null) {
+ scales.get(0, tempS);
+ }
+ } else if (time >= times[lastFrame]) {
+ if (rotations != null)
+ rotations.get(lastFrame, tempQ);
+ if (translations != null)
+ translations.get(lastFrame, tempV);
+ if (scales != null) {
+ scales.get(lastFrame, tempS);
+ }
+ } else {
+ int startFrame = 0;
+ int endFrame = 1;
+ // use lastFrame so we never overflow the array
+ for (int i = 0; i < lastFrame && times[i] < time; ++i) {
+ startFrame = i;
+ endFrame = i + 1;
+ }
+
+ float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]);
+
+ if (rotations != null)
+ rotations.get(startFrame, tempQ);
+ if (translations != null)
+ translations.get(startFrame, tempV);
+ if (scales != null) {
+ scales.get(startFrame, tempS);
+ }
+ if (rotations != null)
+ rotations.get(endFrame, tempQ2);
+ if (translations != null)
+ translations.get(endFrame, tempV2);
+ if (scales != null) {
+ scales.get(endFrame, tempS2);
+ }
+ tempQ.nlerp(tempQ2, blend);
+ tempV.interpolate(tempV2, blend);
+ tempS.interpolate(tempS2, blend);
+ }
+
+ if (translations != null)
+ spatial.setLocalTranslation(tempV);
+ if (rotations != null)
+ spatial.setLocalRotation(tempQ);
+ if (scales != null) {
+ spatial.setLocalScale(tempS);
+ }
+ }
+
+ /**
+ * Set the translations, rotations and scales for this track.
+ *
+ * @param times
+ * a float array with the time of each frame
+ * @param translations
+ * the translation of the bone for each frame
+ * @param rotations
+ * the rotation of the bone for each frame
+ * @param scales
+ * the scale of the bone for each frame
+ */
+ public void setKeyframes(float[] times, Vector3f[] translations,
+ Quaternion[] rotations, Vector3f[] scales) {
+ if (times.length == 0) {
+ throw new RuntimeException("BoneTrack with no keyframes!");
+ }
+
+ this.times = times;
+ if (translations != null) {
+ assert times.length == translations.length;
+ this.translations = new CompactVector3Array();
+ this.translations.add(translations);
+ this.translations.freeze();
+ }
+ if (rotations != null) {
+ assert times.length == rotations.length;
+ this.rotations = new CompactQuaternionArray();
+ this.rotations.add(rotations);
+ this.rotations.freeze();
+ }
+ if (scales != null) {
+ assert times.length == scales.length;
+ this.scales = new CompactVector3Array();
+ this.scales.add(scales);
+ this.scales.freeze();
+ }
+ }
+
+ /**
+ * @return the array of rotations of this track
+ */
+ public Quaternion[] getRotations() {
+ return rotations == null ? null : rotations.toObjectArray();
+ }
+
+ /**
+ * @return the array of scales for this track
+ */
+ public Vector3f[] getScales() {
+ return scales == null ? null : scales.toObjectArray();
+ }
+
+ /**
+ * @return the arrays of time for this track
+ */
+ public float[] getTimes() {
+ return times;
+ }
+
+ /**
+ * @return the array of translations of this track
+ */
+ public Vector3f[] getTranslations() {
+ return translations == null ? null : translations.toObjectArray();
+ }
+
+ /**
+ * @return the length of the track
+ */
+ public float getLength() {
+ return times == null ? 0 : times[times.length - 1] - times[0];
+ }
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ @Override
+ public SpatialTrack clone() {
+ int tablesLength = times.length;
+
+ float[] timesCopy = this.times.clone();
+ Vector3f[] translationsCopy = this.getTranslations() == null ? null : Arrays.copyOf(this.getTranslations(), tablesLength);
+ Quaternion[] rotationsCopy = this.getRotations() == null ? null : Arrays.copyOf(this.getRotations(), tablesLength);
+ Vector3f[] scalesCopy = this.getScales() == null ? null : Arrays.copyOf(this.getScales(), tablesLength);
+
+ //need to use the constructor here because of the final fields used in this class
+ return new SpatialTrack(timesCopy, translationsCopy, rotationsCopy, scalesCopy);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(translations, "translations", null);
+ oc.write(rotations, "rotations", null);
+ oc.write(times, "times", null);
+ oc.write(scales, "scales", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ translations = (CompactVector3Array) ic.readSavable("translations", null);
+ rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
+ times = ic.readFloatArray("times", null);
+ scales = (CompactVector3Array) ic.readSavable("scales", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/Track.java b/engine/src/core/com/jme3/animation/Track.java
new file mode 100644
index 0000000..a56807d
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Track.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.Savable;
+import com.jme3.util.TempVars;
+
+public interface Track extends Savable, Cloneable {
+
+ /**
+ * Sets the time of the animation.
+ *
+ * Internally, the track will retrieve objects from the control
+ * and modify them according to the properties of the channel and the
+ * given parameters.
+ *
+ * @param time The time in the animation
+ * @param weight The weight from 0 to 1 on how much to apply the track
+ * @param control The control which the track should effect
+ * @param channel The channel which the track should effect
+ */
+ public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars);
+
+ /**
+ * @return the length of the track
+ */
+ public float getLength();
+
+ /**
+ * This method creates a clone of the current object.
+ * @return a clone of the current object
+ */
+ public Track clone();
+}
diff --git a/engine/src/core/com/jme3/animation/package.html b/engine/src/core/com/jme3/animation/package.html
new file mode 100644
index 0000000..554bab9
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/package.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.animation</code> package contains various classes
+for managing animation inside a jME3 application. Currently, the majority
+of classes are for handling skeletal animation. The primary control class is
+the {@link com.jme3.animation.AnimControl}, through which animations can be played,
+looped, combined, transitioned, etc.
+
+<h3>Usage</h3>
+
+<p>
+<code>
+// Create or load a model with skeletal animation:<br>
+Spatial model = assetManager.loadModel("...");<br>
+<br>
+// Retrieve the AnimControl.<br>
+AnimControl animCtrl = model.getControl(AnimControl.class);<br>
+<br>
+// Create an animation channel, by default assigned to all bones.<br>
+AnimChannel animChan = animCtrl.createChannel();<br>
+<br>
+// Play an animation<br>
+animChan.setAnim("MyAnim");<br>
+</code>
+<br>
+<h3>Skeletal Animation System</h3>
+<br>
+<p>
+jME3 uses a system of bone-weights: A vertex is assigned up to 4 bones by which
+it is influenced and 4 weights that describe how much the bone influences the
+vertex. The maximum weight value being 1.0, and the requirement that all 4 weights
+for a given vertex <em>must</em> sum to 1.0. This data is specified
+for each skin/mesh that is influenced by the animation control via the
+{link com.jme3.scene.VertexBuffer}s <code>BoneWeight</code> and <code>BoneIndex</code>.
+The BoneIndex buffer must be of the format <code>UnsignedByte</code>, thus
+placing the limit of up to 256 bones for a skeleton. The BoneWeight buffer
+should be of the format <code>Float</code>. Both buffers should reference 4
+bones, even if the maximum number of bones any vertex is influenced is less or more
+than 4.<br>
+If a vertex is influenced by less than 4 bones, the indices following the last
+valid bone should be 0 and the weights following the last valid bone should be 0.0.
+The buffers are designed in such a way so as to permit hardware skinning.<br>
+<p>
+The {@link com.jme3.animation.Skeleton} class describes a bone heirarchy with one
+or more root bones having children, thus containing all bones of the skeleton.
+In addition to accessing the bones in the skeleton via the tree heirarchy, it
+is also possible to access bones via index. The index for any given bone is
+arbitrary and does not depend on the bone's location in the tree hierarchy.
+It is this index that is specified in the BoneIndex VertexBuffer mentioned above
+, and is also used to apply transformations to the bones through the animations.<br>
+<p>
+Every bone has a local and model space transformation. The local space
+transformation is relative to its parent, if it has one, otherwise it is relative
+to the model. The model space transformation is relative to model space.
+The bones additionally have a bind pose transformation, which describes
+the transformations for bones when no animated pose is applied to the skeleton.
+All bones <em>must</em> have a bind pose transformation before they can be
+animated. To set the bind pose for the skeleton, set the local (relative
+to parent) transformations for all the bones using the call
+{@link com.jme3.animation.Bone#setBindTransforms(com.jme3.math.Vector3f, com.jme3.math.Quaternion) }.
+Then call {@link com.jme3.animation.Skeleton#updateWorldVectors() } followed by
+{@link com.jme3.animation.Skeleton#setBindingPose() }. <br>
+<p>
+Animations are stored in a HashMap object, accessed by name. An animation
+is simply a list of tracks, each track describes a timeline with each keyframe
+having a transformation. For bone animations, every track is assigned to a bone,
+while for morph animations, every track is assigned to a mesh.<br>
+<p>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/app/AppTask.java b/engine/src/core/com/jme3/app/AppTask.java
new file mode 100644
index 0000000..1b1b68c
--- /dev/null
+++ b/engine/src/core/com/jme3/app/AppTask.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import java.util.concurrent.*;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>AppTask</code> is used in <code>AppTaskQueue</code> to manage tasks that have
+ * yet to be accomplished. The AppTask system is used to execute tasks either
+ * in the OpenGL/Render thread, or outside of it.
+ *
+ * @author Matthew D. Hicks, lazloh
+ */
+public class AppTask<V> implements Future<V> {
+ private static final Logger logger = Logger.getLogger(AppTask.class
+ .getName());
+
+ private final Callable<V> callable;
+
+ private V result;
+ private ExecutionException exception;
+ private boolean cancelled, finished;
+ private final ReentrantLock stateLock = new ReentrantLock();
+ private final Condition finishedCondition = stateLock.newCondition();
+
+ /**
+ * Create an <code>AppTask</code> that will execute the given
+ * {@link Callable}.
+ *
+ * @param callable The callable to be executed
+ */
+ public AppTask(Callable<V> callable) {
+ this.callable = callable;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ stateLock.lock();
+ try {
+ if (result != null) {
+ return false;
+ }
+ cancelled = true;
+
+ finishedCondition.signalAll();
+
+ return true;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public V get() throws InterruptedException, ExecutionException {
+ stateLock.lock();
+ try {
+ while (!isDone()) {
+ finishedCondition.await();
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ return result;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ stateLock.lock();
+ try {
+ if (!isDone()) {
+ finishedCondition.await(timeout, unit);
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ if (result == null) {
+ throw new TimeoutException("Object not returned in time allocated.");
+ }
+ return result;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public boolean isCancelled() {
+ stateLock.lock();
+ try {
+ return cancelled;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public boolean isDone() {
+ stateLock.lock();
+ try {
+ return finished || cancelled || (exception != null);
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public Callable<V> getCallable() {
+ return callable;
+ }
+
+ public void invoke() {
+ try {
+ final V tmpResult = callable.call();
+
+ stateLock.lock();
+ try {
+ result = tmpResult;
+ finished = true;
+
+ finishedCondition.signalAll();
+ } finally {
+ stateLock.unlock();
+ }
+ } catch (Exception e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "invoke()", "Exception", e);
+
+ stateLock.lock();
+ try {
+ exception = new ExecutionException(e);
+
+ finishedCondition.signalAll();
+ } finally {
+ stateLock.unlock();
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java
new file mode 100644
index 0000000..517ec61
--- /dev/null
+++ b/engine/src/core/com/jme3/app/Application.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app;
+
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioContext;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.Listener;
+import com.jme3.input.*;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The <code>Application</code> class represents an instance of a
+ * real-time 3D rendering jME application.
+ *
+ * An <code>Application</code> provides all the tools that are commonly used in jME3
+ * applications.
+ *
+ * jME3 applications should extend this class and call start() to begin the
+ * application.
+ *
+ */
+public class Application implements SystemListener {
+
+ private static final Logger logger = Logger.getLogger(Application.class.getName());
+
+ protected AssetManager assetManager;
+
+ protected AudioRenderer audioRenderer;
+ protected Renderer renderer;
+ protected RenderManager renderManager;
+ protected ViewPort viewPort;
+ protected ViewPort guiViewPort;
+
+ protected JmeContext context;
+ protected AppSettings settings;
+ protected Timer timer = new NanoTimer();
+ protected Camera cam;
+ protected Listener listener;
+
+ protected boolean inputEnabled = true;
+ protected boolean pauseOnFocus = true;
+ protected float speed = 1f;
+ protected boolean paused = false;
+ protected MouseInput mouseInput;
+ protected KeyInput keyInput;
+ protected JoyInput joyInput;
+ protected TouchInput touchInput;
+ protected InputManager inputManager;
+ protected AppStateManager stateManager;
+
+ private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
+
+ /**
+ * Create a new instance of <code>Application</code>.
+ */
+ public Application(){
+ initStateManager();
+ }
+
+ /**
+ * Returns true if pause on lost focus is enabled, false otherwise.
+ *
+ * @return true if pause on lost focus is enabled
+ *
+ * @see #setPauseOnLostFocus(boolean)
+ */
+ public boolean isPauseOnLostFocus() {
+ return pauseOnFocus;
+ }
+
+ /**
+ * Enable or disable pause on lost focus.
+ * <p>
+ * By default, pause on lost focus is enabled.
+ * If enabled, the application will stop updating
+ * when it loses focus or becomes inactive (e.g. alt-tab).
+ * For online or real-time applications, this might not be preferable,
+ * so this feature should be set to disabled. For other applications,
+ * it is best to keep it on so that CPU usage is not used when
+ * not necessary.
+ *
+ * @param pauseOnLostFocus True to enable pause on lost focus, false
+ * otherwise.
+ */
+ public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
+ this.pauseOnFocus = pauseOnLostFocus;
+ }
+
+ @Deprecated
+ public void setAssetManager(AssetManager assetManager){
+ if (this.assetManager != null)
+ throw new IllegalStateException("Can only set asset manager"
+ + " before initialization.");
+
+ this.assetManager = assetManager;
+ }
+
+ private void initAssetManager(){
+ if (settings != null){
+ String assetCfg = settings.getString("AssetConfigURL");
+ if (assetCfg != null){
+ URL url = null;
+ try {
+ url = new URL(assetCfg);
+ } catch (MalformedURLException ex) {
+ }
+ if (url == null) {
+ url = Application.class.getClassLoader().getResource(assetCfg);
+ if (url == null) {
+ logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
+ return;
+ }
+ }
+ assetManager = JmeSystem.newAssetManager(url);
+ }
+ }
+ if (assetManager == null){
+ assetManager = JmeSystem.newAssetManager(
+ Thread.currentThread().getContextClassLoader()
+ .getResource("com/jme3/asset/Desktop.cfg"));
+ }
+ }
+
+ /**
+ * Set the display settings to define the display created.
+ * <p>
+ * Examples of display parameters include display pixel width and height,
+ * color bit depth, z-buffer bits, anti-aliasing samples, and update frequency.
+ * If this method is called while the application is already running, then
+ * {@link #restart() } must be called to apply the settings to the display.
+ *
+ * @param settings The settings to set.
+ */
+ public void setSettings(AppSettings settings){
+ this.settings = settings;
+ if (context != null && settings.useInput() != inputEnabled){
+ // may need to create or destroy input based
+ // on settings change
+ inputEnabled = !inputEnabled;
+ if (inputEnabled){
+ initInput();
+ }else{
+ destroyInput();
+ }
+ }else{
+ inputEnabled = settings.useInput();
+ }
+ }
+
+ /**
+ * Sets the Timer implementation that will be used for calculating
+ * frame times. By default, Application will use the Timer as returned
+ * by the current JmeContext implementation.
+ */
+ public void setTimer(Timer timer){
+ this.timer = timer;
+
+ if (timer != null) {
+ timer.reset();
+ }
+
+ if (renderManager != null) {
+ renderManager.setTimer(timer);
+ }
+ }
+
+ public Timer getTimer(){
+ return timer;
+ }
+
+ private void initDisplay(){
+ // aquire important objects
+ // from the context
+ settings = context.getSettings();
+
+ // Only reset the timer if a user has not already provided one
+ if (timer == null) {
+ timer = context.getTimer();
+ }
+
+ renderer = context.getRenderer();
+ }
+
+ private void initAudio(){
+ if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){
+ audioRenderer = JmeSystem.newAudioRenderer(settings);
+ audioRenderer.initialize();
+ AudioContext.setAudioRenderer(audioRenderer);
+
+ listener = new Listener();
+ audioRenderer.setListener(listener);
+ }
+ }
+
+ /**
+ * Creates the camera to use for rendering. Default values are perspective
+ * projection with 45° field of view, with near and far values 1 and 1000
+ * units respectively.
+ */
+ private void initCamera(){
+ cam = new Camera(settings.getWidth(), settings.getHeight());
+
+ cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
+ cam.setLocation(new Vector3f(0f, 0f, 10f));
+ cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
+
+ renderManager = new RenderManager(renderer);
+ //Remy - 09/14/2010 setted the timer in the renderManager
+ renderManager.setTimer(timer);
+ viewPort = renderManager.createMainView("Default", cam);
+ viewPort.setClearFlags(true, true, true);
+
+ // Create a new cam for the gui
+ Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
+ guiViewPort = renderManager.createPostView("Gui Default", guiCam);
+ guiViewPort.setClearFlags(false, false, false);
+ }
+
+ /**
+ * Initializes mouse and keyboard input. Also
+ * initializes joystick input if joysticks are enabled in the
+ * AppSettings.
+ */
+ private void initInput(){
+ mouseInput = context.getMouseInput();
+ if (mouseInput != null)
+ mouseInput.initialize();
+
+ keyInput = context.getKeyInput();
+ if (keyInput != null)
+ keyInput.initialize();
+
+ touchInput = context.getTouchInput();
+ if (touchInput != null)
+ touchInput.initialize();
+
+ if (!settings.getBoolean("DisableJoysticks")){
+ joyInput = context.getJoyInput();
+ if (joyInput != null)
+ joyInput.initialize();
+ }
+
+ inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
+ }
+
+ private void initStateManager(){
+ stateManager = new AppStateManager(this);
+ }
+
+ /**
+ * @return The {@link AssetManager asset manager} for this application.
+ */
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ /**
+ * @return the {@link InputManager input manager}.
+ */
+ public InputManager getInputManager(){
+ return inputManager;
+ }
+
+ /**
+ * @return the {@link AppStateManager app state manager}
+ */
+ public AppStateManager getStateManager() {
+ return stateManager;
+ }
+
+ /**
+ * @return the {@link RenderManager render manager}
+ */
+ public RenderManager getRenderManager() {
+ return renderManager;
+ }
+
+ /**
+ * @return The {@link Renderer renderer} for the application
+ */
+ public Renderer getRenderer(){
+ return renderer;
+ }
+
+ /**
+ * @return The {@link AudioRenderer audio renderer} for the application
+ */
+ public AudioRenderer getAudioRenderer() {
+ return audioRenderer;
+ }
+
+ /**
+ * @return The {@link Listener listener} object for audio
+ */
+ public Listener getListener() {
+ return listener;
+ }
+
+ /**
+ * @return The {@link JmeContext display context} for the application
+ */
+ public JmeContext getContext(){
+ return context;
+ }
+
+ /**
+ * @return The {@link Camera camera} for the application
+ */
+ public Camera getCamera(){
+ return cam;
+ }
+
+ /**
+ * Starts the application in {@link Type#Display display} mode.
+ *
+ * @see #start(com.jme3.system.JmeContext.Type)
+ */
+ public void start(){
+ start(JmeContext.Type.Display);
+ }
+
+ /**
+ * Starts the application.
+ * Creating a rendering context and executing
+ * the main loop in a separate thread.
+ */
+ public void start(JmeContext.Type contextType){
+ if (context != null && context.isCreated()){
+ logger.warning("start() called when application already created!");
+ return;
+ }
+
+ if (settings == null){
+ settings = new AppSettings(true);
+ }
+
+ logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
+ context = JmeSystem.newContext(settings, contextType);
+ context.setSystemListener(this);
+ context.create(false);
+ }
+
+ /**
+ * Initializes the application's canvas for use.
+ * <p>
+ * After calling this method, cast the {@link #getContext() context} to
+ * {@link JmeCanvasContext},
+ * then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
+ * and attach it to an AWT/Swing Frame.
+ * The rendering thread will start when the canvas becomes visible on
+ * screen, however if you wish to start the context immediately you
+ * may call {@link #startCanvas() } to force the rendering thread
+ * to start.
+ *
+ * @see JmeCanvasContext
+ * @see Type#Canvas
+ */
+ public void createCanvas(){
+ if (context != null && context.isCreated()){
+ logger.warning("createCanvas() called when application already created!");
+ return;
+ }
+
+ if (settings == null){
+ settings = new AppSettings(true);
+ }
+
+ logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
+ context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
+ context.setSystemListener(this);
+ }
+
+ /**
+ * Starts the rendering thread after createCanvas() has been called.
+ * <p>
+ * Same as calling startCanvas(false)
+ *
+ * @see #startCanvas(boolean)
+ */
+ public void startCanvas(){
+ startCanvas(false);
+ }
+
+ /**
+ * Starts the rendering thread after createCanvas() has been called.
+ * <p>
+ * Calling this method is optional, the canvas will start automatically
+ * when it becomes visible.
+ *
+ * @param waitFor If true, the current thread will block until the
+ * rendering thread is running
+ */
+ public void startCanvas(boolean waitFor){
+ context.create(waitFor);
+ }
+
+ /**
+ * Internal use only.
+ */
+ public void reshape(int w, int h){
+ renderManager.notifyReshape(w, h);
+ }
+
+ /**
+ * Restarts the context, applying any changed settings.
+ * <p>
+ * Changes to the {@link AppSettings} of this Application are not
+ * applied immediately; calling this method forces the context
+ * to restart, applying the new settings.
+ */
+ public void restart(){
+ context.setSettings(settings);
+ context.restart();
+ }
+
+ /**
+ * Requests the context to close, shutting down the main loop
+ * and making necessary cleanup operations.
+ *
+ * Same as calling stop(false)
+ *
+ * @see #stop(boolean)
+ */
+ public void stop(){
+ stop(false);
+ }
+
+ /**
+ * Requests the context to close, shutting down the main loop
+ * and making necessary cleanup operations.
+ * After the application has stopped, it cannot be used anymore.
+ */
+ public void stop(boolean waitFor){
+ logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
+ context.destroy(waitFor);
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ * <p>
+ * Initializes the <code>Application</code>, by creating a display and
+ * default camera. If display settings are not specified, a default
+ * 640x480 display is created. Default values are used for the camera;
+ * perspective projection with 45° field of view, with near
+ * and far values 1 and 1000 units respectively.
+ */
+ public void initialize(){
+ if (assetManager == null){
+ initAssetManager();
+ }
+
+ initDisplay();
+ initCamera();
+
+ if (inputEnabled){
+ initInput();
+ }
+ initAudio();
+
+ // update timer so that the next delta is not too large
+// timer.update();
+ timer.reset();
+
+ // user code here..
+ }
+
+ /**
+ * Internal use only.
+ */
+ public void handleError(String errMsg, Throwable t){
+ logger.log(Level.SEVERE, errMsg, t);
+ // user should add additional code to handle the error.
+ stop(); // stop the application
+ }
+
+ /**
+ * Internal use only.
+ */
+ public void gainFocus(){
+ if (pauseOnFocus) {
+ paused = false;
+ context.setAutoFlushFrames(true);
+ if (inputManager != null) {
+ inputManager.reset();
+ }
+ }
+ }
+
+ /**
+ * Internal use only.
+ */
+ public void loseFocus(){
+ if (pauseOnFocus){
+ paused = true;
+ context.setAutoFlushFrames(false);
+ }
+ }
+
+ /**
+ * Internal use only.
+ */
+ public void requestClose(boolean esc){
+ context.destroy(false);
+ }
+
+ /**
+ * Enqueues a task/callable object to execute in the jME3
+ * rendering thread.
+ * <p>
+ * Callables are executed right at the beginning of the main loop.
+ * They are executed even if the application is currently paused
+ * or out of focus.
+ */
+ public <V> Future<V> enqueue(Callable<V> callable) {
+ AppTask<V> task = new AppTask<V>(callable);
+ taskQueue.add(task);
+ return task;
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ */
+ public void update(){
+ // Make sure the audio renderer is available to callables
+ AudioContext.setAudioRenderer(audioRenderer);
+
+ AppTask<?> task = taskQueue.poll();
+ toploop: do {
+ if (task == null) break;
+ while (task.isCancelled()) {
+ task = taskQueue.poll();
+ if (task == null) break toploop;
+ }
+ task.invoke();
+ } while (((task = taskQueue.poll()) != null));
+
+ if (speed == 0 || paused)
+ return;
+
+ timer.update();
+
+ if (inputEnabled){
+ inputManager.update(timer.getTimePerFrame());
+ }
+
+ if (audioRenderer != null){
+ audioRenderer.update(timer.getTimePerFrame());
+ }
+
+ // user code here..
+ }
+
+ protected void destroyInput(){
+ if (mouseInput != null)
+ mouseInput.destroy();
+
+ if (keyInput != null)
+ keyInput.destroy();
+
+ if (joyInput != null)
+ joyInput.destroy();
+
+ if (touchInput != null)
+ touchInput.destroy();
+
+ inputManager = null;
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ */
+ public void destroy(){
+ stateManager.cleanup();
+
+ destroyInput();
+ if (audioRenderer != null)
+ audioRenderer.cleanup();
+
+ timer.reset();
+ }
+
+ /**
+ * @return The GUI viewport. Which is used for the on screen
+ * statistics and FPS.
+ */
+ public ViewPort getGuiViewPort() {
+ return guiViewPort;
+ }
+
+ public ViewPort getViewPort() {
+ return viewPort;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/app/DebugKeysAppState.java b/engine/src/core/com/jme3/app/DebugKeysAppState.java
new file mode 100644
index 0000000..2317c28
--- /dev/null
+++ b/engine/src/core/com/jme3/app/DebugKeysAppState.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.util.BufferUtils;
+
+
+/**
+ * Registers a few keys that will dump debug information
+ * to the console.
+ *
+ * @author Paul Speed
+ */
+public class DebugKeysAppState extends AbstractAppState {
+
+ public static final String INPUT_MAPPING_CAMERA_POS = "SIMPLEAPP_CameraPos";
+ public static final String INPUT_MAPPING_MEMORY = "SIMPLEAPP_Memory";
+
+ private Application app;
+ private DebugKeyListener keyListener = new DebugKeyListener();
+ private InputManager inputManager;
+
+ public DebugKeysAppState() {
+ }
+
+ @Override
+ public void initialize(AppStateManager stateManager, Application app) {
+ super.initialize(stateManager, app);
+
+ this.app = app;
+ this.inputManager = app.getInputManager();
+
+ if (app.getInputManager() != null) {
+
+ inputManager.addMapping(INPUT_MAPPING_CAMERA_POS, new KeyTrigger(KeyInput.KEY_C));
+ inputManager.addMapping(INPUT_MAPPING_MEMORY, new KeyTrigger(KeyInput.KEY_M));
+
+ inputManager.addListener(keyListener,
+ INPUT_MAPPING_CAMERA_POS,
+ INPUT_MAPPING_MEMORY);
+ }
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+
+ if (inputManager.hasMapping(INPUT_MAPPING_CAMERA_POS))
+ inputManager.deleteMapping(INPUT_MAPPING_CAMERA_POS);
+ if (inputManager.hasMapping(INPUT_MAPPING_MEMORY))
+ inputManager.deleteMapping(INPUT_MAPPING_MEMORY);
+
+ inputManager.removeListener(keyListener);
+ }
+
+
+ private class DebugKeyListener implements ActionListener {
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (!value) {
+ return;
+ }
+
+ if (name.equals(INPUT_MAPPING_CAMERA_POS)) {
+ Camera cam = app.getCamera();
+ if (cam != null) {
+ Vector3f loc = cam.getLocation();
+ Quaternion rot = cam.getRotation();
+ System.out.println("Camera Position: ("
+ + loc.x + ", " + loc.y + ", " + loc.z + ")");
+ System.out.println("Camera Rotation: " + rot);
+ System.out.println("Camera Direction: " + cam.getDirection());
+ }
+ } else if (name.equals(INPUT_MAPPING_MEMORY)) {
+ BufferUtils.printCurrentDirectMemory(null);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/app/FlyCamAppState.java b/engine/src/core/com/jme3/app/FlyCamAppState.java
new file mode 100644
index 0000000..5a7b11e
--- /dev/null
+++ b/engine/src/core/com/jme3/app/FlyCamAppState.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.input.FlyByCamera;
+
+
+/**
+ * Manages a FlyByCamera.
+ *
+ * @author Paul Speed
+ */
+public class FlyCamAppState extends AbstractAppState {
+
+ private Application app;
+ private FlyByCamera flyCam;
+
+ public FlyCamAppState() {
+ }
+
+ /**
+ * This is called by SimpleApplication during initialize().
+ */
+ void setCamera( FlyByCamera cam ) {
+ this.flyCam = cam;
+ }
+
+ public FlyByCamera getCamera() {
+ return flyCam;
+ }
+
+ @Override
+ public void initialize(AppStateManager stateManager, Application app) {
+ super.initialize(stateManager, app);
+
+ this.app = app;
+
+ if (app.getInputManager() != null) {
+
+ if (flyCam == null) {
+ flyCam = new FlyByCamera(app.getCamera());
+ }
+
+ flyCam.registerWithInput(app.getInputManager());
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ flyCam.setEnabled(enabled);
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+
+ flyCam.unregisterInput();
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/app/SimpleApplication.java b/engine/src/core/com/jme3/app/SimpleApplication.java
new file mode 100644
index 0000000..c79ce83
--- /dev/null
+++ b/engine/src/core/com/jme3/app/SimpleApplication.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import com.jme3.app.state.AppState;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.input.FlyByCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystem;
+import com.jme3.util.BufferUtils;
+
+/**
+ * <code>SimpleApplication</code> extends the {@link com.jme3.app.Application}
+ * class to provide default functionality like a first-person camera,
+ * and an accessible root node that is updated and rendered regularly.
+ * Additionally, <code>SimpleApplication</code> will display a statistics view
+ * using the {@link com.jme3.app.StatsView} class. It will display
+ * the current frames-per-second value on-screen in addition to the statistics.
+ * Several keys have special functionality in <code>SimpleApplication</code>:<br/>
+ *
+ * <table>
+ * <tr><td>Esc</td><td>- Close the application</td></tr>
+ * <tr><td>C</td><td>- Display the camera position and rotation in the console.</td></tr>
+ * <tr><td>M</td><td>- Display memory usage in the console.</td></tr>
+ * </table>
+ */
+public abstract class SimpleApplication extends Application {
+
+ public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit";
+ public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS;
+ public static final String INPUT_MAPPING_MEMORY = DebugKeysAppState.INPUT_MAPPING_MEMORY;
+ public static final String INPUT_MAPPING_HIDE_STATS = "SIMPLEAPP_HideStats";
+
+ protected Node rootNode = new Node("Root Node");
+ protected Node guiNode = new Node("Gui Node");
+ protected BitmapText fpsText;
+ protected BitmapFont guiFont;
+ protected FlyByCamera flyCam;
+ protected boolean showSettings = true;
+ private AppActionListener actionListener = new AppActionListener();
+
+ private class AppActionListener implements ActionListener {
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (!value) {
+ return;
+ }
+
+ if (name.equals(INPUT_MAPPING_EXIT)) {
+ stop();
+ }else if (name.equals(INPUT_MAPPING_HIDE_STATS)){
+ if (stateManager.getState(StatsAppState.class) != null) {
+ stateManager.getState(StatsAppState.class).toggleStats();
+ }
+ }
+ }
+ }
+
+ public SimpleApplication() {
+ this( new StatsAppState(), new FlyCamAppState(), new DebugKeysAppState() );
+ }
+
+ public SimpleApplication( AppState... initialStates ) {
+ super();
+
+ if (initialStates != null) {
+ for (AppState a : initialStates) {
+ if (a != null) {
+ stateManager.attach(a);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void start() {
+ // set some default settings in-case
+ // settings dialog is not shown
+ boolean loadSettings = false;
+ if (settings == null) {
+ setSettings(new AppSettings(true));
+ loadSettings = true;
+ }
+
+ // show settings dialog
+ if (showSettings) {
+ if (!JmeSystem.showSettingsDialog(settings, loadSettings)) {
+ return;
+ }
+ }
+ //re-setting settings they can have been merged from the registry.
+ setSettings(settings);
+ super.start();
+ }
+
+ /**
+ * Retrieves flyCam
+ * @return flyCam Camera object
+ *
+ */
+ public FlyByCamera getFlyByCamera() {
+ return flyCam;
+ }
+
+ /**
+ * Retrieves guiNode
+ * @return guiNode Node object
+ *
+ */
+ public Node getGuiNode() {
+ return guiNode;
+ }
+
+ /**
+ * Retrieves rootNode
+ * @return rootNode Node object
+ *
+ */
+ public Node getRootNode() {
+ return rootNode;
+ }
+
+ public boolean isShowSettings() {
+ return showSettings;
+ }
+
+ /**
+ * Toggles settings window to display at start-up
+ * @param showSettings Sets true/false
+ *
+ */
+ public void setShowSettings(boolean showSettings) {
+ this.showSettings = showSettings;
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+
+ // Several things rely on having this
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+
+ guiNode.setQueueBucket(Bucket.Gui);
+ guiNode.setCullHint(CullHint.Never);
+ viewPort.attachScene(rootNode);
+ guiViewPort.attachScene(guiNode);
+
+ if (inputManager != null) {
+
+ // We have to special-case the FlyCamAppState because too
+ // many SimpleApplication subclasses expect it to exist in
+ // simpleInit(). But at least it only gets initialized if
+ // the app state is added.
+ if (stateManager.getState(FlyCamAppState.class) != null) {
+ flyCam = new FlyByCamera(cam);
+ flyCam.setMoveSpeed(1f); // odd to set this here but it did it before
+ stateManager.getState(FlyCamAppState.class).setCamera( flyCam );
+ }
+
+ if (context.getType() == Type.Display) {
+ inputManager.addMapping(INPUT_MAPPING_EXIT, new KeyTrigger(KeyInput.KEY_ESCAPE));
+ }
+
+ if (stateManager.getState(StatsAppState.class) != null) {
+ inputManager.addMapping(INPUT_MAPPING_HIDE_STATS, new KeyTrigger(KeyInput.KEY_F5));
+ inputManager.addListener(actionListener, INPUT_MAPPING_HIDE_STATS);
+ }
+
+ inputManager.addListener(actionListener, INPUT_MAPPING_EXIT);
+ }
+
+ if (stateManager.getState(StatsAppState.class) != null) {
+ // Some of the tests rely on having access to fpsText
+ // for quick display. Maybe a different way would be better.
+ stateManager.getState(StatsAppState.class).setFont(guiFont);
+ fpsText = stateManager.getState(StatsAppState.class).getFpsText();
+ }
+
+ // call user code
+ simpleInitApp();
+ }
+
+ @Override
+ public void update() {
+ super.update(); // makes sure to execute AppTasks
+ if (speed == 0 || paused) {
+ return;
+ }
+
+ float tpf = timer.getTimePerFrame() * speed;
+
+ // update states
+ stateManager.update(tpf);
+
+ // simple update and root node
+ simpleUpdate(tpf);
+
+ rootNode.updateLogicalState(tpf);
+ guiNode.updateLogicalState(tpf);
+
+ rootNode.updateGeometricState();
+ guiNode.updateGeometricState();
+
+ // Moving this here to make sure it is always done.
+ // Now the sets are cleared every frame (guaranteed)
+ // and more than one viewer can access the data. This
+ // used to be cleared by StatsView but then only StatsView
+ // could get accurate counts.
+ renderer.getStatistics().clearFrame();
+
+ // render states
+ stateManager.render(renderManager);
+ renderManager.render(tpf, context.isRenderable());
+ simpleRender(renderManager);
+ stateManager.postRender();
+ }
+
+ public void setDisplayFps(boolean show) {
+ if (stateManager.getState(StatsAppState.class) != null) {
+ stateManager.getState(StatsAppState.class).setDisplayFps(show);
+ }
+ }
+
+ public void setDisplayStatView(boolean show) {
+ if (stateManager.getState(StatsAppState.class) != null) {
+ stateManager.getState(StatsAppState.class).setDisplayStatView(show);
+ }
+ }
+
+ public abstract void simpleInitApp();
+
+ public void simpleUpdate(float tpf) {
+ }
+
+ public void simpleRender(RenderManager rm) {
+ }
+}
diff --git a/engine/src/core/com/jme3/app/StatsAppState.java b/engine/src/core/com/jme3/app/StatsAppState.java
new file mode 100644
index 0000000..d4c968b
--- /dev/null
+++ b/engine/src/core/com/jme3/app/StatsAppState.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial.CullHint;
+
+
+/**
+ * Displays stats in SimpleApplication's GUI node or
+ * using the node and font parameters provided.
+ *
+ * @author Paul Speed
+ */
+public class StatsAppState extends AbstractAppState {
+
+ private Application app;
+ protected StatsView statsView;
+ protected boolean showSettings = true;
+ private boolean showFps = true;
+ private boolean showStats = true;
+
+ protected Node guiNode;
+ protected float secondCounter = 0.0f;
+ protected int frameCounter = 0;
+ protected BitmapText fpsText;
+ protected BitmapFont guiFont;
+
+ public StatsAppState() {
+ }
+
+ public StatsAppState( Node guiNode, BitmapFont guiFont ) {
+ this.guiNode = guiNode;
+ this.guiFont = guiFont;
+ }
+
+ /**
+ * Called by SimpleApplication to provide an early font
+ * so that the fpsText can be created before init. This
+ * is because several applications expect to directly access
+ * fpsText... unfortunately.
+ */
+ void setFont( BitmapFont guiFont ) {
+ this.guiFont = guiFont;
+ this.fpsText = new BitmapText(guiFont, false);
+ }
+
+ public BitmapText getFpsText() {
+ return fpsText;
+ }
+
+ public StatsView getStatsView() {
+ return statsView;
+ }
+
+ public float getSecondCounter() {
+ return secondCounter;
+ }
+
+ public void toggleStats() {
+ setDisplayFps( !showFps );
+ setDisplayStatView( !showStats );
+ }
+
+ public void setDisplayFps(boolean show) {
+ showFps = show;
+ if (fpsText != null) {
+ fpsText.setCullHint(show ? CullHint.Never : CullHint.Always);
+ }
+ }
+
+ public void setDisplayStatView(boolean show) {
+ showStats = show;
+ if (statsView != null ) {
+ statsView.setEnabled(show);
+ statsView.setCullHint(show ? CullHint.Never : CullHint.Always);
+ }
+ }
+
+ @Override
+ public void initialize(AppStateManager stateManager, Application app) {
+ super.initialize(stateManager, app);
+ this.app = app;
+
+ if (app instanceof SimpleApplication) {
+ SimpleApplication simpleApp = (SimpleApplication)app;
+ if (guiNode == null)
+ guiNode = simpleApp.guiNode;
+ if (guiFont == null )
+ guiFont = simpleApp.guiFont;
+ }
+
+ if (guiNode == null) {
+ throw new RuntimeException( "No guiNode specific and cannot be automatically determined." );
+ }
+
+ if (guiFont == null) {
+ guiFont = app.getAssetManager().loadFont("Interface/Fonts/Default.fnt");
+ }
+
+ loadFpsText();
+ loadStatsView();
+ }
+
+ /**
+ * Attaches FPS statistics to guiNode and displays it on the screen.
+ *
+ */
+ public void loadFpsText() {
+ if (fpsText == null) {
+ fpsText = new BitmapText(guiFont, false);
+ }
+
+ fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0);
+ fpsText.setText("Frames per second");
+ fpsText.setCullHint(showFps ? CullHint.Never : CullHint.Always);
+ guiNode.attachChild(fpsText);
+ }
+
+ /**
+ * Attaches Statistics View to guiNode and displays it on the screen
+ * above FPS statistics line.
+ *
+ */
+ public void loadStatsView() {
+ statsView = new StatsView("Statistics View",
+ app.getAssetManager(),
+ app.getRenderer().getStatistics());
+ // move it up so it appears above fps text
+ statsView.setLocalTranslation(0, fpsText.getLineHeight(), 0);
+ statsView.setEnabled(showStats);
+ statsView.setCullHint(showStats ? CullHint.Never : CullHint.Always);
+ guiNode.attachChild(statsView);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ if (enabled) {
+ fpsText.setCullHint(showFps ? CullHint.Never : CullHint.Always);
+ statsView.setEnabled(showStats);
+ statsView.setCullHint(showStats ? CullHint.Never : CullHint.Always);
+ } else {
+ fpsText.setCullHint(CullHint.Always);
+ statsView.setEnabled(false);
+ statsView.setCullHint(CullHint.Always);
+ }
+ }
+
+ @Override
+ public void update(float tpf) {
+ if (showFps) {
+ secondCounter += app.getTimer().getTimePerFrame();
+ frameCounter ++;
+ if (secondCounter >= 1.0f) {
+ int fps = (int) (frameCounter / secondCounter);
+ fpsText.setText("Frames per second: " + fps);
+ secondCounter = 0.0f;
+ frameCounter = 0;
+ }
+ }
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+
+ guiNode.detachChild(statsView);
+ guiNode.detachChild(fpsText);
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/app/StatsView.java b/engine/src/core/com/jme3/app/StatsView.java
new file mode 100644
index 0000000..49eeb13
--- /dev/null
+++ b/engine/src/core/com/jme3/app/StatsView.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Statistics;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+
+/**
+ * The <code>StatsView</code> provides a heads-up display (HUD) of various
+ * statistics of rendering. The data is retrieved every frame from a
+ * {@link com.jme3.renderer.Statistics} and then displayed on screen.<br/>
+ * <br/>
+ * Usage:<br/>
+ * To use the stats view, you need to retrieve the
+ * {@link com.jme3.renderer.Statistics} from the
+ * {@link com.jme3.renderer.Renderer} used by the application. Then, attach
+ * the <code>StatsView</code> to the scene graph.<br/>
+ * <code><br/>
+ * Statistics stats = renderer.getStatistics();<br/>
+ * StatsView statsView = new StatsView("MyStats", assetManager, stats);<br/>
+ * rootNode.attachChild(statsView);<br/>
+ * </code>
+ */
+public class StatsView extends Node implements Control {
+
+ private BitmapText[] labels;
+ private Statistics statistics;
+
+ private String[] statLabels;
+ private int[] statData;
+
+ private boolean enabled = true;
+
+ private final StringBuilder stringBuilder = new StringBuilder();
+
+ public StatsView(String name, AssetManager manager, Statistics stats){
+ super(name);
+
+ setQueueBucket(Bucket.Gui);
+ setCullHint(CullHint.Never);
+
+ statistics = stats;
+
+ statLabels = statistics.getLabels();
+ statData = new int[statLabels.length];
+ labels = new BitmapText[statLabels.length];
+
+ BitmapFont font = manager.loadFont("Interface/Fonts/Console.fnt");
+ for (int i = 0; i < labels.length; i++){
+ labels[i] = new BitmapText(font);
+ labels[i].setLocalTranslation(0, labels[i].getLineHeight() * (i+1), 0);
+ attachChild(labels[i]);
+ }
+
+ addControl(this);
+ }
+
+ public void update(float tpf) {
+
+ if (!isEnabled())
+ return;
+
+ statistics.getData(statData);
+ for (int i = 0; i < labels.length; i++) {
+ stringBuilder.setLength(0);
+ stringBuilder.append(statLabels[i]).append(" = ").append(statData[i]);
+ labels[i].setText(stringBuilder);
+ }
+
+ // Moved to SimpleApplication to make sure it is
+ // done even if there is no StatsView or the StatsView
+ // is disable.
+ //statistics.clearFrame();
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ return (Control) spatial;
+ }
+
+ public void setSpatial(Spatial spatial) {
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ }
+
+}
diff --git a/engine/src/core/com/jme3/app/package.html b/engine/src/core/com/jme3/app/package.html
new file mode 100644
index 0000000..ec6bb9a
--- /dev/null
+++ b/engine/src/core/com/jme3/app/package.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.application</code> provides a toolset for jME3 applications
+to interact with various components of the engine. Typically, the
+{@link com.jme3.app.Application} class will be extended and the update() method
+implemented to provide functionality for the main loop. <br>
+<p>
+An <code>Application</code> will typically provide the following services:
+<ul>
+ <li>{@link com.jme3.asset.AssetManager} - A system for finding and loading
+ data assets included with the application, such as models and textures.</li>
+ <li>{@link com.jme3.renderer.RenderManager} - A high-level rendering
+ interface for 3D graphics, manages viewports and scenes assigned
+ to the viewports, as well as general high-level rendering.</li>
+ <li>{@link com.jme3.input.InputManager} - An interface for handling input
+ from devices such as keyboard, mouse, and gamepad/joystick.</li>
+ <li>{@link com.jme3.app.state.AppStateManager} - Manager for
+ {@link com.jme3.app.state.AppState}s, which are specific application
+ functionality to be executed inside the main loop.</li>
+ <li>{@link com.jme3.audio.AudioRenderer} - Allows playing sound effects and
+ music.</li>
+ <li>{@link com.jme3.system.Timer} - The timer keeps track of time and allows
+ computing the time since the last frame (TPF) that is neccessary
+ for framerate-independent updates and motion.</li>
+ <li>{@link com.jme3.system.AppSettings} - A database containing various
+ settings for the application. These settings may be set by the user
+ or the application itself.</li>
+</ul>
+
+
+<h3>Usage</h3>
+
+An example use of the Application class is as follows<br>
+<br>
+
+<code>
+public class ExampleUse extends Application {<br>
+<br>
+ private Node rootNode = new Node("Root Node");<br>
+<br>
+ public static void main(String[] args){<br>
+ ExampleUse app = new ExampleUse();<br>
+ app.start();<br>
+ }<br>
+<br>
+ @Override<br>
+ public void initialize(){<br>
+ super.initialize();<br>
+<br>
+ // attach root node to viewport<br>
+ viewPort.attachScene(rootNode);<br>
+ }<br>
+<br>
+ @Override<br>
+ public void update(){<br>
+ super.update();<br>
+<br>
+ float tpf = timer.getTimePerFrame();<br>
+<br>
+ // update rootNode<br>
+ rootNode.updateLogicalState(tpf);<br>
+ rootNode.updateGeometricState();<br>
+<br>
+ // render the viewports<br>
+ renderManager.render(tpf);<br>
+ }<br>
+}<br>
+<br>
+</code>
+
+</body>
+</html>
+
diff --git a/engine/src/core/com/jme3/app/state/AbstractAppState.java b/engine/src/core/com/jme3/app/state/AbstractAppState.java
new file mode 100644
index 0000000..1ea1230
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/AbstractAppState.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.renderer.RenderManager;
+
+/**
+ * <code>AbstractAppState</code> implements some common methods
+ * that make creation of AppStates easier.
+ * @author Kirill Vainer
+ */
+public class AbstractAppState implements AppState {
+
+ /**
+ * <code>initialized</code> is set to true when the method
+ * {@link AbstractAppState#initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application) }
+ * is called. When {@link AbstractAppState#cleanup() } is called, <code>initialized</code>
+ * is set back to false.
+ */
+ protected boolean initialized = false;
+ private boolean enabled = true;
+
+ public void initialize(AppStateManager stateManager, Application app) {
+ initialized = true;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void stateAttached(AppStateManager stateManager) {
+ }
+
+ public void stateDetached(AppStateManager stateManager) {
+ }
+
+ public void update(float tpf) {
+ }
+
+ public void render(RenderManager rm) {
+ }
+
+ public void postRender(){
+ }
+
+ public void cleanup() {
+ initialized = false;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/app/state/AppState.java b/engine/src/core/com/jme3/app/state/AppState.java
new file mode 100644
index 0000000..d94a35f
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/AppState.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.renderer.RenderManager;
+
+/**
+ * AppState represents a continously executing code inside the main loop.
+ * An <code>AppState</code> can track when it is attached to the
+ * {@link AppStateManager} or when it is detached. <br/><code>AppState</code>s
+ * are initialized in the render thread, upon a call to {@link AppState#initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application) }
+ * and are de-initialized upon a call to {@link AppState#cleanup()}.
+ * Implementations should return the correct value with a call to
+ * {@link AppState#isInitialized() } as specified above.<br/>
+ *
+ *
+ * @author Kirill Vainer
+ */
+public interface AppState {
+
+ /**
+ * Called to initialize the AppState.
+ *
+ * @param stateManager The state manager
+ * @param app
+ */
+ public void initialize(AppStateManager stateManager, Application app);
+
+ /**
+ * @return True if <code>initialize()</code> was called on the state,
+ * false otherwise.
+ */
+ public boolean isInitialized();
+
+ /**
+ * Enable or disable the functionality of the <code>AppState</code>.
+ * The effect of this call depends on implementation. An
+ * <code>AppState</code> starts as being enabled by default.
+ *
+ * @param active activate the AppState or not.
+ */
+ public void setEnabled(boolean active);
+
+ /**
+ * @return True if the <code>AppState</code> is enabled, false otherwise.
+ *
+ * @see AppState#setEnabled(boolean)
+ */
+ public boolean isEnabled();
+ /**
+ * Called when the state was attached.
+ *
+ * @param stateManager State manager to which the state was attached to.
+ */
+ public void stateAttached(AppStateManager stateManager);
+
+ /**
+ * Called when the state was detached.
+ *
+ * @param stateManager The state manager from which the state was detached from.
+ */
+ public void stateDetached(AppStateManager stateManager);
+
+ /**
+ * Called to update the state.
+ *
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf);
+
+ /**
+ * Render the state.
+ *
+ * @param rm RenderManager
+ */
+ public void render(RenderManager rm);
+
+ /**
+ * Called after all rendering commands are flushed.
+ */
+ public void postRender();
+
+ /**
+ * Cleanup the game state.
+ */
+ public void cleanup();
+
+}
diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java
new file mode 100644
index 0000000..81228af
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/AppStateManager.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.renderer.RenderManager;
+import com.jme3.util.SafeArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The <code>AppStateManager</code> holds a list of {@link AppState}s which
+ * it will update and render.<br/>
+ * When an {@link AppState} is attached or detached, the
+ * {@link AppState#stateAttached(com.jme3.app.state.AppStateManager) } and
+ * {@link AppState#stateDetached(com.jme3.app.state.AppStateManager) } methods
+ * will be called respectively.
+ *
+ * <p>The lifecycle for an attached AppState is as follows:</p>
+ * <ul>
+ * <li>stateAttached() : called when the state is attached on the thread on which
+ * the state was attached.
+ * <li>initialize() : called ONCE on the render thread at the beginning of the next
+ * AppStateManager.update().
+ * <li>stateDetached() : called when the state is attached on the thread on which
+ * the state was detached. This is not necessarily on the
+ * render thread and it is not necessarily safe to modify
+ * the scene graph, etc..
+ * <li>cleanup() : called ONCE on the render thread at the beginning of the next update
+ * after the state has been detached or when the application is
+ * terminating.
+ * </ul>
+ *
+ * @author Kirill Vainer, Paul Speed
+ */
+public class AppStateManager {
+
+ /**
+ * List holding the attached app states that are pending
+ * initialization. Once initialized they will be added to
+ * the running app states.
+ */
+ private final SafeArrayList<AppState> initializing = new SafeArrayList<AppState>(AppState.class);
+
+ /**
+ * Holds the active states once they are initialized.
+ */
+ private final SafeArrayList<AppState> states = new SafeArrayList<AppState>(AppState.class);
+
+ /**
+ * List holding the detached app states that are pending
+ * cleanup.
+ */
+ private final SafeArrayList<AppState> terminating = new SafeArrayList<AppState>(AppState.class);
+
+ // All of the above lists need to be thread safe but access will be
+ // synchronized separately.... but always on the states list. This
+ // is to avoid deadlocking that may occur and the most common use case
+ // is that they are all modified from the same thread anyway.
+
+ private final Application app;
+ private AppState[] stateArray;
+
+ public AppStateManager(Application app){
+ this.app = app;
+ }
+
+ protected AppState[] getInitializing() {
+ synchronized (states){
+ return initializing.getArray();
+ }
+ }
+
+ protected AppState[] getTerminating() {
+ synchronized (states){
+ return terminating.getArray();
+ }
+ }
+
+ protected AppState[] getStates(){
+ synchronized (states){
+ return states.getArray();
+ }
+ }
+
+ /**
+ * Attach a state to the AppStateManager, the same state cannot be attached
+ * twice.
+ *
+ * @param state The state to attach
+ * @return True if the state was successfully attached, false if the state
+ * was already attached.
+ */
+ public boolean attach(AppState state){
+ synchronized (states){
+ if (!states.contains(state) && !initializing.contains(state)){
+ state.stateAttached(this);
+ initializing.add(state);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Detaches the state from the AppStateManager.
+ *
+ * @param state The state to detach
+ * @return True if the state was detached successfully, false
+ * if the state was not attached in the first place.
+ */
+ public boolean detach(AppState state){
+ synchronized (states){
+ if (states.contains(state)){
+ state.stateDetached(this);
+ states.remove(state);
+ terminating.add(state);
+ return true;
+ } else if(initializing.contains(state)){
+ state.stateDetached(this);
+ initializing.remove(state);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Check if a state is attached or not.
+ *
+ * @param state The state to check
+ * @return True if the state is currently attached to this AppStateManager.
+ *
+ * @see AppStateManager#attach(com.jme3.app.state.AppState)
+ */
+ public boolean hasState(AppState state){
+ synchronized (states){
+ return states.contains(state) || initializing.contains(state);
+ }
+ }
+
+ /**
+ * Returns the first state that is an instance of subclass of the specified class.
+ * @param <T>
+ * @param stateClass
+ * @return First attached state that is an instance of stateClass
+ */
+ public <T extends AppState> T getState(Class<T> stateClass){
+ synchronized (states){
+ AppState[] array = getStates();
+ for (AppState state : array) {
+ if (stateClass.isAssignableFrom(state.getClass())){
+ return (T) state;
+ }
+ }
+
+ // This may be more trouble than its worth but I think
+ // it's necessary for proper decoupling of states and provides
+ // similar behavior to before where a state could be looked
+ // up even if it wasn't initialized. -pspeed
+ array = getInitializing();
+ for (AppState state : array) {
+ if (stateClass.isAssignableFrom(state.getClass())){
+ return (T) state;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected void initializePending(){
+ AppState[] array = getInitializing();
+ synchronized( states ) {
+ // Move the states that will be initialized
+ // into the active array. In all but one case the
+ // order doesn't matter but if we do this here then
+ // a state can detach itself in initialize(). If we
+ // did it after then it couldn't.
+ List<AppState> transfer = Arrays.asList(array);
+ states.addAll(transfer);
+ initializing.removeAll(transfer);
+ }
+ for (AppState state : array) {
+ state.initialize(this, app);
+ }
+ }
+
+ protected void terminatePending(){
+ AppState[] array = getTerminating();
+ for (AppState state : array) {
+ state.cleanup();
+ }
+ synchronized( states ) {
+ // Remove just the states that were terminated...
+ // which might now be a subset of the total terminating
+ // list.
+ terminating.removeAll(Arrays.asList(array));
+ }
+ }
+
+ /**
+ * Calls update for attached states, do not call directly.
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf){
+
+ // Cleanup any states pending
+ terminatePending();
+
+ // Initialize any states pending
+ initializePending();
+
+ // Update enabled states
+ AppState[] array = getStates();
+ for (AppState state : array){
+ if (state.isEnabled()) {
+ state.update(tpf);
+ }
+ }
+ }
+
+ /**
+ * Calls render for all attached and initialized states, do not call directly.
+ * @param rm The RenderManager
+ */
+ public void render(RenderManager rm){
+ AppState[] array = getStates();
+ for (AppState state : array){
+ if (state.isEnabled()) {
+ state.render(rm);
+ }
+ }
+ }
+
+ /**
+ * Calls render for all attached and initialized states, do not call directly.
+ */
+ public void postRender(){
+ AppState[] array = getStates();
+ for (AppState state : array){
+ if (state.isEnabled()) {
+ state.postRender();
+ }
+ }
+ }
+
+ /**
+ * Calls cleanup on attached states, do not call directly.
+ */
+ public void cleanup(){
+ AppState[] array = getStates();
+ for (AppState state : array){
+ state.cleanup();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/app/state/package.html b/engine/src/core/com/jme3/app/state/package.html
new file mode 100644
index 0000000..0e93b38
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.app.state</code> package provides
+{@link com.jme3.app.state.AppState app states},
+an abstract way of handling application logic.
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/asset/Asset.java b/engine/src/core/com/jme3/asset/Asset.java
new file mode 100644
index 0000000..f36f963
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/Asset.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+/**
+ * Implementing the asset interface allows use of smart asset management.
+ * <p>
+ * Smart asset management requires cooperation from the {@link AssetKey}.
+ * In particular, the AssetKey should return true in its
+ * {@link AssetKey#useSmartCache() } method. Also smart assets MUST
+ * create a clone of the asset and cannot return the same reference,
+ * e.g. {@link AssetKey#createClonedInstance(java.lang.Object) createCloneInstance(someAsset)} <code>!= someAsset</code>.
+ * <p>
+ * If the {@link AssetManager#loadAsset(com.jme3.asset.AssetKey) } method
+ * is called twice with the same asset key (equals() wise, not necessarily reference wise)
+ * then both assets will have the same asset key set (reference wise) via
+ * {@link Asset#setKey(com.jme3.asset.AssetKey) }, then this asset key
+ * is used to track all instances of that asset. Once all clones of the asset
+ * are garbage collected, the shared asset key becomes unreachable and at that
+ * point it is removed from the smart asset cache.
+ */
+public interface Asset {
+
+ /**
+ * Set by the {@link AssetManager} to track this asset.
+ *
+ * Only clones of the asset has this set, the original copy that
+ * was loaded has this key set to null so that only the clones are tracked
+ * for garbage collection.
+ *
+ * @param key The AssetKey to set
+ */
+ public void setKey(AssetKey key);
+
+ /**
+ * Returns the asset key that is used to track this asset for garbage
+ * collection.
+ *
+ * @return the asset key that is used to track this asset for garbage
+ * collection.
+ */
+ public AssetKey getKey();
+}
diff --git a/engine/src/core/com/jme3/asset/AssetCache.java b/engine/src/core/com/jme3/asset/AssetCache.java
new file mode 100644
index 0000000..5dde799
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetCache.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.WeakHashMap;
+
+/**
+ * An <code>AssetCache</code> allows storage of loaded resources in order
+ * to improve their access time if they are requested again in a short period
+ * of time. The AssetCache stores weak references to the resources, allowing
+ * Java's garbage collector to request deletion of rarely used resources
+ * when heap memory is low.
+ */
+public class AssetCache {
+
+ public static final class SmartAssetInfo {
+ public WeakReference<AssetKey> smartKey;
+ public Asset asset;
+ }
+
+ private final WeakHashMap<AssetKey, SmartAssetInfo> smartCache
+ = new WeakHashMap<AssetKey, SmartAssetInfo>();
+ private final HashMap<AssetKey, Object> regularCache = new HashMap<AssetKey, Object>();
+
+ /**
+ * Adds a resource to the cache.
+ * <br/><br/>
+ * <font color="red">Thread-safe.</font>
+ * @see #getFromCache(java.lang.String)
+ */
+ public void addToCache(AssetKey key, Object obj){
+ synchronized (regularCache){
+ if (obj instanceof Asset && key.useSmartCache()){
+ // put in smart cache
+ Asset asset = (Asset) obj;
+ asset.setKey(null); // no circular references
+ SmartAssetInfo smartInfo = new SmartAssetInfo();
+ smartInfo.asset = asset;
+ // use the original key as smart key
+ smartInfo.smartKey = new WeakReference<AssetKey>(key);
+ smartCache.put(key, smartInfo);
+ }else{
+ // put in regular cache
+ regularCache.put(key, obj);
+ }
+ }
+ }
+
+ /**
+ * Delete an asset from the cache, returns true if it was deleted successfuly.
+ * <br/><br/>
+ * <font color="red">Thread-safe.</font>
+ */
+ public boolean deleteFromCache(AssetKey key){
+ synchronized (regularCache){
+ if (key.useSmartCache()){
+ return smartCache.remove(key) != null;
+ }else{
+ return regularCache.remove(key) != null;
+ }
+ }
+ }
+
+ /**
+ * Gets an object from the cache given an asset key.
+ * <br/><br/>
+ * <font color="red">Thread-safe.</font>
+ * @param key
+ * @return
+ */
+ public Object getFromCache(AssetKey key){
+ synchronized (regularCache){
+ if (key.useSmartCache()) {
+ return smartCache.get(key).asset;
+ } else {
+ return regularCache.get(key);
+ }
+ }
+ }
+
+ /**
+ * Retrieves smart asset info from the cache.
+ * @param key
+ * @return
+ */
+ public SmartAssetInfo getFromSmartCache(AssetKey key){
+ return smartCache.get(key);
+ }
+
+ /**
+ * Deletes all the assets in the regular cache.
+ */
+ public void deleteAllAssets(){
+ synchronized (regularCache){
+ regularCache.clear();
+ smartCache.clear();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/asset/AssetConfig.java b/engine/src/core/com/jme3/asset/AssetConfig.java
new file mode 100644
index 0000000..eaf2036
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetConfig.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>AssetConfig</code> loads a config file to configure the asset manager.
+ * <br/><br/>
+ * The config file is specified with the following format:
+ * <code>
+ * "LOADER" <class> : (<extension> ",")* <extension>
+ * "LOCATOR" <path> <class> : (<extension> ",")* <extension>
+ * </code>
+ *
+ * @author Kirill Vainer
+ */
+public class AssetConfig {
+
+ private AssetManager manager;
+
+ public AssetConfig(AssetManager manager){
+ this.manager = manager;
+ }
+
+ public void loadText(InputStream in) throws IOException{
+ Scanner scan = new Scanner(in);
+ while (scan.hasNext()){
+ String cmd = scan.next();
+ if (cmd.equals("LOADER")){
+ String loaderClass = scan.next();
+ String colon = scan.next();
+ if (!colon.equals(":")){
+ throw new IOException("Expected ':', got '"+colon+"'");
+ }
+ String extensionsList = scan.nextLine();
+ String[] extensions = extensionsList.split(",");
+ for (int i = 0; i < extensions.length; i++){
+ extensions[i] = extensions[i].trim();
+ }
+ if (hasClass(loaderClass)) {
+ manager.registerLoader(loaderClass, extensions);
+ } else {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot find loader {0}", loaderClass);
+ }
+ } else if (cmd.equals("LOCATOR")) {
+ String rootPath = scan.next();
+ String locatorClass = scan.nextLine().trim();
+ if (hasClass(locatorClass)) {
+ manager.registerLocator(rootPath, locatorClass);
+ } else {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot find locator {0}", locatorClass);
+ }
+ } else {
+ throw new IOException("Expected command, got '" + cmd + "'");
+ }
+ }
+ }
+
+ private boolean hasClass(String name) {
+ try {
+ Class clazz = Class.forName(name);
+ return clazz != null;
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+
+ private static String readString(DataInput dataIn) throws IOException{
+ int length = dataIn.readUnsignedShort();
+ char[] chrs = new char[length];
+ for (int i = 0; i < length; i++){
+ chrs[i] = (char) dataIn.readUnsignedByte();
+ }
+ return String.valueOf(chrs);
+ }
+
+ /*
+ public void loadBinary(DataInput dataIn) throws IOException{
+ // read signature and version
+
+ // how many locator entries?
+ int locatorEntries = dataIn.readUnsignedShort();
+ for (int i = 0; i < locatorEntries; i++){
+ String locatorClazz = readString(dataIn);
+ String rootPath = readString(dataIn);
+ manager.registerLocator(rootPath, locatorClazz);
+ }
+
+ int loaderEntries = dataIn.readUnsignedShort();
+ for (int i = 0; i < loaderEntries; i++){
+ String loaderClazz = readString(dataIn);
+ int numExtensions = dataIn.readUnsignedByte();
+ String[] extensions = new String[numExtensions];
+ for (int j = 0; j < numExtensions; j++){
+ extensions[j] = readString(dataIn);
+ }
+
+ manager.registerLoader(loaderClazz, extensions);
+ }
+ }
+ */
+}
diff --git a/engine/src/core/com/jme3/asset/AssetEventListener.java b/engine/src/core/com/jme3/asset/AssetEventListener.java
new file mode 100644
index 0000000..6907bcf
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetEventListener.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+/**
+ * <code>AssetEventListener</code> is an interface for listening to various
+ * events happening inside {@link AssetManager}. For now, it is possible
+ * to receive an event when an asset has been requested
+ * (one of the AssetManager.load***() methods were called), or when
+ * an asset has been loaded.
+ *
+ * @author Kirill Vainer
+ */
+public interface AssetEventListener {
+
+ /**
+ * Called when an asset has been successfully loaded (e.g: loaded from
+ * file system and parsed).
+ *
+ * @param key the AssetKey for the asset loaded.
+ */
+ public void assetLoaded(AssetKey key);
+
+ /**
+ * Called when an asset has been requested (e.g any of the load*** methods
+ * in AssetManager are called).
+ * In contrast to the assetLoaded() method, this one will be called even
+ * if the asset has failed to load, or if it was retrieved from the cache.
+ *
+ * @param key
+ */
+ public void assetRequested(AssetKey key);
+
+ /**
+ * Called when an asset dependency cannot be found for an asset.
+ * When an asset is loaded, each of its dependent assets that
+ * have failed to load due to a {@link AssetNotFoundException}, will cause
+ * an invocation of this callback.
+ *
+ * @param parentKey The key of the parent asset that is being loaded
+ * from within the user application.
+ * @param dependentAssetKey The asset key of the dependent asset that has
+ * failed to load.
+ */
+ public void assetDependencyNotFound(AssetKey parentKey, AssetKey dependentAssetKey);
+
+}
diff --git a/engine/src/core/com/jme3/asset/AssetInfo.java b/engine/src/core/com/jme3/asset/AssetInfo.java
new file mode 100644
index 0000000..42ee822
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetInfo.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.io.InputStream;
+
+/**
+ * The result of locating an asset through an AssetKey. Provides
+ * a means to read the asset data through an InputStream.
+ *
+ * @author Kirill Vainer
+ */
+public abstract class AssetInfo {
+
+ protected AssetManager manager;
+ protected AssetKey key;
+
+ public AssetInfo(AssetManager manager, AssetKey key) {
+ this.manager = manager;
+ this.key = key;
+ }
+
+ public AssetKey getKey() {
+ return key;
+ }
+
+ public AssetManager getManager() {
+ return manager;
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getName() + "[" + "key=" + key + "]";
+ }
+
+ /**
+ * Implementations of this method should return an {@link InputStream}
+ * allowing access to the data represented by the {@link AssetKey}.
+ * <p>
+ * Each invocation of this method should return a new stream to the
+ * asset data, starting at the beginning of the file.
+ *
+ * @return The asset data.
+ */
+ public abstract InputStream openStream();
+
+}
diff --git a/engine/src/core/com/jme3/asset/AssetKey.java b/engine/src/core/com/jme3/asset/AssetKey.java
new file mode 100644
index 0000000..9b6f0cd
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetKey.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.LinkedList;
+
+/**
+ * <code>AssetKey</code> is a key that is used to
+ * look up a resource from a cache.
+ * This class should be immutable.
+ */
+public class AssetKey<T> implements Savable {
+
+ protected String name;
+ protected transient String folder;
+ protected transient String extension;
+
+ public AssetKey(String name){
+ this.name = reducePath(name);
+ this.extension = getExtension(this.name);
+ }
+
+ public AssetKey(){
+ }
+
+ protected static String getExtension(String name){
+ int idx = name.lastIndexOf('.');
+ //workaround for filenames ending with xml and another dot ending before that (my.mesh.xml)
+ if (name.toLowerCase().endsWith(".xml")) {
+ idx = name.substring(0, idx).lastIndexOf('.');
+ if (idx == -1) {
+ idx = name.lastIndexOf('.');
+ }
+ }
+ if (idx <= 0 || idx == name.length() - 1)
+ return "";
+ else
+ return name.substring(idx+1).toLowerCase();
+ }
+
+ protected static String getFolder(String name){
+ int idx = name.lastIndexOf('/');
+ if (idx <= 0 || idx == name.length() - 1)
+ return "";
+ else
+ return name.substring(0, idx+1);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return The extension of the <code>AssetKey</code>'s name. For example,
+ * the name "Interface/Logo/Monkey.png" has an extension of "png".
+ */
+ public String getExtension() {
+ return extension;
+ }
+
+ public String getFolder(){
+ if (folder == null)
+ folder = getFolder(name);
+
+ return folder;
+ }
+
+ /**
+ * Do any post-processing on the resource after it has been loaded.
+ * @param asset
+ */
+ public Object postProcess(Object asset){
+ return asset;
+ }
+
+ /**
+ * Create a new instance of the asset, based on a prototype that is stored
+ * in the cache. Implementations are allowed to return the given parameter
+ * as-is if it is considered that cloning is not necessary for that particular
+ * asset type.
+ *
+ * @param asset The asset to be cloned.
+ * @return The asset, possibly cloned.
+ */
+ public Object createClonedInstance(Object asset){
+ return asset;
+ }
+
+ /**
+ * @return True if the asset for this key should be cached. Subclasses
+ * should override this method if they want to override caching behavior.
+ */
+ public boolean shouldCache(){
+ return true;
+ }
+
+ /**
+ * @return Should return true, if the asset objects implement the "Asset"
+ * interface and want to be removed from the cache when no longer
+ * referenced in user-code.
+ */
+ public boolean useSmartCache(){
+ return false;
+ }
+
+ /**
+ * Removes all relative elements of a path (A/B/../C.png and A/./C.png).
+ * @param path The path containing relative elements
+ * @return A path without relative elements
+ */
+ public static String reducePath(String path) {
+ if (path == null || path.indexOf("./") == -1) {
+ return path;
+ }
+ String[] parts = path.split("/");
+ LinkedList<String> list = new LinkedList<String>();
+ for (int i = 0; i < parts.length; i++) {
+ String string = parts[i];
+ if (string.length() == 0 || string.equals(".")) {
+ //do nothing
+ } else if (string.equals("..")) {
+ if (list.size() > 0) {
+ list.removeLast();
+ } else {
+ throw new IllegalStateException("Relative path is outside assetmanager root!");
+ }
+ } else {
+ list.add(string);
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < list.size(); i++) {
+ String string = list.get(i);
+ if (i != 0) {
+ builder.append("/");
+ }
+ builder.append(string);
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public boolean equals(Object other){
+ if (!(other instanceof AssetKey)){
+ return false;
+ }
+ return name.equals(((AssetKey)other).name);
+ }
+
+ @Override
+ public int hashCode(){
+ return name.hashCode();
+ }
+
+ @Override
+ public String toString(){
+ return name;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ name = reducePath(ic.readString("name", null));
+ extension = getExtension(name);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/asset/AssetLoadException.java b/engine/src/core/com/jme3/asset/AssetLoadException.java
new file mode 100644
index 0000000..a0139aa
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetLoadException.java
@@ -0,0 +1,17 @@
+package com.jme3.asset;
+
+/**
+ * <code>AssetLoadException</code> is thrown when the {@link AssetManager}
+ * is able to find the requested asset, but there was a problem while loading
+ * it.
+ *
+ * @author Kirill Vainer
+ */
+public class AssetLoadException extends RuntimeException {
+ public AssetLoadException(String message){
+ super(message);
+ }
+ public AssetLoadException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/engine/src/core/com/jme3/asset/AssetLoader.java b/engine/src/core/com/jme3/asset/AssetLoader.java
new file mode 100644
index 0000000..4ebffc5
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetLoader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.io.IOException;
+
+/**
+ * An interface for asset loaders. An <code>AssetLoader</code> is responsible
+ * for loading a certain type of asset associated with file extension(s).
+ * The loader will load the data in the provided {@link AssetInfo} object by
+ * calling {@link AssetInfo#openStream() }, returning an object representing
+ * the parsed data.
+ */
+public interface AssetLoader {
+
+ /**
+ * Loads asset from the given input stream, parsing it into
+ * an application-usable object.
+ *
+ * @return An object representing the resource.
+ * @throws java.io.IOException If an I/O error occurs while loading
+ */
+ public Object load(AssetInfo assetInfo) throws IOException;
+}
diff --git a/engine/src/core/com/jme3/asset/AssetLocator.java b/engine/src/core/com/jme3/asset/AssetLocator.java
new file mode 100644
index 0000000..561e372
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetLocator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+/**
+ * <code>AssetLocator</code> is used to locate a resource based on an AssetKey.
+ *
+ * @author Kirill Vainer
+ */
+public interface AssetLocator {
+ /**
+ * @param rootPath The root path where to look for assets.
+ * Typically this method will only be called once per
+ * instance of an asset locator.
+ */
+ public void setRootPath(String rootPath);
+
+ /**
+ * Request to locate an asset. The asset key
+ * contains a name identifying the asset.
+ * If an asset was not found, null should be returned.
+ * The {@link AssetInfo} implementation provided should have a proper
+ * return value for its {@link AssetInfo#openStream() } method.
+ *
+ * @param manager
+ * @param key
+ * @return The {@link AssetInfo} that was located, or null if not found.
+ */
+ public AssetInfo locate(AssetManager manager, AssetKey key);
+}
diff --git a/engine/src/core/com/jme3/asset/AssetManager.java b/engine/src/core/com/jme3/asset/AssetManager.java
new file mode 100644
index 0000000..a863ebf
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetManager.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioKey;
+import com.jme3.font.BitmapFont;
+import com.jme3.material.Material;
+import com.jme3.scene.Spatial;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderKey;
+import com.jme3.texture.Texture;
+import java.util.List;
+
+/**
+ * <code>AssetManager</code> provides an interface for managing the data assets
+ * of a jME3 application.
+ */
+public interface AssetManager {
+
+ /**
+ * Adds a ClassLoader that is used to load *Classes* that are needed for Assets like j3o models.
+ * This does *not* allow loading assets from that classpath, use registerLocator for that.
+ * @param loader A ClassLoader that Classes in asset files can be loaded from
+ */
+ public void addClassLoader(ClassLoader loader);
+
+ /**
+ * Remove a ClassLoader from the list of registered ClassLoaders
+ */
+ public void removeClassLoader(ClassLoader loader);
+
+ /**
+ * Retrieve the list of registered ClassLoaders that are used for loading Classes from
+ * asset files.
+ */
+ public List<ClassLoader> getClassLoaders();
+
+ /**
+ * Registers a loader for the given extensions.
+ * @param loaderClassName
+ * @param extensions
+ */
+ public void registerLoader(String loaderClassName, String ... extensions);
+
+ /**
+ * Registers an {@link AssetLocator} by using a class name, instead of
+ * a class instance. See the {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
+ * method for more information.
+ *
+ * @param rootPath The root path from which to locate assets, implementation
+ * dependent.
+ * @param locatorClassName The full class name of the {@link AssetLocator}
+ * implementation.
+ */
+ public void registerLocator(String rootPath, String locatorClassName);
+
+ /**
+ *
+ * @param loaderClass
+ * @param extensions
+ */
+ public void registerLoader(Class<? extends AssetLoader> loaderClass, String ... extensions);
+
+ /**
+ * Registers the given locator class for locating assets with this
+ * <code>AssetManager</code>. {@link AssetLocator}s are invoked in the order
+ * they were registered, to locate the asset by the {@link AssetKey}.
+ * Once an {@link AssetLocator} returns a non-null AssetInfo, it is sent
+ * to the {@link AssetLoader} to load the asset.
+ * Once a locator is registered, it can be removed via
+ * {@link #unregisterLocator(java.lang.String, java.lang.Class) }.
+ *
+ * @param rootPath Specifies the root path from which to locate assets
+ * for the given {@link AssetLocator}. The purpose of this parameter
+ * depends on the type of the {@link AssetLocator}.
+ * @param locatorClass The class type of the {@link AssetLocator} to register.
+ *
+ * @see AssetLocator#setRootPath(java.lang.String)
+ * @see AssetLocator#locate(com.jme3.asset.AssetManager, com.jme3.asset.AssetKey)
+ * @see #unregisterLocator(java.lang.String, java.lang.Class)
+ */
+ public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass);
+
+ /**
+ * Unregisters the given locator class. This essentially undoes the operation
+ * done by {@link #registerLocator(java.lang.String, java.lang.Class) }.
+ *
+ * @param rootPath Should be the same as the root path specified in {@link
+ * #registerLocator(java.lang.String, java.lang.Class) }.
+ * @param locatorClass The locator class to unregister
+ */
+ public void unregisterLocator(String rootPath, Class<? extends AssetLocator> locatorClass);
+
+ /**
+ * Set an {@link AssetEventListener} to receive events from this
+ * <code>AssetManager</code>. There can only be one {@link AssetEventListener}
+ * associated with an <code>AssetManager</code>
+ *
+ * @param listener
+ */
+ public void setAssetEventListener(AssetEventListener listener);
+
+ /**
+ * Manually locates an asset with the given {@link AssetKey}. This method
+ * should be used for debugging or internal uses. <br/>
+ * The call will attempt to locate the asset by invoking the
+ * {@link AssetLocator} that are registered with this <code>AssetManager</code>,
+ * in the same way that the {@link AssetManager#loadAsset(com.jme3.asset.AssetKey) }
+ * method locates assets.
+ *
+ * @param key The {@link AssetKey} to locate.
+ * @return The {@link AssetInfo} object returned from the {@link AssetLocator}
+ * that located the asset, or null if the asset cannot be located.
+ */
+ public AssetInfo locateAsset(AssetKey<?> key);
+
+ /**
+ * Load an asset from a key, the asset will be located
+ * by one of the {@link AssetLocator} implementations provided in the
+ * {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
+ * call. If located successfully, it will be loaded via the the appropriate
+ * {@link AssetLoader} implementation based on the file's extension, as
+ * specified in the call
+ * {@link AssetManager#registerLoader(java.lang.Class, java.lang.String[]) }.
+ *
+ * @param <T> The object type that will be loaded from the AssetKey instance.
+ * @param key The AssetKey
+ * @return The loaded asset, or null if it was failed to be located
+ * or loaded.
+ */
+ public <T> T loadAsset(AssetKey<T> key);
+
+ /**
+ * Load a named asset by name, calling this method
+ * is the same as calling
+ * <code>
+ * loadAsset(new AssetKey(name)).
+ * </code>
+ *
+ * @param name The name of the asset to load.
+ * @return The loaded asset, or null if failed to be loaded.
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Object loadAsset(String name);
+
+ /**
+ * Loads texture file, supported types are BMP, JPG, PNG, GIF,
+ * TGA and DDS.
+ *
+ * @param key The {@link TextureKey} to use for loading.
+ * @return The loaded texture, or null if failed to be loaded.
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Texture loadTexture(TextureKey key);
+
+ /**
+ * Loads texture file, supported types are BMP, JPG, PNG, GIF,
+ * TGA and DDS.
+ *
+ * @param name The name of the texture to load.
+ * @return The texture that was loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Texture loadTexture(String name);
+
+ /**
+ * Load audio file, supported types are WAV or OGG.
+ * @param key
+ * @return The audio data loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public AudioData loadAudio(AudioKey key);
+
+ /**
+ * Load audio file, supported types are WAV or OGG.
+ * The file is loaded without stream-mode.
+ * @param name
+ * @return The audio data loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public AudioData loadAudio(String name);
+
+ /**
+ * Loads a named model. Models can be jME3 object files (J3O) or
+ * OgreXML/OBJ files.
+ * @param key
+ * @return The model that was loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Spatial loadModel(ModelKey key);
+
+ /**
+ * Loads a named model. Models can be jME3 object files (J3O) or
+ * OgreXML/OBJ files.
+ * @param name
+ * @return The model that was loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Spatial loadModel(String name);
+
+ /**
+ * Load a material (J3M) file.
+ * @param name
+ * @return The material that was loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Material loadMaterial(String name);
+
+ /**
+ * Loads shader file(s), shouldn't be used by end-user in most cases.
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Shader loadShader(ShaderKey key);
+
+ /**
+ * Load a font file. Font files are in AngelCode text format,
+ * and are with the extension "fnt".
+ *
+ * @param name
+ * @return The font loaded
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public BitmapFont loadFont(String name);
+}
diff --git a/engine/src/core/com/jme3/asset/AssetNotFoundException.java b/engine/src/core/com/jme3/asset/AssetNotFoundException.java
new file mode 100644
index 0000000..e04a7fb
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetNotFoundException.java
@@ -0,0 +1,17 @@
+package com.jme3.asset;
+
+/**
+ * <code>AssetNotFoundException</code> is thrown when the {@link AssetManager}
+ * is unable to locate the requested asset using any of the registered
+ * {@link AssetLocator}s.
+ *
+ * @author Kirill Vainer
+ */
+public class AssetNotFoundException extends RuntimeException {
+ public AssetNotFoundException(String message){
+ super(message);
+ }
+ public AssetNotFoundException(String message, Exception ex){
+ super(message, ex);
+ }
+}
diff --git a/engine/src/core/com/jme3/asset/Desktop.cfg b/engine/src/core/com/jme3/asset/Desktop.cfg
new file mode 100644
index 0000000..93dafb7
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/Desktop.cfg
@@ -0,0 +1,22 @@
+LOCATOR / com.jme3.asset.plugins.ClasspathLocator
+
+LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg
+LOADER com.jme3.audio.plugins.WAVLoader : wav
+LOADER com.jme3.audio.plugins.OGGLoader : ogg
+LOADER com.jme3.material.plugins.J3MLoader : j3m
+LOADER com.jme3.material.plugins.J3MLoader : j3md
+LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
+LOADER com.jme3.texture.plugins.DDSLoader : dds
+LOADER com.jme3.texture.plugins.PFMLoader : pfm
+LOADER com.jme3.texture.plugins.HDRLoader : hdr
+LOADER com.jme3.texture.plugins.TGALoader : tga
+LOADER com.jme3.export.binary.BinaryImporter : j3o
+LOADER com.jme3.export.binary.BinaryImporter : j3f
+LOADER com.jme3.scene.plugins.OBJLoader : obj
+LOADER com.jme3.scene.plugins.MTLLoader : mtl
+LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml
+LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml
+LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
+LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
+LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
+LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib \ No newline at end of file
diff --git a/engine/src/core/com/jme3/asset/DesktopAssetManager.java b/engine/src/core/com/jme3/asset/DesktopAssetManager.java
new file mode 100644
index 0000000..e2d8ef8
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/DesktopAssetManager.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.asset.AssetCache.SmartAssetInfo;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioKey;
+import com.jme3.font.BitmapFont;
+import com.jme3.material.Material;
+import com.jme3.scene.Spatial;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderKey;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>AssetManager</code> is the primary method for managing and loading
+ * assets inside jME.
+ *
+ * @author Kirill Vainer
+ */
+public class DesktopAssetManager implements AssetManager {
+
+ private static final Logger logger = Logger.getLogger(AssetManager.class.getName());
+
+ private final AssetCache cache = new AssetCache();
+ private final ImplHandler handler = new ImplHandler(this);
+
+ private AssetEventListener eventListener = null;
+ private List<ClassLoader> classLoaders;
+
+// private final ThreadingManager threadingMan = new ThreadingManager(this);
+// private final Set<AssetKey> alreadyLoadingSet = new HashSet<AssetKey>();
+
+ public DesktopAssetManager(){
+ this(null);
+ }
+
+ @Deprecated
+ public DesktopAssetManager(boolean loadDefaults){
+ this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg"));
+ }
+
+ public DesktopAssetManager(URL configFile){
+ if (configFile != null){
+ InputStream stream = null;
+ try{
+ AssetConfig cfg = new AssetConfig(this);
+ stream = configFile.openStream();
+ cfg.loadText(stream);
+ }catch (IOException ex){
+ logger.log(Level.SEVERE, "Failed to load asset config", ex);
+ }finally{
+ if (stream != null)
+ try{
+ stream.close();
+ }catch (IOException ex){
+ }
+ }
+ }
+ logger.info("DesktopAssetManager created.");
+ }
+
+ public void addClassLoader(ClassLoader loader){
+ if(classLoaders == null)
+ classLoaders = Collections.synchronizedList(new ArrayList<ClassLoader>());
+ synchronized(classLoaders) {
+ classLoaders.add(loader);
+ }
+ }
+
+ public void removeClassLoader(ClassLoader loader){
+ if(classLoaders != null) synchronized(classLoaders) {
+ classLoaders.remove(loader);
+ }
+ }
+
+ public List<ClassLoader> getClassLoaders(){
+ return classLoaders;
+ }
+
+ public void setAssetEventListener(AssetEventListener listener){
+ eventListener = listener;
+ }
+
+ public void registerLoader(Class<? extends AssetLoader> loader, String ... extensions){
+ handler.addLoader(loader, extensions);
+ if (logger.isLoggable(Level.FINER)){
+ logger.log(Level.FINER, "Registered loader: {0} for extensions {1}",
+ new Object[]{loader.getSimpleName(), Arrays.toString(extensions)});
+ }
+ }
+
+ public void registerLoader(String clsName, String ... extensions){
+ Class<? extends AssetLoader> clazz = null;
+ try{
+ clazz = (Class<? extends AssetLoader>) Class.forName(clsName);
+ }catch (ClassNotFoundException ex){
+ logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex);
+ }catch (NoClassDefFoundError ex){
+ logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex);
+ }
+ if (clazz != null){
+ registerLoader(clazz, extensions);
+ }
+ }
+
+ public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass){
+ handler.addLocator(locatorClass, rootPath);
+ if (logger.isLoggable(Level.FINER)){
+ logger.log(Level.FINER, "Registered locator: {0}",
+ locatorClass.getSimpleName());
+ }
+ }
+
+ public void registerLocator(String rootPath, String clsName){
+ Class<? extends AssetLocator> clazz = null;
+ try{
+ clazz = (Class<? extends AssetLocator>) Class.forName(clsName);
+ }catch (ClassNotFoundException ex){
+ logger.log(Level.WARNING, "Failed to find locator: "+clsName, ex);
+ }catch (NoClassDefFoundError ex){
+ logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex);
+ }
+ if (clazz != null){
+ registerLocator(rootPath, clazz);
+ }
+ }
+
+ public void unregisterLocator(String rootPath, Class<? extends AssetLocator> clazz){
+ handler.removeLocator(clazz, rootPath);
+ if (logger.isLoggable(Level.FINER)){
+ logger.log(Level.FINER, "Unregistered locator: {0}",
+ clazz.getSimpleName());
+ }
+ }
+
+ public void clearCache(){
+ cache.deleteAllAssets();
+ }
+
+ /**
+ * Delete an asset from the cache, returns true if it was deleted
+ * successfully.
+ * <br/><br/>
+ * <font color="red">Thread-safe.</font>
+ */
+ public boolean deleteFromCache(AssetKey key){
+ return cache.deleteFromCache(key);
+ }
+
+ /**
+ * Adds a resource to the cache.
+ * <br/><br/>
+ * <font color="red">Thread-safe.</font>
+ */
+ public void addToCache(AssetKey key, Object asset){
+ cache.addToCache(key, asset);
+ }
+
+ public AssetInfo locateAsset(AssetKey<?> key){
+ if (handler.getLocatorCount() == 0){
+ logger.warning("There are no locators currently"+
+ " registered. Use AssetManager."+
+ "registerLocator() to register a"+
+ " locator.");
+ return null;
+ }
+
+ AssetInfo info = handler.tryLocate(key);
+ if (info == null){
+ logger.log(Level.WARNING, "Cannot locate resource: {0}", key);
+ }
+
+ return info;
+ }
+
+ /**
+ * <font color="red">Thread-safe.</font>
+ *
+ * @param <T>
+ * @param key
+ * @return
+ */
+ public <T> T loadAsset(AssetKey<T> key){
+ if (key == null)
+ throw new IllegalArgumentException("key cannot be null");
+
+ if (eventListener != null)
+ eventListener.assetRequested(key);
+
+ AssetKey smartKey = null;
+ Object o = null;
+ if (key.shouldCache()){
+ if (key.useSmartCache()){
+ SmartAssetInfo smartInfo = cache.getFromSmartCache(key);
+ if (smartInfo != null){
+ smartKey = smartInfo.smartKey.get();
+ if (smartKey != null){
+ o = smartInfo.asset;
+ }
+ }
+ }else{
+ o = cache.getFromCache(key);
+ }
+ }
+ if (o == null){
+ AssetLoader loader = handler.aquireLoader(key);
+ if (loader == null){
+ throw new IllegalStateException("No loader registered for type \"" +
+ key.getExtension() + "\"");
+ }
+
+ if (handler.getLocatorCount() == 0){
+ throw new IllegalStateException("There are no locators currently"+
+ " registered. Use AssetManager."+
+ "registerLocator() to register a"+
+ " locator.");
+ }
+
+ AssetInfo info = handler.tryLocate(key);
+ if (info == null){
+ if (handler.getParentKey() != null && eventListener != null){
+ // Inform event listener that an asset has failed to load.
+ // If the parent AssetLoader chooses not to propagate
+ // the exception, this is the only means of finding
+ // that something went wrong.
+ eventListener.assetDependencyNotFound(handler.getParentKey(), key);
+ }
+ throw new AssetNotFoundException(key.toString());
+ }
+
+ try {
+ handler.establishParentKey(key);
+ o = loader.load(info);
+ } catch (IOException ex) {
+ throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
+ } finally {
+ handler.releaseParentKey(key);
+ }
+ if (o == null){
+ throw new AssetLoadException("Error occured while loading asset \"" + key + "\" using" + loader.getClass().getSimpleName());
+ }else{
+ if (logger.isLoggable(Level.FINER)){
+ logger.log(Level.FINER, "Loaded {0} with {1}",
+ new Object[]{key, loader.getClass().getSimpleName()});
+ }
+
+ // do processing on asset before caching
+ o = key.postProcess(o);
+
+ if (key.shouldCache())
+ cache.addToCache(key, o);
+
+ if (eventListener != null)
+ eventListener.assetLoaded(key);
+ }
+ }
+
+ // object o is the asset
+ // create an instance for user
+ T clone = (T) key.createClonedInstance(o);
+
+ if (key.useSmartCache()){
+ if (smartKey != null){
+ // smart asset was already cached, use original key
+ ((Asset)clone).setKey(smartKey);
+ }else{
+ // smart asset was cached on this call, use our key
+ ((Asset)clone).setKey(key);
+ }
+ }
+
+ return clone;
+ }
+
+ public Object loadAsset(String name){
+ return loadAsset(new AssetKey(name));
+ }
+
+ /**
+ * Loads a texture.
+ *
+ * @return
+ */
+ public Texture loadTexture(TextureKey key){
+ return (Texture) loadAsset(key);
+ }
+
+ public Material loadMaterial(String name){
+ return (Material) loadAsset(new MaterialKey(name));
+ }
+
+ /**
+ * Loads a texture.
+ *
+ * @param name
+ * @param generateMipmaps Enable if applying texture to 3D objects, disable
+ * for GUI/HUD elements.
+ * @return
+ */
+ public Texture loadTexture(String name, boolean generateMipmaps){
+ TextureKey key = new TextureKey(name, true);
+ key.setGenerateMips(generateMipmaps);
+ key.setAsCube(false);
+ return loadTexture(key);
+ }
+
+ public Texture loadTexture(String name, boolean generateMipmaps, boolean flipY, boolean asCube, int aniso){
+ TextureKey key = new TextureKey(name, flipY);
+ key.setGenerateMips(generateMipmaps);
+ key.setAsCube(asCube);
+ key.setAnisotropy(aniso);
+ return loadTexture(key);
+ }
+
+ public Texture loadTexture(String name){
+ return loadTexture(name, true);
+ }
+
+ public AudioData loadAudio(AudioKey key){
+ return (AudioData) loadAsset(key);
+ }
+
+ public AudioData loadAudio(String name){
+ return loadAudio(new AudioKey(name, false));
+ }
+
+ /**
+ * Loads a bitmap font with the given name.
+ *
+ * @param name
+ * @return
+ */
+ public BitmapFont loadFont(String name){
+ return (BitmapFont) loadAsset(new AssetKey(name));
+ }
+
+ public InputStream loadGLSLLibrary(AssetKey key){
+ return (InputStream) loadAsset(key);
+ }
+
+ /**
+ * Load a vertex/fragment shader combo.
+ *
+ * @param key
+ * @return
+ */
+ public Shader loadShader(ShaderKey key){
+ // cache abuse in method
+ // that doesn't use loaders/locators
+ Shader s = (Shader) cache.getFromCache(key);
+ if (s == null){
+ String vertName = key.getVertName();
+ String fragName = key.getFragName();
+
+ String vertSource = (String) loadAsset(new AssetKey(vertName));
+ String fragSource = (String) loadAsset(new AssetKey(fragName));
+
+ s = new Shader(key.getLanguage());
+ s.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled());
+ s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled());
+
+ cache.addToCache(key, s);
+ }
+ return s;
+ }
+
+ public Spatial loadModel(ModelKey key){
+ return (Spatial) loadAsset(key);
+ }
+
+ /**
+ * Load a model.
+ *
+ * @param name
+ * @return
+ */
+ public Spatial loadModel(String name){
+ return loadModel(new ModelKey(name));
+ }
+
+}
diff --git a/engine/src/core/com/jme3/asset/ImplHandler.java b/engine/src/core/com/jme3/asset/ImplHandler.java
new file mode 100644
index 0000000..9b0b50a
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/ImplHandler.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>ImplHandler</code> manages the asset loader and asset locator
+ * implementations in a thread safe way. This allows implementations
+ * which store local persistent data to operate with a multi-threaded system.
+ * This is done by keeping an instance of each asset loader and asset
+ * locator object in a thread local.
+ */
+public class ImplHandler {
+
+ private static final Logger logger = Logger.getLogger(ImplHandler.class.getName());
+
+ private final AssetManager owner;
+
+ private final ThreadLocal<AssetKey> parentAssetKey
+ = new ThreadLocal<AssetKey>();
+
+ private final ArrayList<ImplThreadLocal> genericLocators =
+ new ArrayList<ImplThreadLocal>();
+
+ private final HashMap<String, ImplThreadLocal> loaders =
+ new HashMap<String, ImplThreadLocal>();
+
+ public ImplHandler(AssetManager owner){
+ this.owner = owner;
+ }
+
+ protected class ImplThreadLocal extends ThreadLocal {
+
+ private final Class<?> type;
+ private final String path;
+
+ public ImplThreadLocal(Class<?> type){
+ this.type = type;
+ path = null;
+ }
+
+ public ImplThreadLocal(Class<?> type, String path){
+ this.type = type;
+ this.path = path;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Class<?> getTypeClass(){
+ return type;
+ }
+
+ @Override
+ protected Object initialValue(){
+ try {
+ return type.newInstance();
+ } catch (InstantiationException ex) {
+ logger.log(Level.SEVERE,"Cannot create locator of type {0}, does"
+ + " the class have an empty and publically accessible"+
+ " constructor?", type.getName());
+ logger.throwing(type.getName(), "<init>", ex);
+ } catch (IllegalAccessException ex) {
+ logger.log(Level.SEVERE,"Cannot create locator of type {0}, "
+ + "does the class have an empty and publically "
+ + "accessible constructor?", type.getName());
+ logger.throwing(type.getName(), "<init>", ex);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Establishes the asset key that is used for tracking dependent assets
+ * that have failed to load. When set, the {@link DesktopAssetManager}
+ * gets a hint that it should suppress {@link AssetNotFoundException}s
+ * and instead call the listener callback (if set).
+ *
+ * @param parentKey The parent key
+ */
+ public void establishParentKey(AssetKey parentKey){
+ if (parentAssetKey.get() == null){
+ parentAssetKey.set(parentKey);
+ }
+ }
+
+ public void releaseParentKey(AssetKey parentKey){
+ if (parentAssetKey.get() == parentKey){
+ parentAssetKey.set(null);
+ }
+ }
+
+ public AssetKey getParentKey(){
+ return parentAssetKey.get();
+ }
+
+ /**
+ * Attempts to locate the given resource name.
+ * @param key The full name of the resource.
+ * @return The AssetInfo containing resource information required for
+ * access, or null if not found.
+ */
+ public AssetInfo tryLocate(AssetKey key){
+ synchronized (genericLocators){
+ if (genericLocators.isEmpty())
+ return null;
+
+ for (ImplThreadLocal local : genericLocators){
+ AssetLocator locator = (AssetLocator) local.get();
+ if (local.getPath() != null){
+ locator.setRootPath((String) local.getPath());
+ }
+ AssetInfo info = locator.locate(owner, key);
+ if (info != null)
+ return info;
+ }
+ }
+ return null;
+ }
+
+ public int getLocatorCount(){
+ synchronized (genericLocators){
+ return genericLocators.size();
+ }
+ }
+
+ /**
+ * Returns the AssetLoader registered for the given extension
+ * of the current thread.
+ * @return AssetLoader registered with addLoader.
+ */
+ public AssetLoader aquireLoader(AssetKey key){
+ synchronized (loaders){
+ ImplThreadLocal local = loaders.get(key.getExtension());
+ if (local != null){
+ AssetLoader loader = (AssetLoader) local.get();
+ return loader;
+ }
+ return null;
+ }
+ }
+
+ public void addLoader(final Class<?> loaderType, String ... extensions){
+ ImplThreadLocal local = new ImplThreadLocal(loaderType);
+ for (String extension : extensions){
+ extension = extension.toLowerCase();
+ synchronized (loaders){
+ loaders.put(extension, local);
+ }
+ }
+ }
+
+ public void addLocator(final Class<?> locatorType, String rootPath){
+ ImplThreadLocal local = new ImplThreadLocal(locatorType, rootPath);
+ synchronized (genericLocators){
+ genericLocators.add(local);
+ }
+ }
+
+ public void removeLocator(final Class<?> locatorType, String rootPath){
+ synchronized (genericLocators){
+ Iterator<ImplThreadLocal> it = genericLocators.iterator();
+ while (it.hasNext()){
+ ImplThreadLocal locator = it.next();
+ if (locator.getPath().equals(rootPath) &&
+ locator.getTypeClass().equals(locatorType)){
+ it.remove();
+ }
+ }
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/asset/MaterialKey.java b/engine/src/core/com/jme3/asset/MaterialKey.java
new file mode 100644
index 0000000..cc74fbc
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/MaterialKey.java
@@ -0,0 +1,29 @@
+package com.jme3.asset;
+
+import com.jme3.material.Material;
+
+/**
+ * Used for loading {@link Material materials} only (not material definitions).
+ *
+ * @author Kirill Vainer
+ */
+public class MaterialKey extends AssetKey {
+ public MaterialKey(String name){
+ super(name);
+ }
+
+ public MaterialKey(){
+ super();
+ }
+
+ @Override
+ public boolean useSmartCache(){
+ return true;
+ }
+
+ @Override
+ public Object createClonedInstance(Object asset){
+ Material mat = (Material) asset;
+ return mat.clone();
+ }
+}
diff --git a/engine/src/core/com/jme3/asset/ModelKey.java b/engine/src/core/com/jme3/asset/ModelKey.java
new file mode 100644
index 0000000..fcf5c53
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/ModelKey.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.scene.Spatial;
+
+/**
+ *
+ * @author Kirill Vainer
+ */
+public class ModelKey extends AssetKey<Spatial> {
+
+ public ModelKey(String name){
+ super(name);
+ }
+
+ public ModelKey(){
+ super();
+ }
+ @Override
+ public boolean useSmartCache(){
+ return true;
+ }
+
+ @Override
+ public Object createClonedInstance(Object asset){
+ Spatial model = (Spatial) asset;
+ return model.clone();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/asset/TextureKey.java b/engine/src/core/com/jme3/asset/TextureKey.java
new file mode 100644
index 0000000..118c84e
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/TextureKey.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.asset;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.texture.Texture.Type;
+import com.jme3.texture.*;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class TextureKey extends AssetKey<Texture> {
+
+ private boolean generateMips;
+ private boolean flipY;
+ private boolean asCube;
+ private boolean asTexture3D;
+ private int anisotropy;
+ private Texture.Type textureTypeHint=Texture.Type.TwoDimensional;
+
+ public TextureKey(String name, boolean flipY) {
+ super(name);
+ this.flipY = flipY;
+ }
+
+ public TextureKey(String name) {
+ super(name);
+ this.flipY = true;
+ }
+
+ public TextureKey() {
+ }
+
+ @Override
+ public String toString() {
+ return name + (flipY ? " (Flipped)" : "") + (asCube ? " (Cube)" : "") + (generateMips ? " (Mipmaped)" : "");
+ }
+
+ /**
+ * Enable smart caching for textures
+ * @return true to enable smart cache
+ */
+ @Override
+ public boolean useSmartCache() {
+ return true;
+ }
+
+ @Override
+ public Object createClonedInstance(Object asset) {
+ Texture tex = (Texture) asset;
+ return tex.createSimpleClone();
+ }
+
+ @Override
+ public Object postProcess(Object asset) {
+ Image img = (Image) asset;
+ if (img == null) {
+ return null;
+ }
+
+ Texture tex;
+ if (isAsCube()) {
+ if (isFlipY()) {
+ // also flip -y and +y image in cubemap
+ ByteBuffer pos_y = img.getData(2);
+ img.setData(2, img.getData(3));
+ img.setData(3, pos_y);
+ }
+ tex = new TextureCubeMap();
+ } else if (isAsTexture3D()) {
+ tex = new Texture3D();
+ } else {
+ tex = new Texture2D();
+ }
+
+ // enable mipmaps if image has them
+ // or generate them if requested by user
+ if (img.hasMipmaps() || isGenerateMips()) {
+ tex.setMinFilter(Texture.MinFilter.Trilinear);
+ }
+
+ tex.setAnisotropicFilter(getAnisotropy());
+ tex.setName(getName());
+ tex.setImage(img);
+ return tex;
+ }
+
+ public boolean isFlipY() {
+ return flipY;
+ }
+
+ public int getAnisotropy() {
+ return anisotropy;
+ }
+
+ public void setAnisotropy(int anisotropy) {
+ this.anisotropy = anisotropy;
+ }
+
+ public boolean isAsCube() {
+ return asCube;
+ }
+
+ public void setAsCube(boolean asCube) {
+ this.asCube = asCube;
+ }
+
+ public boolean isGenerateMips() {
+ return generateMips;
+ }
+
+ public void setGenerateMips(boolean generateMips) {
+ this.generateMips = generateMips;
+ }
+
+ public boolean isAsTexture3D() {
+ return asTexture3D;
+ }
+
+ public void setAsTexture3D(boolean asTexture3D) {
+ this.asTexture3D = asTexture3D;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof TextureKey)) {
+ return false;
+ }
+ return super.equals(other) && isFlipY() == ((TextureKey) other).isFlipY();
+ }
+
+ public Type getTextureTypeHint() {
+ return textureTypeHint;
+ }
+
+ public void setTextureTypeHint(Type textureTypeHint) {
+ this.textureTypeHint = textureTypeHint;
+ }
+
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(flipY, "flip_y", false);
+ oc.write(generateMips, "generate_mips", false);
+ oc.write(asCube, "as_cubemap", false);
+ oc.write(anisotropy, "anisotropy", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ flipY = ic.readBoolean("flip_y", false);
+ generateMips = ic.readBoolean("generate_mips", false);
+ asCube = ic.readBoolean("as_cubemap", false);
+ anisotropy = ic.readInt("anisotropy", 0);
+ }
+}
diff --git a/engine/src/core/com/jme3/asset/ThreadingManager.java b/engine/src/core/com/jme3/asset/ThreadingManager.java
new file mode 100644
index 0000000..6ed2c74
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/ThreadingManager.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * <code>ThreadingManager</code> manages the threads used to load content
+ * within the Content Manager system. A pool of threads and a task queue
+ * is used to load resource data and perform I/O while the application's
+ * render thread is active.
+ */
+public class ThreadingManager {
+
+ protected final ExecutorService executor =
+ Executors.newFixedThreadPool(2,
+ new LoadingThreadFactory());
+
+ protected final AssetManager owner;
+
+ protected int nextThreadId = 0;
+
+ public ThreadingManager(AssetManager owner){
+ this.owner = owner;
+ }
+
+ protected class LoadingThreadFactory implements ThreadFactory {
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r, "pool" + (nextThreadId++));
+ t.setDaemon(true);
+ t.setPriority(Thread.MIN_PRIORITY);
+ return t;
+ }
+ }
+
+ protected class LoadingTask implements Callable<Object> {
+ private final String resourceName;
+ public LoadingTask(String resourceName){
+ this.resourceName = resourceName;
+ }
+ public Object call() throws Exception {
+ return owner.loadAsset(new AssetKey(resourceName));
+ }
+ }
+
+// protected class MultiLoadingTask implements Callable<Void> {
+// private final String[] resourceNames;
+// public MultiLoadingTask(String[] resourceNames){
+// this.resourceNames = resourceNames;
+// }
+// public Void call(){
+// owner.loadContents(resourceNames);
+// return null;
+// }
+// }
+
+// public Future<Void> loadContents(String ... names){
+// return executor.submit(new MultiLoadingTask(names));
+// }
+
+// public Future<Object> loadContent(String name) {
+// return executor.submit(new LoadingTask(name));
+// }
+
+ public static boolean isLoadingThread() {
+ return Thread.currentThread().getName().startsWith("pool");
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/asset/package.html b/engine/src/core/com/jme3/asset/package.html
new file mode 100644
index 0000000..d9e1913
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/package.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+<code>com.jme3.asset</code> contains the {@link com.jme3.asset.AssetManager},
+a utility class that is used to load assets such as textures, models, and
+sound effects in a jME3 application. <br>
+
+<p>
+
+<h3>AssetLoaders</h3>
+{@link com.jme3.asset.AssetLoader asset loaders} are registered to load
+assets of a particular format. For example, an <code>AssetLoader</code> that
+loads TGA images should read a stream in .tga format and return an
+{@link com.jme3.texture.Image} object as its output.
+<code>AssetLoader</code>s are initialized once a file of that format
+is loaded, there's only one AssetLoader per thread so
+AssetLoader's load() method does not have to be thread safe.
+
+<h3>AssetLocators</h3>
+{@link com.jme3.asset.AssetLocators asset locators} are used to resolve
+an asset name (a string) into an {@link java.io.InputStream} which is
+contained in an {@link com.jme3.asset.AssetInfo} object.
+There are <code>AssetLocators</code> for loading files from the application's
+classpath, the local hard drive, a ZIP file, an HTTP server, and more. The user
+can implement their own AssetLocators and register them with the <code>AssetManager</code>
+to load their resources from their own location.
+
+
+</body>
+</html>
+
diff --git a/engine/src/core/com/jme3/audio/AudioBuffer.java b/engine/src/core/com/jme3/audio/AudioBuffer.java
new file mode 100644
index 0000000..7d29c49
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioBuffer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.audio.AudioData.DataType;
+import com.jme3.util.NativeObject;
+import java.nio.ByteBuffer;
+
+/**
+ * An <code>AudioBuffer</code> is an implementation of AudioData
+ * where the audio is buffered (stored in memory). All parts of it
+ * are accessible at any time. <br/>
+ * AudioBuffers are useful for short sounds, like effects, etc.
+ *
+ * @author Kirill Vainer
+ */
+public class AudioBuffer extends AudioData {
+
+ /**
+ * The audio data buffer. Should be direct and native ordered.
+ */
+ protected ByteBuffer audioData;
+
+ public AudioBuffer(){
+ super();
+ }
+
+ protected AudioBuffer(int id){
+ super(id);
+ }
+
+ public DataType getDataType() {
+ return DataType.Buffer;
+ }
+
+ /**
+ * @return The duration of the audio in seconds. It is expected
+ * that audio is uncompressed.
+ */
+ public float getDuration(){
+ int bytesPerSec = (bitsPerSample / 8) * channels * sampleRate;
+ if (audioData != null)
+ return (float) audioData.capacity() / bytesPerSec;
+ else
+ return Float.NaN; // unknown
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getSimpleName() +
+ "[id="+id+", ch="+channels+", bits="+bitsPerSample +
+ ", rate="+sampleRate+", duration="+getDuration()+"]";
+ }
+
+ /**
+ * Update the data in the buffer with new data.
+ * @param data
+ */
+ public void updateData(ByteBuffer data){
+ this.audioData = data;
+ updateNeeded = true;
+ }
+
+ /**
+ * @return The buffered audio data.
+ */
+ public ByteBuffer getData(){
+ return audioData;
+ }
+
+ public void resetObject() {
+ id = -1;
+ setUpdateNeeded();
+ }
+
+ public void deleteObject(AudioRenderer ar) {
+
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((AudioRenderer)rendererObject).deleteAudioData(this);
+ }
+
+ @Override
+ public NativeObject createDestructableClone() {
+ return new AudioBuffer(id);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioContext.java b/engine/src/core/com/jme3/audio/AudioContext.java
new file mode 100644
index 0000000..642f832
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioContext.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+/**
+ * Holds render thread specific audio context information.
+ *
+ * @author Paul Speed
+ */
+public class AudioContext {
+
+ private static ThreadLocal<AudioRenderer> audioRenderer = new ThreadLocal<AudioRenderer>();
+
+ public static void setAudioRenderer( AudioRenderer ar ) {
+ audioRenderer.set(ar);
+ }
+
+ public static AudioRenderer getAudioRenderer() {
+ return audioRenderer.get();
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/AudioData.java b/engine/src/core/com/jme3/audio/AudioData.java
new file mode 100644
index 0000000..186a734
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioData.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.util.NativeObject;
+
+/**
+ * <code>AudioData</code> is an abstract representation
+ * of audio data. There are two ways to handle audio data, short audio files
+ * are to be stored entirely in memory, while long audio files (music) are
+ * streamed from the hard drive as they are played.
+ *
+ * @author Kirill Vainer
+ */
+public abstract class AudioData extends NativeObject {
+
+ protected int sampleRate;
+ protected int channels;
+ protected int bitsPerSample;
+
+ public enum DataType {
+ Buffer,
+ Stream
+ }
+
+ public AudioData(){
+ super(AudioData.class);
+ }
+
+ protected AudioData(int id){
+ super(AudioData.class, id);
+ }
+
+ /**
+ * @return The data type, either <code>Buffer</code> or <code>Stream</code>.
+ */
+ public abstract DataType getDataType();
+
+ /**
+ * @return the duration in seconds of the audio clip.
+ */
+ public abstract float getDuration();
+
+ /**
+ * @return Bits per single sample from a channel.
+ */
+ public int getBitsPerSample() {
+ return bitsPerSample;
+ }
+
+ /**
+ * @return Number of channels. 1 for mono, 2 for stereo, etc.
+ */
+ public int getChannels() {
+ return channels;
+ }
+
+ /**
+ * @return The sample rate, or how many samples per second.
+ */
+ public int getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * Setup the format of the audio data.
+ * @param channels # of channels, 1 = mono, 2 = stereo
+ * @param bitsPerSample Bits per sample, e.g 8 bits, 16 bits.
+ * @param sampleRate Sample rate, 44100, 22050, etc.
+ */
+ public void setupFormat(int channels, int bitsPerSample, int sampleRate){
+ if (id != -1)
+ throw new IllegalStateException("Already set up");
+
+ this.channels = channels;
+ this.bitsPerSample = bitsPerSample;
+ this.sampleRate = sampleRate;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioKey.java b/engine/src/core/com/jme3/audio/AudioKey.java
new file mode 100644
index 0000000..e9492bb
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioKey.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * <code>AudioKey</code> is extending AssetKey by holding stream flag.
+ *
+ * @author Kirill Vainer
+ */
+public class AudioKey extends AssetKey<AudioData> {
+
+ private boolean stream;
+ private boolean streamCache;
+
+ /**
+ * Create a new AudioKey.
+ *
+ * @param name Name of the asset
+ * @param stream If true, the audio will be streamed from harddrive,
+ * otherwise it will be buffered entirely and then played.
+ * @param streamCache If stream is true, then this specifies if
+ * the stream cache is used. When enabled, the audio stream will
+ * be read entirely but not decoded, allowing features such as
+ * seeking, determining duration and looping.
+ */
+ public AudioKey(String name, boolean stream, boolean streamCache){
+ this(name, stream);
+ this.streamCache = streamCache;
+ }
+
+ /**
+ * Create a new AudioKey
+ *
+ * @param name Name of the asset
+ * @param stream If true, the audio will be streamed from harddrive,
+ * otherwise it will be buffered entirely and then played.
+ */
+ public AudioKey(String name, boolean stream){
+ super(name);
+ this.stream = stream;
+ }
+
+ public AudioKey(String name){
+ super(name);
+ this.stream = false;
+ }
+
+ public AudioKey(){
+ }
+
+ @Override
+ public String toString(){
+ return name + (stream ?
+ (streamCache ?
+ " (Stream/Cache)" :
+ " (Stream)") :
+ " (Buffer)");
+ }
+
+ /**
+ * @return True if the loaded audio should be a {@link AudioStream} or
+ * false if it should be a {@link AudioBuffer}.
+ */
+ public boolean isStream() {
+ return stream;
+ }
+
+ /**
+ * Specifies if the stream cache is used.
+ *
+ * When enabled, the audio stream will
+ * be read entirely but not decoded, allowing features such as
+ * seeking, looping and determining duration.
+ */
+ public boolean useStreamCache(){
+ return streamCache;
+ }
+
+ @Override
+ public boolean shouldCache(){
+ return !stream && !streamCache;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(stream, "do_stream", false);
+ oc.write(streamCache, "use_stream_cache", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ stream = ic.readBoolean("do_stream", false);
+ streamCache = ic.readBoolean("use_stream_cache", false);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioNode.java b/engine/src/core/com/jme3/audio/AudioNode.java
new file mode 100644
index 0000000..bc8b8cd
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioNode.java
@@ -0,0 +1,810 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.util.PlaceholderAssets;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * An <code>AudioNode</code> is used in jME3 for playing audio files.
+ * <br/>
+ * First, an {@link AudioNode} is loaded from file, and then assigned
+ * to an audio node for playback. Once the audio node is attached to the
+ * scene, its location will influence the position it is playing from relative
+ * to the {@link Listener}.
+ * <br/>
+ * An audio node can also play in "headspace", meaning its location
+ * or velocity does not influence how it is played.
+ * The "positional" property of an AudioNode can be set via
+ * {@link AudioNode#setPositional(boolean) }.
+ *
+ * @author normenhansen
+ * @author Kirill Vainer
+ */
+public class AudioNode extends Node {
+
+ protected boolean loop = false;
+ protected float volume = 1;
+ protected float pitch = 1;
+ protected float timeOffset = 0;
+ protected Filter dryFilter;
+ protected AudioKey audioKey;
+ protected transient AudioData data = null;
+ protected transient volatile Status status = Status.Stopped;
+ protected transient volatile int channel = -1;
+ protected Vector3f velocity = new Vector3f();
+ protected boolean reverbEnabled = true;
+ protected float maxDistance = 200; // 200 meters
+ protected float refDistance = 10; // 10 meters
+ protected Filter reverbFilter;
+ private boolean directional = false;
+ protected Vector3f direction = new Vector3f(0, 0, 1);
+ protected float innerAngle = 360;
+ protected float outerAngle = 360;
+ protected boolean positional = true;
+
+ /**
+ * <code>Status</code> indicates the current status of the audio node.
+ */
+ public enum Status {
+ /**
+ * The audio node is currently playing. This will be set if
+ * {@link AudioNode#play() } is called.
+ */
+ Playing,
+
+ /**
+ * The audio node is currently paused.
+ */
+ Paused,
+
+ /**
+ * The audio node is currently stopped.
+ * This will be set if {@link AudioNode#stop() } is called
+ * or the audio has reached the end of the file.
+ */
+ Stopped,
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> without any audio data set.
+ */
+ public AudioNode() {
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> without any audio data set.
+ *
+ * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+ *
+ * @deprecated AudioRenderer parameter is ignored.
+ */
+ public AudioNode(AudioRenderer audioRenderer) {
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given data and key.
+ *
+ * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+ * @param audioData The audio data contains the audio track to play.
+ * @param audioKey The audio key that was used to load the AudioData
+ *
+ * @deprecated AudioRenderer parameter is ignored.
+ */
+ public AudioNode(AudioRenderer audioRenderer, AudioData audioData, AudioKey audioKey) {
+ setAudioData(audioData, audioKey);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given data and key.
+ *
+ * @param audioData The audio data contains the audio track to play.
+ * @param audioKey The audio key that was used to load the AudioData
+ */
+ public AudioNode(AudioData audioData, AudioKey audioKey) {
+ setAudioData(audioData, audioKey);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given audio file.
+ *
+ * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+ * @param assetManager The asset manager to use to load the audio file
+ * @param name The filename of the audio file
+ * @param stream If true, the audio will be streamed gradually from disk,
+ * otherwise, it will be buffered.
+ * @param streamCache If stream is also true, then this specifies if
+ * the stream cache is used. When enabled, the audio stream will
+ * be read entirely but not decoded, allowing features such as
+ * seeking, looping and determining duration.
+ *
+ * @deprecated AudioRenderer parameter is ignored.
+ */
+ public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name, boolean stream, boolean streamCache) {
+ this.audioKey = new AudioKey(name, stream, streamCache);
+ this.data = (AudioData) assetManager.loadAsset(audioKey);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given audio file.
+ *
+ * @param assetManager The asset manager to use to load the audio file
+ * @param name The filename of the audio file
+ * @param stream If true, the audio will be streamed gradually from disk,
+ * otherwise, it will be buffered.
+ * @param streamCache If stream is also true, then this specifies if
+ * the stream cache is used. When enabled, the audio stream will
+ * be read entirely but not decoded, allowing features such as
+ * seeking, looping and determining duration.
+ */
+ public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) {
+ this.audioKey = new AudioKey(name, stream, streamCache);
+ this.data = (AudioData) assetManager.loadAsset(audioKey);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given audio file.
+ *
+ * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+ * @param assetManager The asset manager to use to load the audio file
+ * @param name The filename of the audio file
+ * @param stream If true, the audio will be streamed gradually from disk,
+ * otherwise, it will be buffered.
+ *
+ * @deprecated AudioRenderer parameter is ignored.
+ */
+ public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name, boolean stream) {
+ this(audioRenderer, assetManager, name, stream, false);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given audio file.
+ *
+ * @param assetManager The asset manager to use to load the audio file
+ * @param name The filename of the audio file
+ * @param stream If true, the audio will be streamed gradually from disk,
+ * otherwise, it will be buffered.
+ */
+ public AudioNode(AssetManager assetManager, String name, boolean stream) {
+ this(assetManager, name, stream, false);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given audio file.
+ *
+ * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+ * @param assetManager The asset manager to use to load the audio file
+ * @param name The filename of the audio file
+ *
+ * @deprecated AudioRenderer parameter is ignored.
+ */
+ public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) {
+ this(assetManager, name, false);
+ }
+
+ /**
+ * Creates a new <code>AudioNode</code> with the given audio file.
+ *
+ * @param assetManager The asset manager to use to load the audio file
+ * @param name The filename of the audio file
+ */
+ public AudioNode(AssetManager assetManager, String name) {
+ this(assetManager, name, false);
+ }
+
+ protected AudioRenderer getRenderer() {
+ AudioRenderer result = AudioContext.getAudioRenderer();
+ if( result == null )
+ throw new IllegalStateException( "No audio renderer available, make sure call is being performed on render thread." );
+ return result;
+ }
+
+ /**
+ * Start playing the audio.
+ */
+ public void play(){
+ getRenderer().playSource(this);
+ }
+
+ /**
+ * Start playing an instance of this audio. This method can be used
+ * to play the same <code>AudioNode</code> multiple times. Note
+ * that changes to the parameters of this AudioNode will not effect the
+ * instances already playing.
+ */
+ public void playInstance(){
+ getRenderer().playSourceInstance(this);
+ }
+
+ /**
+ * Stop playing the audio that was started with {@link AudioNode#play() }.
+ */
+ public void stop(){
+ getRenderer().stopSource(this);
+ }
+
+ /**
+ * Pause the audio that was started with {@link AudioNode#play() }.
+ */
+ public void pause(){
+ getRenderer().pauseSource(this);
+ }
+
+ /**
+ * Do not use.
+ */
+ public final void setChannel(int channel) {
+ if (status != Status.Stopped) {
+ throw new IllegalStateException("Can only set source id when stopped");
+ }
+
+ this.channel = channel;
+ }
+
+ /**
+ * Do not use.
+ */
+ public int getChannel() {
+ return channel;
+ }
+
+ /**
+ * @return The {#link Filter dry filter} that is set.
+ * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
+ */
+ public Filter getDryFilter() {
+ return dryFilter;
+ }
+
+ /**
+ * Set the dry filter to use for this audio node.
+ *
+ * When {@link AudioNode#setReverbEnabled(boolean) reverb} is used,
+ * the dry filter will only influence the "dry" portion of the audio,
+ * e.g. not the reverberated parts of the AudioNode playing.
+ *
+ * See the relevent documentation for the {@link Filter} to determine
+ * the effect.
+ *
+ * @param dryFilter The filter to set, or null to disable dry filter.
+ */
+ public void setDryFilter(Filter dryFilter) {
+ this.dryFilter = dryFilter;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.DryFilter);
+ }
+
+ /**
+ * Set the audio data to use for the audio. Note that this method
+ * can only be called once, if for example the audio node was initialized
+ * without an {@link AudioData}.
+ *
+ * @param audioData The audio data contains the audio track to play.
+ * @param audioKey The audio key that was used to load the AudioData
+ */
+ public void setAudioData(AudioData audioData, AudioKey audioKey) {
+ if (data != null) {
+ throw new IllegalStateException("Cannot change data once its set");
+ }
+
+ data = audioData;
+ this.audioKey = audioKey;
+ }
+
+ /**
+ * @return The {@link AudioData} set previously with
+ * {@link AudioNode#setAudioData(com.jme3.audio.AudioData, com.jme3.audio.AudioKey) }
+ * or any of the constructors that initialize the audio data.
+ */
+ public AudioData getAudioData() {
+ return data;
+ }
+
+ /**
+ * @return The {@link Status} of the audio node.
+ * The status will be changed when either the {@link AudioNode#play() }
+ * or {@link AudioNode#stop() } methods are called.
+ */
+ public Status getStatus() {
+ return status;
+ }
+
+ /**
+ * Do not use.
+ */
+ public final void setStatus(Status status) {
+ this.status = status;
+ }
+
+ /**
+ * @return True if the audio will keep looping after it is done playing,
+ * otherwise, false.
+ * @see AudioNode#setLooping(boolean)
+ */
+ public boolean isLooping() {
+ return loop;
+ }
+
+ /**
+ * Set the looping mode for the audio node. The default is false.
+ *
+ * @param loop True if the audio should keep looping after it is done playing.
+ */
+ public void setLooping(boolean loop) {
+ this.loop = loop;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.Looping);
+ }
+
+ /**
+ * @return The pitch of the audio, also the speed of playback.
+ *
+ * @see AudioNode#setPitch(float)
+ */
+ public float getPitch() {
+ return pitch;
+ }
+
+ /**
+ * Set the pitch of the audio, also the speed of playback.
+ * The value must be between 0.5 and 2.0.
+ *
+ * @param pitch The pitch to set.
+ * @throws IllegalArgumentException If pitch is not between 0.5 and 2.0.
+ */
+ public void setPitch(float pitch) {
+ if (pitch < 0.5f || pitch > 2.0f) {
+ throw new IllegalArgumentException("Pitch must be between 0.5 and 2.0");
+ }
+
+ this.pitch = pitch;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.Pitch);
+ }
+
+ /**
+ * @return The volume of this audio node.
+ *
+ * @see AudioNode#setVolume(float)
+ */
+ public float getVolume() {
+ return volume;
+ }
+
+ /**
+ * Set the volume of this audio node.
+ *
+ * The volume is specified as gain. 1.0 is the default.
+ *
+ * @param volume The volume to set.
+ * @throws IllegalArgumentException If volume is negative
+ */
+ public void setVolume(float volume) {
+ if (volume < 0f) {
+ throw new IllegalArgumentException("Volume cannot be negative");
+ }
+
+ this.volume = volume;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.Volume);
+ }
+
+ /**
+ * @return The time offset in seconds when the sound will start playing.
+ */
+ public float getTimeOffset() {
+ return timeOffset;
+ }
+
+ /**
+ * Set the time offset in seconds when the sound will start playing.
+ *
+ * @param timeOffset The time offset
+ * @throws IllegalArgumentException If timeOffset is negative
+ */
+ public void setTimeOffset(float timeOffset) {
+ if (timeOffset < 0f) {
+ throw new IllegalArgumentException("Time offset cannot be negative");
+ }
+
+ this.timeOffset = timeOffset;
+ if (data instanceof AudioStream) {
+ System.out.println("request setTime");
+ ((AudioStream) data).setTime(timeOffset);
+ }else if(status == Status.Playing){
+ stop();
+ play();
+ }
+ }
+
+ /**
+ * @return The velocity of the audio node.
+ *
+ * @see AudioNode#setVelocity(com.jme3.math.Vector3f)
+ */
+ public Vector3f getVelocity() {
+ return velocity;
+ }
+
+ /**
+ * Set the velocity of the audio node. The velocity is expected
+ * to be in meters. Does nothing if the audio node is not positional.
+ *
+ * @param velocity The velocity to set.
+ * @see AudioNode#setPositional(boolean)
+ */
+ public void setVelocity(Vector3f velocity) {
+ this.velocity.set(velocity);
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.Velocity);
+ }
+
+ /**
+ * @return True if reverb is enabled, otherwise false.
+ *
+ * @see AudioNode#setReverbEnabled(boolean)
+ */
+ public boolean isReverbEnabled() {
+ return reverbEnabled;
+ }
+
+ /**
+ * Set to true to enable reverberation effects for this audio node.
+ * Does nothing if the audio node is not positional.
+ * <br/>
+ * When enabled, the audio environment set with
+ * {@link AudioRenderer#setEnvironment(com.jme3.audio.Environment) }
+ * will apply a reverb effect to the audio playing from this audio node.
+ *
+ * @param reverbEnabled True to enable reverb.
+ */
+ public void setReverbEnabled(boolean reverbEnabled) {
+ this.reverbEnabled = reverbEnabled;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.ReverbEnabled);
+ }
+
+ /**
+ * @return Filter for the reverberations of this audio node.
+ *
+ * @see AudioNode#setReverbFilter(com.jme3.audio.Filter)
+ */
+ public Filter getReverbFilter() {
+ return reverbFilter;
+ }
+
+ /**
+ * Set the reverb filter for this audio node.
+ * <br/>
+ * The reverb filter will influence the reverberations
+ * of the audio node playing. This only has an effect if
+ * reverb is enabled.
+ *
+ * @param reverbFilter The reverb filter to set.
+ * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
+ */
+ public void setReverbFilter(Filter reverbFilter) {
+ this.reverbFilter = reverbFilter;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.ReverbFilter);
+ }
+
+ /**
+ * @return Max distance for this audio node.
+ *
+ * @see AudioNode#setMaxDistance(float)
+ */
+ public float getMaxDistance() {
+ return maxDistance;
+ }
+
+ /**
+ * Set the maximum distance for the attenuation of the audio node.
+ * Does nothing if the audio node is not positional.
+ * <br/>
+ * The maximum distance is the distance beyond which the audio
+ * node will no longer be attenuated. Normal attenuation is logarithmic
+ * from refDistance (it reduces by half when the distance doubles).
+ * Max distance sets where this fall-off stops and the sound will never
+ * get any quieter than at that distance. If you want a sound to fall-off
+ * very quickly then set ref distance very short and leave this distance
+ * very long.
+ *
+ * @param maxDistance The maximum playing distance.
+ * @throws IllegalArgumentException If maxDistance is negative
+ */
+ public void setMaxDistance(float maxDistance) {
+ if (maxDistance < 0) {
+ throw new IllegalArgumentException("Max distance cannot be negative");
+ }
+
+ this.maxDistance = maxDistance;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.MaxDistance);
+ }
+
+ /**
+ * @return The reference playing distance for the audio node.
+ *
+ * @see AudioNode#setRefDistance(float)
+ */
+ public float getRefDistance() {
+ return refDistance;
+ }
+
+ /**
+ * Set the reference playing distance for the audio node.
+ * Does nothing if the audio node is not positional.
+ * <br/>
+ * The reference playing distance is the distance at which the
+ * audio node will be exactly half of its volume.
+ *
+ * @param refDistance The reference playing distance.
+ * @throws IllegalArgumentException If refDistance is negative
+ */
+ public void setRefDistance(float refDistance) {
+ if (refDistance < 0) {
+ throw new IllegalArgumentException("Reference distance cannot be negative");
+ }
+
+ this.refDistance = refDistance;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.RefDistance);
+ }
+
+ /**
+ * @return True if the audio node is directional
+ *
+ * @see AudioNode#setDirectional(boolean)
+ */
+ public boolean isDirectional() {
+ return directional;
+ }
+
+ /**
+ * Set the audio node to be directional.
+ * Does nothing if the audio node is not positional.
+ * <br/>
+ * After setting directional, you should call
+ * {@link AudioNode#setDirection(com.jme3.math.Vector3f) }
+ * to set the audio node's direction.
+ *
+ * @param directional If the audio node is directional
+ */
+ public void setDirectional(boolean directional) {
+ this.directional = directional;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.IsDirectional);
+ }
+
+ /**
+ * @return The direction of this audio node.
+ *
+ * @see AudioNode#setDirection(com.jme3.math.Vector3f)
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * Set the direction of this audio node.
+ * Does nothing if the audio node is not directional.
+ *
+ * @param direction
+ * @see AudioNode#setDirectional(boolean)
+ */
+ public void setDirection(Vector3f direction) {
+ this.direction = direction;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.Direction);
+ }
+
+ /**
+ * @return The directional audio node, cone inner angle.
+ *
+ * @see AudioNode#setInnerAngle(float)
+ */
+ public float getInnerAngle() {
+ return innerAngle;
+ }
+
+ /**
+ * Set the directional audio node cone inner angle.
+ * Does nothing if the audio node is not directional.
+ *
+ * @param innerAngle The cone inner angle.
+ */
+ public void setInnerAngle(float innerAngle) {
+ this.innerAngle = innerAngle;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.InnerAngle);
+ }
+
+ /**
+ * @return The directional audio node, cone outer angle.
+ *
+ * @see AudioNode#setOuterAngle(float)
+ */
+ public float getOuterAngle() {
+ return outerAngle;
+ }
+
+ /**
+ * Set the directional audio node cone outer angle.
+ * Does nothing if the audio node is not directional.
+ *
+ * @param outerAngle The cone outer angle.
+ */
+ public void setOuterAngle(float outerAngle) {
+ this.outerAngle = outerAngle;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.OuterAngle);
+ }
+
+ /**
+ * @return True if the audio node is positional.
+ *
+ * @see AudioNode#setPositional(boolean)
+ */
+ public boolean isPositional() {
+ return positional;
+ }
+
+ /**
+ * Set the audio node as positional.
+ * The position, velocity, and distance parameters effect positional
+ * audio nodes. Set to false if the audio node should play in "headspace".
+ *
+ * @param positional True if the audio node should be positional, otherwise
+ * false if it should be headspace.
+ */
+ public void setPositional(boolean positional) {
+ this.positional = positional;
+ if (channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.IsPositional);
+ }
+
+ @Override
+ public void updateGeometricState(){
+ boolean updatePos = false;
+ if ((refreshFlags & RF_TRANSFORM) != 0){
+ updatePos = true;
+ }
+
+ super.updateGeometricState();
+
+ if (updatePos && channel >= 0)
+ getRenderer().updateSourceParam(this, AudioParam.Position);
+ }
+
+ @Override
+ public AudioNode clone(){
+ AudioNode clone = (AudioNode) super.clone();
+
+ clone.direction = direction.clone();
+ clone.velocity = velocity.clone();
+
+ return clone;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(audioKey, "audio_key", null);
+ oc.write(loop, "looping", false);
+ oc.write(volume, "volume", 1);
+ oc.write(pitch, "pitch", 1);
+ oc.write(timeOffset, "time_offset", 0);
+ oc.write(dryFilter, "dry_filter", null);
+
+ oc.write(velocity, "velocity", null);
+ oc.write(reverbEnabled, "reverb_enabled", false);
+ oc.write(reverbFilter, "reverb_filter", null);
+ oc.write(maxDistance, "max_distance", 20);
+ oc.write(refDistance, "ref_distance", 10);
+
+ oc.write(directional, "directional", false);
+ oc.write(direction, "direction", null);
+ oc.write(innerAngle, "inner_angle", 360);
+ oc.write(outerAngle, "outer_angle", 360);
+
+ oc.write(positional, "positional", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+
+ // NOTE: In previous versions of jME3, audioKey was actually
+ // written with the name "key". This has been changed
+ // to "audio_key" in case Spatial's key will be written as "key".
+ if (ic.getSavableVersion(AudioNode.class) == 0){
+ audioKey = (AudioKey) ic.readSavable("key", null);
+ }else{
+ audioKey = (AudioKey) ic.readSavable("audio_key", null);
+ }
+
+ loop = ic.readBoolean("looping", false);
+ volume = ic.readFloat("volume", 1);
+ pitch = ic.readFloat("pitch", 1);
+ timeOffset = ic.readFloat("time_offset", 0);
+ dryFilter = (Filter) ic.readSavable("dry_filter", null);
+
+ velocity = (Vector3f) ic.readSavable("velocity", null);
+ reverbEnabled = ic.readBoolean("reverb_enabled", false);
+ reverbFilter = (Filter) ic.readSavable("reverb_filter", null);
+ maxDistance = ic.readFloat("max_distance", 20);
+ refDistance = ic.readFloat("ref_distance", 10);
+
+ directional = ic.readBoolean("directional", false);
+ direction = (Vector3f) ic.readSavable("direction", null);
+ innerAngle = ic.readFloat("inner_angle", 360);
+ outerAngle = ic.readFloat("outer_angle", 360);
+
+ positional = ic.readBoolean("positional", false);
+
+ if (audioKey != null) {
+ try {
+ data = im.getAssetManager().loadAudio(audioKey);
+ } catch (AssetNotFoundException ex){
+ Logger.getLogger(AudioNode.class.getName()).log(Level.FINE, "Cannot locate {0} for audio node {1}", new Object[]{audioKey, key});
+ data = PlaceholderAssets.getPlaceholderAudio();
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ String ret = getClass().getSimpleName()
+ + "[status=" + status;
+ if (volume != 1f) {
+ ret += ", vol=" + volume;
+ }
+ if (pitch != 1f) {
+ ret += ", pitch=" + pitch;
+ }
+ return ret + "]";
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/AudioParam.java b/engine/src/core/com/jme3/audio/AudioParam.java
new file mode 100644
index 0000000..bf53c24
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioParam.java
@@ -0,0 +1,19 @@
+package com.jme3.audio;
+
+public enum AudioParam {
+ Volume,
+ Pitch,
+ Looping,
+ Position,
+ IsPositional,
+ Direction,
+ IsDirectional,
+ Velocity,
+ OuterAngle,
+ InnerAngle,
+ RefDistance,
+ MaxDistance,
+ DryFilter,
+ ReverbFilter,
+ ReverbEnabled;
+}
diff --git a/engine/src/core/com/jme3/audio/AudioRenderer.java b/engine/src/core/com/jme3/audio/AudioRenderer.java
new file mode 100644
index 0000000..15ad9c9
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioRenderer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+/**
+ * Interface to be implemented by audio renderers.
+ *
+ * @author Kirill Vainer
+ */
+public interface AudioRenderer {
+
+ /**
+ * @param listener The listener camera, all 3D sounds will be
+ * oriented around the listener.
+ */
+ public void setListener(Listener listener);
+
+ /**
+ * Sets the environment, used for reverb effects.
+ *
+ * @see AudioNode#setReverbEnabled(boolean)
+ * @param env The environment to set.
+ */
+ public void setEnvironment(Environment env);
+
+ public void playSourceInstance(AudioNode src);
+ public void playSource(AudioNode src);
+ public void pauseSource(AudioNode src);
+ public void stopSource(AudioNode src);
+
+ public void updateSourceParam(AudioNode src, AudioParam param);
+ public void updateListenerParam(Listener listener, ListenerParam param);
+
+ public void deleteFilter(Filter filter);
+ public void deleteAudioData(AudioData ad);
+
+ /**
+ * Initializes the renderer. Should be the first method called
+ * before using the system.
+ */
+ public void initialize();
+
+ /**
+ * Update the audio system. Must be called periodically.
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf);
+
+ /**
+ * Cleanup/destroy the audio system. Call this when app closes.
+ */
+ public void cleanup();
+}
diff --git a/engine/src/core/com/jme3/audio/AudioStream.java b/engine/src/core/com/jme3/audio/AudioStream.java
new file mode 100644
index 0000000..6d20d67
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioStream.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.util.NativeObject;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>AudioStream</code> is an implementation of AudioData that
+ * acquires the audio from an InputStream. Audio can be streamed
+ * from network, hard drive etc. It is assumed the data coming
+ * from the input stream is uncompressed.
+ *
+ * @author Kirill Vainer
+ */
+public class AudioStream extends AudioData implements Closeable{
+
+ private final static Logger logger = Logger.getLogger(AudioStream.class.getName());
+ protected InputStream in;
+ protected float duration = -1f;
+ protected boolean open = false;
+ protected int[] ids;
+
+ public AudioStream(){
+ super();
+ }
+
+ protected AudioStream(int[] ids){
+ // Pass some dummy ID so handle
+ // doesn't get created.
+ super(-1);
+ // This is what gets destroyed in reality
+ this.ids = ids;
+ }
+
+ public void updateData(InputStream in, float duration){
+ if (id != -1 || this.in != null)
+ throw new IllegalStateException("Data already set!");
+
+ this.in = in;
+ this.duration = duration;
+ open = true;
+ }
+
+ /**
+ * Reads samples from the stream. The format of the data
+ * depends on the getSampleRate(), getChannels(), getBitsPerSample()
+ * values.
+ *
+ * @param buf Buffer where to read the samples
+ * @param offset The offset in the buffer where to read samples
+ * @param length The length inside the buffer where to read samples
+ * @return number of bytes read.
+ */
+ public int readSamples(byte[] buf, int offset, int length){
+ if (!open)
+ return -1;
+
+ try{
+ return in.read(buf, offset, length);
+ }catch (IOException ex){
+ return -1;
+ }
+ }
+
+ /**
+ * Reads samples from the stream.
+ *
+ * @see AudioStream#readSamples(byte[], int, int)
+ * @param buf Buffer where to read the samples
+ * @return number of bytes read.
+ */
+ public int readSamples(byte[] buf){
+ return readSamples(buf, 0, buf.length);
+ }
+
+ public float getDuration(){
+ return duration;
+ }
+
+ @Override
+ public int getId(){
+ throw new RuntimeException("Don't use getId() on streams");
+ }
+
+ @Override
+ public void setId(int id){
+ throw new RuntimeException("Don't use setId() on streams");
+ }
+
+ public void initIds(int count){
+ ids = new int[count];
+ }
+
+ public int getId(int index){
+ return ids[index];
+ }
+
+ public void setId(int index, int id){
+ ids[index] = id;
+ }
+
+ public int[] getIds(){
+ return ids;
+ }
+
+ public void setIds(int[] ids){
+ this.ids = ids;
+ }
+
+ @Override
+ public DataType getDataType() {
+ return DataType.Stream;
+ }
+
+ @Override
+ public void resetObject() {
+ id = -1;
+ ids = null;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ // It seems that the audio renderer is already doing a good
+ // job at deleting audio streams when they finish playing.
+// ((AudioRenderer)rendererObject).deleteAudioData(this);
+ }
+
+ @Override
+ public NativeObject createDestructableClone() {
+ return new AudioStream(ids);
+ }
+
+ /**
+ * @return Whether the stream is open or not. Reading from a closed
+ * stream will always return eof.
+ */
+ public boolean isOpen(){
+ return open;
+ }
+
+ /**
+ * Closes the stream, releasing all data relating to it. Reading
+ * from the stream will return eof.
+ * @throws IOException
+ */
+ public void close() {
+ if (in != null && open){
+ try{
+ in.close();
+ }catch (IOException ex){
+ }
+ open = false;
+ }else{
+ throw new RuntimeException("AudioStream is already closed!");
+ }
+ }
+
+
+ public void setTime(float time){
+ if(in instanceof SeekableStream){
+ ((SeekableStream)in).setTime(time);
+ }else{
+ logger.log(Level.WARNING,"Cannot use setTime on a stream that is not seekable. You must load the file with the streamCache option set to true");
+ }
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/audio/Environment.java b/engine/src/core/com/jme3/audio/Environment.java
new file mode 100644
index 0000000..9a1279e
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/Environment.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.math.FastMath;
+
+/**
+ * Audio environment, for reverb effects.
+ * @author Kirill
+ */
+public class Environment {
+
+ private float airAbsorbGainHf = 0.99426f;
+ private float roomRolloffFactor = 0;
+
+ private float decayTime = 1.49f;
+ private float decayHFRatio = 0.54f;
+
+ private float density = 1.0f;
+ private float diffusion = 0.3f;
+
+ private float gain = 0.316f;
+ private float gainHf = 0.022f;
+
+ private float lateReverbDelay = 0.088f;
+ private float lateReverbGain = 0.768f;
+
+ private float reflectDelay = 0.162f;
+ private float reflectGain = 0.052f;
+
+ private boolean decayHfLimit = true;
+
+ public static final Environment Garage, Dungeon, Cavern, AcousticLab, Closet;
+
+ static {
+ Garage = new Environment(1, 1, 1, 1, .9f, .5f, .751f, .0039f, .661f, .0137f);
+ Dungeon = new Environment(.75f, 1, 1, .75f, 1.6f, 1, 0.95f, 0.0026f, 0.93f, 0.0103f);
+ Cavern = new Environment(.5f, 1, 1, .5f, 2.25f, 1, .908f, .0103f, .93f, .041f);
+ AcousticLab = new Environment(.5f, 1, 1, 1, .28f, 1, .87f, .002f, .81f, .008f);
+ Closet = new Environment(1, 1, 1, 1, .15f, 1, .6f, .0025f, .5f, .0006f);
+ }
+
+ private static final float eaxDbToAmp(float eaxDb){
+ float dB = eaxDb / 2000f;
+ return FastMath.pow(10f, dB);
+ }
+
+ public Environment(){
+ }
+
+ public Environment(Environment source) {
+ this.airAbsorbGainHf = source.airAbsorbGainHf;
+ this.roomRolloffFactor = source.roomRolloffFactor;
+ this.decayTime = source.decayTime;
+ this.decayHFRatio = source.decayHFRatio;
+ this.density = source.density;
+ this.diffusion = source.diffusion;
+ this.gain = source.gain;
+ this.gainHf = source.gainHf;
+ this.lateReverbDelay = source.lateReverbDelay;
+ this.lateReverbGain = source.lateReverbGain;
+ this.reflectDelay = source.reflectDelay;
+ this.reflectGain = source.reflectGain;
+ this.decayHfLimit = source.decayHfLimit;
+ }
+
+ public Environment(float density, float diffusion, float gain, float gainHf,
+ float decayTime, float decayHf, float reflGain,
+ float reflDelay, float lateGain, float lateDelay){
+ this.decayTime = decayTime;
+ this.decayHFRatio = decayHf;
+ this.density = density;
+ this.diffusion = diffusion;
+ this.gain = gain;
+ this.gainHf = gainHf;
+ this.lateReverbDelay = lateDelay;
+ this.lateReverbGain = lateGain;
+ this.reflectDelay = reflDelay;
+ this.reflectGain = reflGain;
+ }
+
+ public Environment(float[] e){
+ if (e.length != 28)
+ throw new IllegalArgumentException("Not an EAX preset");
+
+ // skip env id
+ // e[0]
+ // skip room size
+ // e[1]
+
+// density = 0;
+ diffusion = e[2];
+ gain = eaxDbToAmp(e[3]); // convert
+ gainHf = eaxDbToAmp(e[4]) / eaxDbToAmp(e[5]); // convert
+ decayTime = e[6];
+ decayHFRatio = e[7] / e[8];
+ reflectGain = eaxDbToAmp(e[9]); // convert
+ reflectDelay = e[10];
+
+ // skip 3 pan values
+ // e[11] e[12] e[13]
+
+ lateReverbGain = eaxDbToAmp(e[14]); // convert
+ lateReverbDelay = e[15];
+
+ // skip 3 pan values
+ // e[16] e[17] e[18]
+
+ // skip echo time, echo damping, mod time, mod damping
+ // e[19] e[20] e[21] e[22]
+
+ airAbsorbGainHf = eaxDbToAmp(e[23]);
+
+ // skip HF Reference and LF Reference
+ // e[24] e[25]
+
+ roomRolloffFactor = e[26];
+
+ // skip flags
+ // e[27]
+ }
+
+ public float getAirAbsorbGainHf() {
+ return airAbsorbGainHf;
+ }
+
+ public void setAirAbsorbGainHf(float airAbsorbGainHf) {
+ this.airAbsorbGainHf = airAbsorbGainHf;
+ }
+
+ public float getDecayHFRatio() {
+ return decayHFRatio;
+ }
+
+ public void setDecayHFRatio(float decayHFRatio) {
+ this.decayHFRatio = decayHFRatio;
+ }
+
+ public boolean isDecayHfLimit() {
+ return decayHfLimit;
+ }
+
+ public void setDecayHfLimit(boolean decayHfLimit) {
+ this.decayHfLimit = decayHfLimit;
+ }
+
+ public float getDecayTime() {
+ return decayTime;
+ }
+
+ public void setDecayTime(float decayTime) {
+ this.decayTime = decayTime;
+ }
+
+ public float getDensity() {
+ return density;
+ }
+
+ public void setDensity(float density) {
+ this.density = density;
+ }
+
+ public float getDiffusion() {
+ return diffusion;
+ }
+
+ public void setDiffusion(float diffusion) {
+ this.diffusion = diffusion;
+ }
+
+ public float getGain() {
+ return gain;
+ }
+
+ public void setGain(float gain) {
+ this.gain = gain;
+ }
+
+ public float getGainHf() {
+ return gainHf;
+ }
+
+ public void setGainHf(float gainHf) {
+ this.gainHf = gainHf;
+ }
+
+ public float getLateReverbDelay() {
+ return lateReverbDelay;
+ }
+
+ public void setLateReverbDelay(float lateReverbDelay) {
+ this.lateReverbDelay = lateReverbDelay;
+ }
+
+ public float getLateReverbGain() {
+ return lateReverbGain;
+ }
+
+ public void setLateReverbGain(float lateReverbGain) {
+ this.lateReverbGain = lateReverbGain;
+ }
+
+ public float getReflectDelay() {
+ return reflectDelay;
+ }
+
+ public void setReflectDelay(float reflectDelay) {
+ this.reflectDelay = reflectDelay;
+ }
+
+ public float getReflectGain() {
+ return reflectGain;
+ }
+
+ public void setReflectGain(float reflectGain) {
+ this.reflectGain = reflectGain;
+ }
+
+ public float getRoomRolloffFactor() {
+ return roomRolloffFactor;
+ }
+
+ public void setRoomRolloffFactor(float roomRolloffFactor) {
+ this.roomRolloffFactor = roomRolloffFactor;
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/Filter.java b/engine/src/core/com/jme3/audio/Filter.java
new file mode 100644
index 0000000..da231d6
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/Filter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import com.jme3.util.NativeObject;
+import java.io.IOException;
+
+public abstract class Filter extends NativeObject implements Savable {
+
+ public Filter(){
+ super(Filter.class);
+ }
+
+ protected Filter(int id){
+ super(Filter.class, id);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ // nothing to save
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ // nothing to read
+ }
+
+ @Override
+ public void resetObject() {
+ this.id = -1;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((AudioRenderer)rendererObject).deleteFilter(this);
+ }
+
+ @Override
+ public abstract NativeObject createDestructableClone();
+
+}
diff --git a/engine/src/core/com/jme3/audio/Listener.java b/engine/src/core/com/jme3/audio/Listener.java
new file mode 100644
index 0000000..609b0ca
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/Listener.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+
+public class Listener {
+
+ private Vector3f location;
+ private Vector3f velocity;
+ private Quaternion rotation;
+ private float volume = 1;
+ private AudioRenderer renderer;
+
+ public Listener(){
+ location = new Vector3f();
+ velocity = new Vector3f();
+ rotation = new Quaternion();
+ }
+
+ public Listener(Listener source){
+ location = source.location.clone();
+ velocity = source.velocity.clone();
+ rotation = source.rotation.clone();
+ volume = source.volume;
+ }
+
+ public void setRenderer(AudioRenderer renderer){
+ this.renderer = renderer;
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ this.volume = volume;
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Volume);
+ }
+
+ public Vector3f getLocation() {
+ return location;
+ }
+
+ public Quaternion getRotation() {
+ return rotation;
+ }
+
+ public Vector3f getVelocity() {
+ return velocity;
+ }
+
+ public Vector3f getLeft(){
+ return rotation.getRotationColumn(0);
+ }
+
+ public Vector3f getUp(){
+ return rotation.getRotationColumn(1);
+ }
+
+ public Vector3f getDirection(){
+ return rotation.getRotationColumn(2);
+ }
+
+ public void setLocation(Vector3f location) {
+ this.location.set(location);
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Position);
+ }
+
+ public void setRotation(Quaternion rotation) {
+ this.rotation.set(rotation);
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Rotation);
+ }
+
+ public void setVelocity(Vector3f velocity) {
+ this.velocity.set(velocity);
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Velocity);
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/ListenerParam.java b/engine/src/core/com/jme3/audio/ListenerParam.java
new file mode 100644
index 0000000..6b3b55e
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/ListenerParam.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+public enum ListenerParam {
+ Position,
+ Velocity,
+ Rotation,
+ Volume;
+}
diff --git a/engine/src/core/com/jme3/audio/LowPassFilter.java b/engine/src/core/com/jme3/audio/LowPassFilter.java
new file mode 100644
index 0000000..58242cf
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/LowPassFilter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.util.NativeObject;
+import java.io.IOException;
+
+public class LowPassFilter extends Filter {
+
+ protected float volume, highFreqVolume;
+
+ public LowPassFilter(float volume, float highFreqVolume) {
+ super();
+ setVolume(volume);
+ setHighFreqVolume(highFreqVolume);
+ }
+
+ protected LowPassFilter(int id){
+ super(id);
+ }
+
+ public float getHighFreqVolume() {
+ return highFreqVolume;
+ }
+
+ public void setHighFreqVolume(float highFreqVolume) {
+ if (highFreqVolume < 0 || highFreqVolume > 1)
+ throw new IllegalArgumentException("High freq volume must be between 0 and 1");
+
+ this.highFreqVolume = highFreqVolume;
+ this.updateNeeded = true;
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ if (volume < 0 || volume > 1)
+ throw new IllegalArgumentException("Volume must be between 0 and 1");
+
+ this.volume = volume;
+ this.updateNeeded = true;
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(volume, "volume", 0);
+ oc.write(highFreqVolume, "hf_volume", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ volume = ic.readFloat("volume", 0);
+ highFreqVolume = ic.readFloat("hf_volume", 0);
+ }
+
+ @Override
+ public NativeObject createDestructableClone() {
+ return new LowPassFilter(id);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/SeekableStream.java b/engine/src/core/com/jme3/audio/SeekableStream.java
new file mode 100644
index 0000000..bb5b9e4
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/SeekableStream.java
@@ -0,0 +1,15 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.audio;
+
+/**
+ *
+ * @author Nehon
+ */
+public interface SeekableStream{
+
+ public void setTime(float time);
+
+}
diff --git a/engine/src/core/com/jme3/bounding/BoundingBox.java b/engine/src/core/com/jme3/bounding/BoundingBox.java
new file mode 100644
index 0000000..bc11411
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingBox.java
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bounding;
+
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.*;
+import com.jme3.scene.Mesh;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+//import com.jme.scene.TriMesh;
+
+/**
+ * <code>BoundingBox</code> defines an axis-aligned cube that defines a
+ * container for a group of vertices of a particular piece of geometry. This box
+ * defines a center and extents from that center along the x, y and z axis. <br>
+ * <br>
+ * A typical usage is to allow the class define the center and radius by calling
+ * either <code>containAABB</code> or <code>averagePoints</code>. A call to
+ * <code>computeFramePoint</code> in turn calls <code>containAABB</code>.
+ *
+ * @author Joshua Slack
+ * @version $Id: BoundingBox.java,v 1.50 2007/09/22 16:46:35 irrisor Exp $
+ */
+public class BoundingBox extends BoundingVolume {
+
+ float xExtent, yExtent, zExtent;
+
+ /**
+ * Default constructor instantiates a new <code>BoundingBox</code>
+ * object.
+ */
+ public BoundingBox() {
+ }
+
+ /**
+ * Contstructor instantiates a new <code>BoundingBox</code> object with
+ * given specs.
+ */
+ public BoundingBox(Vector3f c, float x, float y, float z) {
+ this.center.set(c);
+ this.xExtent = x;
+ this.yExtent = y;
+ this.zExtent = z;
+ }
+
+ public BoundingBox(BoundingBox source) {
+ this.center.set(source.center);
+ this.xExtent = source.xExtent;
+ this.yExtent = source.yExtent;
+ this.zExtent = source.zExtent;
+ }
+
+ public BoundingBox(Vector3f min, Vector3f max) {
+ setMinMax(min, max);
+ }
+
+ public Type getType() {
+ return Type.AABB;
+ }
+
+ /**
+ * <code>computeFromPoints</code> creates a new Bounding Box from a given
+ * set of points. It uses the <code>containAABB</code> method as default.
+ *
+ * @param points
+ * the points to contain.
+ */
+ public void computeFromPoints(FloatBuffer points) {
+ containAABB(points);
+ }
+
+ /**
+ * <code>computeFromTris</code> creates a new Bounding Box from a given
+ * set of triangles. It is used in OBBTree calculations.
+ *
+ * @param tris
+ * @param start
+ * @param end
+ */
+ public void computeFromTris(Triangle[] tris, int start, int end) {
+ if (end - start <= 0) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ Vector3f min = vars.vect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+ Vector3f max = vars.vect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+
+ Vector3f point;
+ for (int i = start; i < end; i++) {
+ point = tris[i].get(0);
+ checkMinMax(min, max, point);
+ point = tris[i].get(1);
+ checkMinMax(min, max, point);
+ point = tris[i].get(2);
+ checkMinMax(min, max, point);
+ }
+
+ center.set(min.addLocal(max));
+ center.multLocal(0.5f);
+
+ xExtent = max.x - center.x;
+ yExtent = max.y - center.y;
+ zExtent = max.z - center.z;
+
+ vars.release();
+ }
+
+ public void computeFromTris(int[] indices, Mesh mesh, int start, int end) {
+ if (end - start <= 0) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ Vector3f vect1 = vars.vect1;
+ Vector3f vect2 = vars.vect2;
+ Triangle triangle = vars.triangle;
+
+ Vector3f min = vect1.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+ Vector3f max = vect2.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ Vector3f point;
+
+ for (int i = start; i < end; i++) {
+ mesh.getTriangle(indices[i], triangle);
+ point = triangle.get(0);
+ checkMinMax(min, max, point);
+ point = triangle.get(1);
+ checkMinMax(min, max, point);
+ point = triangle.get(2);
+ checkMinMax(min, max, point);
+ }
+
+ center.set(min.addLocal(max));
+ center.multLocal(0.5f);
+
+ xExtent = max.x - center.x;
+ yExtent = max.y - center.y;
+ zExtent = max.z - center.z;
+
+ vars.release();
+ }
+
+ public static void checkMinMax(Vector3f min, Vector3f max, Vector3f point) {
+ if (point.x < min.x) {
+ min.x = point.x;
+ }
+ if (point.x > max.x) {
+ max.x = point.x;
+ }
+ if (point.y < min.y) {
+ min.y = point.y;
+ }
+ if (point.y > max.y) {
+ max.y = point.y;
+ }
+ if (point.z < min.z) {
+ min.z = point.z;
+ }
+ if (point.z > max.z) {
+ max.z = point.z;
+ }
+ }
+
+ /**
+ * <code>containAABB</code> creates a minimum-volume axis-aligned bounding
+ * box of the points, then selects the smallest enclosing sphere of the box
+ * with the sphere centered at the boxes center.
+ *
+ * @param points
+ * the list of points.
+ */
+ public void containAABB(FloatBuffer points) {
+ if (points == null) {
+ return;
+ }
+
+ points.rewind();
+ if (points.remaining() <= 2) // we need at least a 3 float vector
+ {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ BufferUtils.populateFromBuffer(vars.vect1, points, 0);
+ float minX = vars.vect1.x, minY = vars.vect1.y, minZ = vars.vect1.z;
+ float maxX = vars.vect1.x, maxY = vars.vect1.y, maxZ = vars.vect1.z;
+
+ for (int i = 1, len = points.remaining() / 3; i < len; i++) {
+ BufferUtils.populateFromBuffer(vars.vect1, points, i);
+
+ if (vars.vect1.x < minX) {
+ minX = vars.vect1.x;
+ } else if (vars.vect1.x > maxX) {
+ maxX = vars.vect1.x;
+ }
+
+ if (vars.vect1.y < minY) {
+ minY = vars.vect1.y;
+ } else if (vars.vect1.y > maxY) {
+ maxY = vars.vect1.y;
+ }
+
+ if (vars.vect1.z < minZ) {
+ minZ = vars.vect1.z;
+ } else if (vars.vect1.z > maxZ) {
+ maxZ = vars.vect1.z;
+ }
+ }
+
+ vars.release();
+
+ center.set(minX + maxX, minY + maxY, minZ + maxZ);
+ center.multLocal(0.5f);
+
+ xExtent = maxX - center.x;
+ yExtent = maxY - center.y;
+ zExtent = maxZ - center.z;
+ }
+
+ /**
+ * <code>transform</code> modifies the center of the box to reflect the
+ * change made via a rotation, translation and scale.
+ *
+ * @param trans
+ * the transform to apply
+ * @param store
+ * box to store result in
+ */
+ public BoundingVolume transform(Transform trans, BoundingVolume store) {
+
+ BoundingBox box;
+ if (store == null || store.getType() != Type.AABB) {
+ box = new BoundingBox();
+ } else {
+ box = (BoundingBox) store;
+ }
+
+ center.mult(trans.getScale(), box.center);
+ trans.getRotation().mult(box.center, box.center);
+ box.center.addLocal(trans.getTranslation());
+
+ TempVars vars = TempVars.get();
+
+ Matrix3f transMatrix = vars.tempMat3;
+ transMatrix.set(trans.getRotation());
+ // Make the rotation matrix all positive to get the maximum x/y/z extent
+ transMatrix.absoluteLocal();
+
+ Vector3f scale = trans.getScale();
+ vars.vect1.set(xExtent * scale.x, yExtent * scale.y, zExtent * scale.z);
+ transMatrix.mult(vars.vect1, vars.vect2);
+ // Assign the biggest rotations after scales.
+ box.xExtent = FastMath.abs(vars.vect2.getX());
+ box.yExtent = FastMath.abs(vars.vect2.getY());
+ box.zExtent = FastMath.abs(vars.vect2.getZ());
+
+ vars.release();
+
+ return box;
+ }
+
+ public BoundingVolume transform(Matrix4f trans, BoundingVolume store) {
+ BoundingBox box;
+ if (store == null || store.getType() != Type.AABB) {
+ box = new BoundingBox();
+ } else {
+ box = (BoundingBox) store;
+ }
+ TempVars vars = TempVars.get();
+
+
+ float w = trans.multProj(center, box.center);
+ box.center.divideLocal(w);
+
+ Matrix3f transMatrix = vars.tempMat3;
+ trans.toRotationMatrix(transMatrix);
+
+ // Make the rotation matrix all positive to get the maximum x/y/z extent
+ transMatrix.absoluteLocal();
+
+ vars.vect1.set(xExtent, yExtent, zExtent);
+ transMatrix.mult(vars.vect1, vars.vect1);
+
+ // Assign the biggest rotations after scales.
+ box.xExtent = FastMath.abs(vars.vect1.getX());
+ box.yExtent = FastMath.abs(vars.vect1.getY());
+ box.zExtent = FastMath.abs(vars.vect1.getZ());
+
+ vars.release();
+
+ return box;
+ }
+
+ /**
+ * <code>whichSide</code> takes a plane (typically provided by a view
+ * frustum) to determine which side this bound is on.
+ *
+ * @param plane
+ * the plane to check against.
+ */
+ public Plane.Side whichSide(Plane plane) {
+ float radius = FastMath.abs(xExtent * plane.getNormal().getX())
+ + FastMath.abs(yExtent * plane.getNormal().getY())
+ + FastMath.abs(zExtent * plane.getNormal().getZ());
+
+ float distance = plane.pseudoDistance(center);
+
+ //changed to < and > to prevent floating point precision problems
+ if (distance < -radius) {
+ return Plane.Side.Negative;
+ } else if (distance > radius) {
+ return Plane.Side.Positive;
+ } else {
+ return Plane.Side.None;
+ }
+ }
+
+ /**
+ * <code>merge</code> combines this sphere with a second bounding sphere.
+ * This new sphere contains both bounding spheres and is returned.
+ *
+ * @param volume
+ * the sphere to combine with this sphere.
+ * @return the new sphere
+ */
+ public BoundingVolume merge(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+ case AABB: {
+ BoundingBox vBox = (BoundingBox) volume;
+ return merge(vBox.center, vBox.xExtent, vBox.yExtent,
+ vBox.zExtent, new BoundingBox(new Vector3f(0, 0, 0), 0,
+ 0, 0));
+ }
+
+ case Sphere: {
+ BoundingSphere vSphere = (BoundingSphere) volume;
+ return merge(vSphere.center, vSphere.radius, vSphere.radius,
+ vSphere.radius, new BoundingBox(new Vector3f(0, 0, 0),
+ 0, 0, 0));
+ }
+
+// case OBB: {
+// OrientedBoundingBox box = (OrientedBoundingBox) volume;
+// BoundingBox rVal = (BoundingBox) this.clone(null);
+// return rVal.mergeOBB(box);
+// }
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * <code>mergeLocal</code> combines this sphere with a second bounding
+ * sphere locally. Altering this sphere to contain both the original and the
+ * additional sphere volumes;
+ *
+ * @param volume
+ * the sphere to combine with this sphere.
+ * @return this
+ */
+ public BoundingVolume mergeLocal(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+ case AABB: {
+ BoundingBox vBox = (BoundingBox) volume;
+ return merge(vBox.center, vBox.xExtent, vBox.yExtent,
+ vBox.zExtent, this);
+ }
+
+ case Sphere: {
+ BoundingSphere vSphere = (BoundingSphere) volume;
+ return merge(vSphere.center, vSphere.radius, vSphere.radius,
+ vSphere.radius, this);
+ }
+
+// case OBB: {
+// return mergeOBB((OrientedBoundingBox) volume);
+// }
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Merges this AABB with the given OBB.
+ *
+ * @param volume
+ * the OBB to merge this AABB with.
+ * @return This AABB extended to fit the given OBB.
+ */
+// private BoundingBox mergeOBB(OrientedBoundingBox volume) {
+// if (!volume.correctCorners)
+// volume.computeCorners();
+//
+// TempVars vars = TempVars.get();
+// Vector3f min = vars.compVect1.set(center.x - xExtent, center.y - yExtent,
+// center.z - zExtent);
+// Vector3f max = vars.compVect2.set(center.x + xExtent, center.y + yExtent,
+// center.z + zExtent);
+//
+// for (int i = 1; i < volume.vectorStore.length; i++) {
+// Vector3f temp = volume.vectorStore[i];
+// if (temp.x < min.x)
+// min.x = temp.x;
+// else if (temp.x > max.x)
+// max.x = temp.x;
+//
+// if (temp.y < min.y)
+// min.y = temp.y;
+// else if (temp.y > max.y)
+// max.y = temp.y;
+//
+// if (temp.z < min.z)
+// min.z = temp.z;
+// else if (temp.z > max.z)
+// max.z = temp.z;
+// }
+//
+// center.set(min.addLocal(max));
+// center.multLocal(0.5f);
+//
+// xExtent = max.x - center.x;
+// yExtent = max.y - center.y;
+// zExtent = max.z - center.z;
+// return this;
+// }
+ /**
+ * <code>merge</code> combines this bounding box with another box which is
+ * defined by the center, x, y, z extents.
+ *
+ * @param boxCenter
+ * the center of the box to merge with
+ * @param boxX
+ * the x extent of the box to merge with.
+ * @param boxY
+ * the y extent of the box to merge with.
+ * @param boxZ
+ * the z extent of the box to merge with.
+ * @param rVal
+ * the resulting merged box.
+ * @return the resulting merged box.
+ */
+ private BoundingBox merge(Vector3f boxCenter, float boxX, float boxY,
+ float boxZ, BoundingBox rVal) {
+
+ TempVars vars = TempVars.get();
+
+ vars.vect1.x = center.x - xExtent;
+ if (vars.vect1.x > boxCenter.x - boxX) {
+ vars.vect1.x = boxCenter.x - boxX;
+ }
+ vars.vect1.y = center.y - yExtent;
+ if (vars.vect1.y > boxCenter.y - boxY) {
+ vars.vect1.y = boxCenter.y - boxY;
+ }
+ vars.vect1.z = center.z - zExtent;
+ if (vars.vect1.z > boxCenter.z - boxZ) {
+ vars.vect1.z = boxCenter.z - boxZ;
+ }
+
+ vars.vect2.x = center.x + xExtent;
+ if (vars.vect2.x < boxCenter.x + boxX) {
+ vars.vect2.x = boxCenter.x + boxX;
+ }
+ vars.vect2.y = center.y + yExtent;
+ if (vars.vect2.y < boxCenter.y + boxY) {
+ vars.vect2.y = boxCenter.y + boxY;
+ }
+ vars.vect2.z = center.z + zExtent;
+ if (vars.vect2.z < boxCenter.z + boxZ) {
+ vars.vect2.z = boxCenter.z + boxZ;
+ }
+
+ center.set(vars.vect2).addLocal(vars.vect1).multLocal(0.5f);
+
+ xExtent = vars.vect2.x - center.x;
+ yExtent = vars.vect2.y - center.y;
+ zExtent = vars.vect2.z - center.z;
+
+ vars.release();
+
+ return rVal;
+ }
+
+ /**
+ * <code>clone</code> creates a new BoundingBox object containing the same
+ * data as this one.
+ *
+ * @param store
+ * where to store the cloned information. if null or wrong class,
+ * a new store is created.
+ * @return the new BoundingBox
+ */
+ public BoundingVolume clone(BoundingVolume store) {
+ if (store != null && store.getType() == Type.AABB) {
+ BoundingBox rVal = (BoundingBox) store;
+ rVal.center.set(center);
+ rVal.xExtent = xExtent;
+ rVal.yExtent = yExtent;
+ rVal.zExtent = zExtent;
+ rVal.checkPlane = checkPlane;
+ return rVal;
+ }
+
+ BoundingBox rVal = new BoundingBox(center.clone(),
+ xExtent, yExtent, zExtent);
+ return rVal;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this object.
+ * The form is: "Radius: RRR.SSSS Center: <Vector>".
+ *
+ * @return the string representation of this.
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [Center: " + center + " xExtent: "
+ + xExtent + " yExtent: " + yExtent + " zExtent: " + zExtent
+ + "]";
+ }
+
+ /**
+ * intersects determines if this Bounding Box intersects with another given
+ * bounding volume. If so, true is returned, otherwise, false is returned.
+ *
+ * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume)
+ */
+ public boolean intersects(BoundingVolume bv) {
+ return bv.intersectsBoundingBox(this);
+ }
+
+ /**
+ * determines if this bounding box intersects a given bounding sphere.
+ *
+ * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
+ */
+ public boolean intersectsSphere(BoundingSphere bs) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
+
+ if (FastMath.abs(center.x - bs.center.x) < bs.getRadius()
+ + xExtent
+ && FastMath.abs(center.y - bs.center.y) < bs.getRadius()
+ + yExtent
+ && FastMath.abs(center.z - bs.center.z) < bs.getRadius()
+ + zExtent) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * determines if this bounding box intersects a given bounding box. If the
+ * two boxes intersect in any way, true is returned. Otherwise, false is
+ * returned.
+ *
+ * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox)
+ */
+ public boolean intersectsBoundingBox(BoundingBox bb) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
+
+ if (center.x + xExtent < bb.center.x - bb.xExtent
+ || center.x - xExtent > bb.center.x + bb.xExtent) {
+ return false;
+ } else if (center.y + yExtent < bb.center.y - bb.yExtent
+ || center.y - yExtent > bb.center.y + bb.yExtent) {
+ return false;
+ } else if (center.z + zExtent < bb.center.z - bb.zExtent
+ || center.z - zExtent > bb.center.z + bb.zExtent) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * determines if this bounding box intersects with a given oriented bounding
+ * box.
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox)
+ */
+// public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
+// return obb.intersectsBoundingBox(this);
+// }
+ /**
+ * determines if this bounding box intersects with a given ray object. If an
+ * intersection has occurred, true is returned, otherwise false is returned.
+ *
+ * @see BoundingVolume#intersects(com.jme3.math.Ray)
+ */
+ public boolean intersects(Ray ray) {
+ assert Vector3f.isValidVector(center);
+
+ float rhs;
+
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = ray.origin.subtract(getCenter(vars.vect2), vars.vect1);
+
+ final float[] fWdU = vars.fWdU;
+ final float[] fAWdU = vars.fAWdU;
+ final float[] fDdU = vars.fDdU;
+ final float[] fADdU = vars.fADdU;
+ final float[] fAWxDdU = vars.fAWxDdU;
+
+ fWdU[0] = ray.getDirection().dot(Vector3f.UNIT_X);
+ fAWdU[0] = FastMath.abs(fWdU[0]);
+ fDdU[0] = diff.dot(Vector3f.UNIT_X);
+ fADdU[0] = FastMath.abs(fDdU[0]);
+ if (fADdU[0] > xExtent && fDdU[0] * fWdU[0] >= 0.0) {
+ vars.release();
+ return false;
+ }
+
+ fWdU[1] = ray.getDirection().dot(Vector3f.UNIT_Y);
+ fAWdU[1] = FastMath.abs(fWdU[1]);
+ fDdU[1] = diff.dot(Vector3f.UNIT_Y);
+ fADdU[1] = FastMath.abs(fDdU[1]);
+ if (fADdU[1] > yExtent && fDdU[1] * fWdU[1] >= 0.0) {
+ vars.release();
+ return false;
+ }
+
+ fWdU[2] = ray.getDirection().dot(Vector3f.UNIT_Z);
+ fAWdU[2] = FastMath.abs(fWdU[2]);
+ fDdU[2] = diff.dot(Vector3f.UNIT_Z);
+ fADdU[2] = FastMath.abs(fDdU[2]);
+ if (fADdU[2] > zExtent && fDdU[2] * fWdU[2] >= 0.0) {
+ vars.release();
+ return false;
+ }
+
+ Vector3f wCrossD = ray.getDirection().cross(diff, vars.vect2);
+
+ fAWxDdU[0] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_X));
+ rhs = yExtent * fAWdU[2] + zExtent * fAWdU[1];
+ if (fAWxDdU[0] > rhs) {
+ vars.release();
+ return false;
+ }
+
+ fAWxDdU[1] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Y));
+ rhs = xExtent * fAWdU[2] + zExtent * fAWdU[0];
+ if (fAWxDdU[1] > rhs) {
+ vars.release();
+ return false;
+ }
+
+ fAWxDdU[2] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Z));
+ rhs = xExtent * fAWdU[1] + yExtent * fAWdU[0];
+ if (fAWxDdU[2] > rhs) {
+ vars.release();
+ return false;
+ }
+
+ vars.release();
+ return true;
+ }
+
+ /**
+ * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
+ */
+ private int collideWithRay(Ray ray, CollisionResults results) {
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = vars.vect1.set(ray.origin).subtractLocal(center);
+ Vector3f direction = vars.vect2.set(ray.direction);
+
+ float[] t = {0f, Float.POSITIVE_INFINITY};
+
+ float saveT0 = t[0], saveT1 = t[1];
+ boolean notEntirelyClipped = clip(+direction.x, -diff.x - xExtent, t)
+ && clip(-direction.x, +diff.x - xExtent, t)
+ && clip(+direction.y, -diff.y - yExtent, t)
+ && clip(-direction.y, +diff.y - yExtent, t)
+ && clip(+direction.z, -diff.z - zExtent, t)
+ && clip(-direction.z, +diff.z - zExtent, t);
+ vars.release();
+
+ if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) {
+ if (t[1] > t[0]) {
+ float[] distances = t;
+ Vector3f[] points = new Vector3f[]{
+ new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
+ new Vector3f(ray.direction).multLocal(distances[1]).addLocal(ray.origin)
+ };
+
+ CollisionResult result = new CollisionResult(points[0], distances[0]);
+ results.addCollision(result);
+ result = new CollisionResult(points[1], distances[1]);
+ results.addCollision(result);
+ return 2;
+ }
+
+ Vector3f point = new Vector3f(ray.direction).multLocal(t[0]).addLocal(ray.origin);
+ CollisionResult result = new CollisionResult(point, t[0]);
+ results.addCollision(result);
+ return 1;
+ }
+ return 0;
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) {
+ if (other instanceof Ray) {
+ Ray ray = (Ray) other;
+ return collideWithRay(ray, results);
+ } else if (other instanceof Triangle) {
+ Triangle t = (Triangle) other;
+ if (intersects(t.get1(), t.get2(), t.get3())) {
+ CollisionResult r = new CollisionResult();
+ results.addCollision(r);
+ return 1;
+ }
+ return 0;
+ } else {
+ throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
+ }
+ }
+
+ /**
+ * C code ported from <a href="http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt">
+ * http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt</a>
+ *
+ * @param v1 The first point in the triangle
+ * @param v2 The second point in the triangle
+ * @param v3 The third point in the triangle
+ * @return True if the bounding box intersects the triangle, false
+ * otherwise.
+ */
+ public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3) {
+ return Intersection.intersect(this, v1, v2, v3);
+ }
+
+ @Override
+ public boolean contains(Vector3f point) {
+ return FastMath.abs(center.x - point.x) < xExtent
+ && FastMath.abs(center.y - point.y) < yExtent
+ && FastMath.abs(center.z - point.z) < zExtent;
+ }
+
+ @Override
+ public boolean intersects(Vector3f point) {
+ return FastMath.abs(center.x - point.x) <= xExtent
+ && FastMath.abs(center.y - point.y) <= yExtent
+ && FastMath.abs(center.z - point.z) <= zExtent;
+ }
+
+ public float distanceToEdge(Vector3f point) {
+ // compute coordinates of point in box coordinate system
+ TempVars vars= TempVars.get();
+ Vector3f closest = vars.vect1;
+
+ point.subtract(center,closest);
+
+ // project test point onto box
+ float sqrDistance = 0.0f;
+ float delta;
+
+ if (closest.x < -xExtent) {
+ delta = closest.x + xExtent;
+ sqrDistance += delta * delta;
+ closest.x = -xExtent;
+ } else if (closest.x > xExtent) {
+ delta = closest.x - xExtent;
+ sqrDistance += delta * delta;
+ closest.x = xExtent;
+ }
+
+ if (closest.y < -yExtent) {
+ delta = closest.y + yExtent;
+ sqrDistance += delta * delta;
+ closest.y = -yExtent;
+ } else if (closest.y > yExtent) {
+ delta = closest.y - yExtent;
+ sqrDistance += delta * delta;
+ closest.y = yExtent;
+ }
+
+ if (closest.z < -zExtent) {
+ delta = closest.z + zExtent;
+ sqrDistance += delta * delta;
+ closest.z = -zExtent;
+ } else if (closest.z > zExtent) {
+ delta = closest.z - zExtent;
+ sqrDistance += delta * delta;
+ closest.z = zExtent;
+ }
+
+ vars.release();
+ return FastMath.sqrt(sqrDistance);
+ }
+
+ /**
+ * <code>clip</code> determines if a line segment intersects the current
+ * test plane.
+ *
+ * @param denom
+ * the denominator of the line segment.
+ * @param numer
+ * the numerator of the line segment.
+ * @param t
+ * test values of the plane.
+ * @return true if the line segment intersects the plane, false otherwise.
+ */
+ private boolean clip(float denom, float numer, float[] t) {
+ // Return value is 'true' if line segment intersects the current test
+ // plane. Otherwise 'false' is returned in which case the line segment
+ // is entirely clipped.
+ if (denom > 0.0f) {
+ if (numer > denom * t[1]) {
+ return false;
+ }
+ if (numer > denom * t[0]) {
+ t[0] = numer / denom;
+ }
+ return true;
+ } else if (denom < 0.0f) {
+ if (numer > denom * t[0]) {
+ return false;
+ }
+ if (numer > denom * t[1]) {
+ t[1] = numer / denom;
+ }
+ return true;
+ } else {
+ return numer <= 0.0;
+ }
+ }
+
+ /**
+ * Query extent.
+ *
+ * @param store
+ * where extent gets stored - null to return a new vector
+ * @return store / new vector
+ */
+ public Vector3f getExtent(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.set(xExtent, yExtent, zExtent);
+ return store;
+ }
+
+ public float getXExtent() {
+ return xExtent;
+ }
+
+ public float getYExtent() {
+ return yExtent;
+ }
+
+ public float getZExtent() {
+ return zExtent;
+ }
+
+ public void setXExtent(float xExtent) {
+ if (xExtent < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ this.xExtent = xExtent;
+ }
+
+ public void setYExtent(float yExtent) {
+ if (yExtent < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ this.yExtent = yExtent;
+ }
+
+ public void setZExtent(float zExtent) {
+ if (zExtent < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ this.zExtent = zExtent;
+ }
+
+ public Vector3f getMin(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.set(center).subtractLocal(xExtent, yExtent, zExtent);
+ return store;
+ }
+
+ public Vector3f getMax(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.set(center).addLocal(xExtent, yExtent, zExtent);
+ return store;
+ }
+
+ public void setMinMax(Vector3f min, Vector3f max) {
+ this.center.set(max).addLocal(min).multLocal(0.5f);
+ xExtent = FastMath.abs(max.x - center.x);
+ yExtent = FastMath.abs(max.y - center.y);
+ zExtent = FastMath.abs(max.z - center.z);
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(xExtent, "xExtent", 0);
+ capsule.write(yExtent, "yExtent", 0);
+ capsule.write(zExtent, "zExtent", 0);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ xExtent = capsule.readFloat("xExtent", 0);
+ yExtent = capsule.readFloat("yExtent", 0);
+ zExtent = capsule.readFloat("zExtent", 0);
+ }
+
+ @Override
+ public float getVolume() {
+ return (8 * xExtent * yExtent * zExtent);
+ }
+}
diff --git a/engine/src/core/com/jme3/bounding/BoundingSphere.java b/engine/src/core/com/jme3/bounding/BoundingSphere.java
new file mode 100644
index 0000000..12be035
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingSphere.java
@@ -0,0 +1,858 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bounding;
+
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>BoundingSphere</code> defines a sphere that defines a container for a
+ * group of vertices of a particular piece of geometry. This sphere defines a
+ * radius and a center. <br>
+ * <br>
+ * A typical usage is to allow the class define the center and radius by calling
+ * either <code>containAABB</code> or <code>averagePoints</code>. A call to
+ * <code>computeFramePoint</code> in turn calls <code>containAABB</code>.
+ *
+ * @author Mark Powell
+ * @version $Id: BoundingSphere.java,v 1.59 2007/08/17 10:34:26 rherlitz Exp $
+ */
+public class BoundingSphere extends BoundingVolume {
+
+ private static final Logger logger =
+ Logger.getLogger(BoundingSphere.class.getName());
+ float radius;
+ private static final float RADIUS_EPSILON = 1f + 0.00001f;
+
+ /**
+ * Default contstructor instantiates a new <code>BoundingSphere</code>
+ * object.
+ */
+ public BoundingSphere() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>BoundingSphere</code> object.
+ *
+ * @param r
+ * the radius of the sphere.
+ * @param c
+ * the center of the sphere.
+ */
+ public BoundingSphere(float r, Vector3f c) {
+ this.center.set(c);
+ this.radius = r;
+ }
+
+ public Type getType() {
+ return Type.Sphere;
+ }
+
+ /**
+ * <code>getRadius</code> returns the radius of the bounding sphere.
+ *
+ * @return the radius of the bounding sphere.
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * <code>setRadius</code> sets the radius of this bounding sphere.
+ *
+ * @param radius
+ * the new radius of the bounding sphere.
+ */
+ public void setRadius(float radius) {
+ this.radius = radius;
+ }
+
+ /**
+ * <code>computeFromPoints</code> creates a new Bounding Sphere from a
+ * given set of points. It uses the <code>calcWelzl</code> method as
+ * default.
+ *
+ * @param points
+ * the points to contain.
+ */
+ public void computeFromPoints(FloatBuffer points) {
+ calcWelzl(points);
+ }
+
+ /**
+ * <code>computeFromTris</code> creates a new Bounding Box from a given
+ * set of triangles. It is used in OBBTree calculations.
+ *
+ * @param tris
+ * @param start
+ * @param end
+ */
+ public void computeFromTris(Triangle[] tris, int start, int end) {
+ if (end - start <= 0) {
+ return;
+ }
+
+ Vector3f[] vertList = new Vector3f[(end - start) * 3];
+
+ int count = 0;
+ for (int i = start; i < end; i++) {
+ vertList[count++] = tris[i].get(0);
+ vertList[count++] = tris[i].get(1);
+ vertList[count++] = tris[i].get(2);
+ }
+ averagePoints(vertList);
+ }
+//
+// /**
+// * <code>computeFromTris</code> creates a new Bounding Box from a given
+// * set of triangles. It is used in OBBTree calculations.
+// *
+// * @param indices
+// * @param mesh
+// * @param start
+// * @param end
+// */
+// public void computeFromTris(int[] indices, Mesh mesh, int start, int end) {
+// if (end - start <= 0) {
+// return;
+// }
+//
+// Vector3f[] vertList = new Vector3f[(end - start) * 3];
+//
+// int count = 0;
+// for (int i = start; i < end; i++) {
+// mesh.getTriangle(indices[i], verts);
+// vertList[count++] = new Vector3f(verts[0]);
+// vertList[count++] = new Vector3f(verts[1]);
+// vertList[count++] = new Vector3f(verts[2]);
+// }
+//
+// averagePoints(vertList);
+// }
+
+ /**
+ * Calculates a minimum bounding sphere for the set of points. The algorithm
+ * was originally found in C++ at
+ * <p><a href="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1">
+ * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1</a><br><strong>broken link</strong></p>
+ * <p>and translated to java by Cep21</p>
+ *
+ * @param points
+ * The points to calculate the minimum bounds from.
+ */
+ public void calcWelzl(FloatBuffer points) {
+ if (center == null) {
+ center = new Vector3f();
+ }
+ FloatBuffer buf = BufferUtils.createFloatBuffer(points.limit());
+ points.rewind();
+ buf.put(points);
+ buf.flip();
+ recurseMini(buf, buf.limit() / 3, 0, 0);
+ }
+
+ /**
+ * Used from calcWelzl. This function recurses to calculate a minimum
+ * bounding sphere a few points at a time.
+ *
+ * @param points
+ * The array of points to look through.
+ * @param p
+ * The size of the list to be used.
+ * @param b
+ * The number of points currently considering to include with the
+ * sphere.
+ * @param ap
+ * A variable simulating pointer arithmatic from C++, and offset
+ * in <code>points</code>.
+ */
+ private void recurseMini(FloatBuffer points, int p, int b, int ap) {
+ //TempVars vars = TempVars.get();
+
+ Vector3f tempA = new Vector3f(); //vars.vect1;
+ Vector3f tempB = new Vector3f(); //vars.vect2;
+ Vector3f tempC = new Vector3f(); //vars.vect3;
+ Vector3f tempD = new Vector3f(); //vars.vect4;
+
+ switch (b) {
+ case 0:
+ this.radius = 0;
+ this.center.set(0, 0, 0);
+ break;
+ case 1:
+ this.radius = 1f - RADIUS_EPSILON;
+ BufferUtils.populateFromBuffer(center, points, ap - 1);
+ break;
+ case 2:
+ BufferUtils.populateFromBuffer(tempA, points, ap - 1);
+ BufferUtils.populateFromBuffer(tempB, points, ap - 2);
+ setSphere(tempA, tempB);
+ break;
+ case 3:
+ BufferUtils.populateFromBuffer(tempA, points, ap - 1);
+ BufferUtils.populateFromBuffer(tempB, points, ap - 2);
+ BufferUtils.populateFromBuffer(tempC, points, ap - 3);
+ setSphere(tempA, tempB, tempC);
+ break;
+ case 4:
+ BufferUtils.populateFromBuffer(tempA, points, ap - 1);
+ BufferUtils.populateFromBuffer(tempB, points, ap - 2);
+ BufferUtils.populateFromBuffer(tempC, points, ap - 3);
+ BufferUtils.populateFromBuffer(tempD, points, ap - 4);
+ setSphere(tempA, tempB, tempC, tempD);
+ //vars.release();
+ return;
+ }
+ for (int i = 0; i < p; i++) {
+ BufferUtils.populateFromBuffer(tempA, points, i + ap);
+ if (tempA.distanceSquared(center) - (radius * radius) > RADIUS_EPSILON - 1f) {
+ for (int j = i; j > 0; j--) {
+ BufferUtils.populateFromBuffer(tempB, points, j + ap);
+ BufferUtils.populateFromBuffer(tempC, points, j - 1 + ap);
+ BufferUtils.setInBuffer(tempC, points, j + ap);
+ BufferUtils.setInBuffer(tempB, points, j - 1 + ap);
+ }
+ recurseMini(points, i, b + 1, ap + 1);
+ }
+ }
+ //vars.release();
+ }
+
+ /**
+ * Calculates the minimum bounding sphere of 4 points. Used in welzl's
+ * algorithm.
+ *
+ * @param O
+ * The 1st point inside the sphere.
+ * @param A
+ * The 2nd point inside the sphere.
+ * @param B
+ * The 3rd point inside the sphere.
+ * @param C
+ * The 4th point inside the sphere.
+ * @see #calcWelzl(java.nio.FloatBuffer)
+ */
+ private void setSphere(Vector3f O, Vector3f A, Vector3f B, Vector3f C) {
+ Vector3f a = A.subtract(O);
+ Vector3f b = B.subtract(O);
+ Vector3f c = C.subtract(O);
+
+ float Denominator = 2.0f * (a.x * (b.y * c.z - c.y * b.z) - b.x
+ * (a.y * c.z - c.y * a.z) + c.x * (a.y * b.z - b.y * a.z));
+ if (Denominator == 0) {
+ center.set(0, 0, 0);
+ radius = 0;
+ } else {
+ Vector3f o = a.cross(b).multLocal(c.lengthSquared()).addLocal(
+ c.cross(a).multLocal(b.lengthSquared())).addLocal(
+ b.cross(c).multLocal(a.lengthSquared())).divideLocal(
+ Denominator);
+
+ radius = o.length() * RADIUS_EPSILON;
+ O.add(o, center);
+ }
+ }
+
+ /**
+ * Calculates the minimum bounding sphere of 3 points. Used in welzl's
+ * algorithm.
+ *
+ * @param O
+ * The 1st point inside the sphere.
+ * @param A
+ * The 2nd point inside the sphere.
+ * @param B
+ * The 3rd point inside the sphere.
+ * @see #calcWelzl(java.nio.FloatBuffer)
+ */
+ private void setSphere(Vector3f O, Vector3f A, Vector3f B) {
+ Vector3f a = A.subtract(O);
+ Vector3f b = B.subtract(O);
+ Vector3f acrossB = a.cross(b);
+
+ float Denominator = 2.0f * acrossB.dot(acrossB);
+
+ if (Denominator == 0) {
+ center.set(0, 0, 0);
+ radius = 0;
+ } else {
+
+ Vector3f o = acrossB.cross(a).multLocal(b.lengthSquared()).addLocal(b.cross(acrossB).multLocal(a.lengthSquared())).divideLocal(Denominator);
+ radius = o.length() * RADIUS_EPSILON;
+ O.add(o, center);
+ }
+ }
+
+ /**
+ * Calculates the minimum bounding sphere of 2 points. Used in welzl's
+ * algorithm.
+ *
+ * @param O
+ * The 1st point inside the sphere.
+ * @param A
+ * The 2nd point inside the sphere.
+ * @see #calcWelzl(java.nio.FloatBuffer)
+ */
+ private void setSphere(Vector3f O, Vector3f A) {
+ radius = FastMath.sqrt(((A.x - O.x) * (A.x - O.x) + (A.y - O.y)
+ * (A.y - O.y) + (A.z - O.z) * (A.z - O.z)) / 4f) + RADIUS_EPSILON - 1f;
+ center.interpolate(O, A, .5f);
+ }
+
+ /**
+ * <code>averagePoints</code> selects the sphere center to be the average
+ * of the points and the sphere radius to be the smallest value to enclose
+ * all points.
+ *
+ * @param points
+ * the list of points to contain.
+ */
+ public void averagePoints(Vector3f[] points) {
+ logger.info("Bounding Sphere calculated using average points.");
+ center = points[0];
+
+ for (int i = 1; i < points.length; i++) {
+ center.addLocal(points[i]);
+ }
+
+ float quantity = 1.0f / points.length;
+ center.multLocal(quantity);
+
+ float maxRadiusSqr = 0;
+ for (int i = 0; i < points.length; i++) {
+ Vector3f diff = points[i].subtract(center);
+ float radiusSqr = diff.lengthSquared();
+ if (radiusSqr > maxRadiusSqr) {
+ maxRadiusSqr = radiusSqr;
+ }
+ }
+
+ radius = (float) Math.sqrt(maxRadiusSqr) + RADIUS_EPSILON - 1f;
+
+ }
+
+ /**
+ * <code>transform</code> modifies the center of the sphere to reflect the
+ * change made via a rotation, translation and scale.
+ *
+ * @param trans
+ * the transform to apply
+ * @param store
+ * sphere to store result in
+ * @return BoundingVolume
+ * @return ref
+ */
+ public BoundingVolume transform(Transform trans, BoundingVolume store) {
+ BoundingSphere sphere;
+ if (store == null || store.getType() != BoundingVolume.Type.Sphere) {
+ sphere = new BoundingSphere(1, new Vector3f(0, 0, 0));
+ } else {
+ sphere = (BoundingSphere) store;
+ }
+
+ center.mult(trans.getScale(), sphere.center);
+ trans.getRotation().mult(sphere.center, sphere.center);
+ sphere.center.addLocal(trans.getTranslation());
+ sphere.radius = FastMath.abs(getMaxAxis(trans.getScale()) * radius) + RADIUS_EPSILON - 1f;
+ return sphere;
+ }
+
+ public BoundingVolume transform(Matrix4f trans, BoundingVolume store) {
+ BoundingSphere sphere;
+ if (store == null || store.getType() != BoundingVolume.Type.Sphere) {
+ sphere = new BoundingSphere(1, new Vector3f(0, 0, 0));
+ } else {
+ sphere = (BoundingSphere) store;
+ }
+
+ trans.mult(center, sphere.center);
+ Vector3f axes = new Vector3f(1, 1, 1);
+ trans.mult(axes, axes);
+ float ax = getMaxAxis(axes);
+ sphere.radius = FastMath.abs(ax * radius) + RADIUS_EPSILON - 1f;
+ return sphere;
+ }
+
+ private float getMaxAxis(Vector3f scale) {
+ float x = FastMath.abs(scale.x);
+ float y = FastMath.abs(scale.y);
+ float z = FastMath.abs(scale.z);
+
+ if (x >= y) {
+ if (x >= z) {
+ return x;
+ }
+ return z;
+ }
+
+ if (y >= z) {
+ return y;
+ }
+
+ return z;
+ }
+
+ /**
+ * <code>whichSide</code> takes a plane (typically provided by a view
+ * frustum) to determine which side this bound is on.
+ *
+ * @param plane
+ * the plane to check against.
+ * @return side
+ */
+ public Plane.Side whichSide(Plane plane) {
+ float distance = plane.pseudoDistance(center);
+
+ if (distance <= -radius) {
+ return Plane.Side.Negative;
+ } else if (distance >= radius) {
+ return Plane.Side.Positive;
+ } else {
+ return Plane.Side.None;
+ }
+ }
+
+ /**
+ * <code>merge</code> combines this sphere with a second bounding sphere.
+ * This new sphere contains both bounding spheres and is returned.
+ *
+ * @param volume
+ * the sphere to combine with this sphere.
+ * @return a new sphere
+ */
+ public BoundingVolume merge(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+
+ case Sphere: {
+ BoundingSphere sphere = (BoundingSphere) volume;
+ float temp_radius = sphere.getRadius();
+ Vector3f temp_center = sphere.center;
+ BoundingSphere rVal = new BoundingSphere();
+ return merge(temp_radius, temp_center, rVal);
+ }
+
+ case AABB: {
+ BoundingBox box = (BoundingBox) volume;
+ Vector3f radVect = new Vector3f(box.xExtent, box.yExtent,
+ box.zExtent);
+ Vector3f temp_center = box.center;
+ BoundingSphere rVal = new BoundingSphere();
+ return merge(radVect.length(), temp_center, rVal);
+ }
+
+// case OBB: {
+// OrientedBoundingBox box = (OrientedBoundingBox) volume;
+// BoundingSphere rVal = (BoundingSphere) this.clone(null);
+// return rVal.mergeOBB(box);
+// }
+
+ default:
+ return null;
+
+ }
+ }
+
+ /**
+ * <code>mergeLocal</code> combines this sphere with a second bounding
+ * sphere locally. Altering this sphere to contain both the original and the
+ * additional sphere volumes;
+ *
+ * @param volume
+ * the sphere to combine with this sphere.
+ * @return this
+ */
+ public BoundingVolume mergeLocal(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+
+ case Sphere: {
+ BoundingSphere sphere = (BoundingSphere) volume;
+ float temp_radius = sphere.getRadius();
+ Vector3f temp_center = sphere.center;
+ return merge(temp_radius, temp_center, this);
+ }
+
+ case AABB: {
+ BoundingBox box = (BoundingBox) volume;
+ TempVars vars = TempVars.get();
+ Vector3f radVect = vars.vect1;
+ radVect.set(box.xExtent, box.yExtent, box.zExtent);
+ Vector3f temp_center = box.center;
+ float len = radVect.length();
+ vars.release();
+ return merge(len, temp_center, this);
+ }
+
+// case OBB: {
+// return mergeOBB((OrientedBoundingBox) volume);
+// }
+
+ default:
+ return null;
+ }
+ }
+
+// /**
+// * Merges this sphere with the given OBB.
+// *
+// * @param volume
+// * The OBB to merge.
+// * @return This sphere, after merging.
+// */
+// private BoundingSphere mergeOBB(OrientedBoundingBox volume) {
+// // compute edge points from the obb
+// if (!volume.correctCorners)
+// volume.computeCorners();
+// _mergeBuf.rewind();
+// for (int i = 0; i < 8; i++) {
+// _mergeBuf.put(volume.vectorStore[i].x);
+// _mergeBuf.put(volume.vectorStore[i].y);
+// _mergeBuf.put(volume.vectorStore[i].z);
+// }
+//
+// // remember old radius and center
+// float oldRadius = radius;
+// Vector3f oldCenter = _compVect2.set( center );
+//
+// // compute new radius and center from obb points
+// computeFromPoints(_mergeBuf);
+// Vector3f newCenter = _compVect3.set( center );
+// float newRadius = radius;
+//
+// // restore old center and radius
+// center.set( oldCenter );
+// radius = oldRadius;
+//
+// //merge obb points result
+// merge( newRadius, newCenter, this );
+//
+// return this;
+// }
+ private BoundingVolume merge(float temp_radius, Vector3f temp_center,
+ BoundingSphere rVal) {
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = temp_center.subtract(center, vars.vect1);
+ float lengthSquared = diff.lengthSquared();
+ float radiusDiff = temp_radius - radius;
+
+ float fRDiffSqr = radiusDiff * radiusDiff;
+
+ if (fRDiffSqr >= lengthSquared) {
+ if (radiusDiff <= 0.0f) {
+ vars.release();
+ return this;
+ }
+
+ Vector3f rCenter = rVal.center;
+ if (rCenter == null) {
+ rVal.setCenter(rCenter = new Vector3f());
+ }
+ rCenter.set(temp_center);
+ rVal.setRadius(temp_radius);
+ vars.release();
+ return rVal;
+ }
+
+ float length = (float) Math.sqrt(lengthSquared);
+
+ Vector3f rCenter = rVal.center;
+ if (rCenter == null) {
+ rVal.setCenter(rCenter = new Vector3f());
+ }
+ if (length > RADIUS_EPSILON) {
+ float coeff = (length + radiusDiff) / (2.0f * length);
+ rCenter.set(center.addLocal(diff.multLocal(coeff)));
+ } else {
+ rCenter.set(center);
+ }
+
+ rVal.setRadius(0.5f * (length + radius + temp_radius));
+ vars.release();
+ return rVal;
+ }
+
+ /**
+ * <code>clone</code> creates a new BoundingSphere object containing the
+ * same data as this one.
+ *
+ * @param store
+ * where to store the cloned information. if null or wrong class,
+ * a new store is created.
+ * @return the new BoundingSphere
+ */
+ public BoundingVolume clone(BoundingVolume store) {
+ if (store != null && store.getType() == Type.Sphere) {
+ BoundingSphere rVal = (BoundingSphere) store;
+ if (null == rVal.center) {
+ rVal.center = new Vector3f();
+ }
+ rVal.center.set(center);
+ rVal.radius = radius;
+ rVal.checkPlane = checkPlane;
+ return rVal;
+ }
+
+ return new BoundingSphere(radius,
+ (center != null ? (Vector3f) center.clone() : null));
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this object.
+ * The form is: "Radius: RRR.SSSS Center: <Vector>".
+ *
+ * @return the string representation of this.
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [Radius: " + radius + " Center: "
+ + center + "]";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume)
+ */
+ public boolean intersects(BoundingVolume bv) {
+ return bv.intersectsSphere(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
+ */
+ public boolean intersectsSphere(BoundingSphere bs) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
+
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = center.subtract(bs.center, vars.vect1);
+ float rsum = getRadius() + bs.getRadius();
+ boolean eq = (diff.dot(diff) <= rsum * rsum);
+ vars.release();
+ return eq;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
+ */
+ public boolean intersectsBoundingBox(BoundingBox bb) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
+
+ if (FastMath.abs(bb.center.x - center.x) < getRadius()
+ + bb.xExtent
+ && FastMath.abs(bb.center.y - center.y) < getRadius()
+ + bb.yExtent
+ && FastMath.abs(bb.center.z - center.z) < getRadius()
+ + bb.zExtent) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox)
+ */
+// public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
+// return obb.intersectsSphere(this);
+// }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)
+ */
+ public boolean intersects(Ray ray) {
+ assert Vector3f.isValidVector(center);
+
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal(center);
+ float radiusSquared = getRadius() * getRadius();
+ float a = diff.dot(diff) - radiusSquared;
+ if (a <= 0.0) {
+ // in sphere
+ return true;
+ }
+
+ // outside sphere
+ float b = ray.getDirection().dot(diff);
+ vars.release();
+ if (b >= 0.0) {
+ return false;
+ }
+ return b * b >= a;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
+ */
+ private int collideWithRay(Ray ray, CollisionResults results) {
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal(
+ center);
+ float a = diff.dot(diff) - (getRadius() * getRadius());
+ float a1, discr, root;
+ if (a <= 0.0) {
+ // inside sphere
+ a1 = ray.direction.dot(diff);
+ discr = (a1 * a1) - a;
+ root = FastMath.sqrt(discr);
+
+ float distance = root - a1;
+ Vector3f point = new Vector3f(ray.direction).multLocal(distance).addLocal(ray.origin);
+
+ CollisionResult result = new CollisionResult(point, distance);
+ results.addCollision(result);
+ vars.release();
+ return 1;
+ }
+
+ a1 = ray.direction.dot(diff);
+ vars.release();
+ if (a1 >= 0.0) {
+ return 0;
+ }
+
+ discr = a1 * a1 - a;
+ if (discr < 0.0) {
+ return 0;
+ } else if (discr >= FastMath.ZERO_TOLERANCE) {
+ root = FastMath.sqrt(discr);
+ float dist = -a1 - root;
+ Vector3f point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin);
+ results.addCollision(new CollisionResult(point, dist));
+
+ dist = -a1 + root;
+ point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin);
+ results.addCollision(new CollisionResult(point, dist));
+ return 2;
+ } else {
+ float dist = -a1;
+ Vector3f point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin);
+ results.addCollision(new CollisionResult(point, dist));
+ return 1;
+ }
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) {
+ if (other instanceof Ray) {
+ Ray ray = (Ray) other;
+ return collideWithRay(ray, results);
+ } else if (other instanceof Triangle){
+ Triangle t = (Triangle) other;
+
+ float r2 = radius * radius;
+ float d1 = center.distanceSquared(t.get1());
+ float d2 = center.distanceSquared(t.get2());
+ float d3 = center.distanceSquared(t.get3());
+
+ if (d1 <= r2 || d2 <= r2 || d3 <= r2) {
+ CollisionResult r = new CollisionResult();
+ r.setDistance(FastMath.sqrt(Math.min(Math.min(d1, d2), d3)) - radius);
+ results.addCollision(r);
+ return 1;
+ }
+
+ return 0;
+ } else {
+ throw new UnsupportedCollisionException();
+ }
+ }
+
+ @Override
+ public boolean contains(Vector3f point) {
+ return center.distanceSquared(point) < (getRadius() * getRadius());
+ }
+
+ @Override
+ public boolean intersects(Vector3f point) {
+ return center.distanceSquared(point) <= (getRadius() * getRadius());
+ }
+
+ public float distanceToEdge(Vector3f point) {
+ return center.distance(point) - radius;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ try {
+ e.getCapsule(this).write(radius, "radius", 0);
+ } catch (IOException ex) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "write(JMEExporter)", "Exception", ex);
+ }
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ try {
+ radius = e.getCapsule(this).readFloat("radius", 0);
+ } catch (IOException ex) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "read(JMEImporter)", "Exception", ex);
+ }
+ }
+
+ @Override
+ public float getVolume() {
+ return 4 * FastMath.ONE_THIRD * FastMath.PI * radius * radius * radius;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/bounding/BoundingVolume.java b/engine/src/core/com/jme3/bounding/BoundingVolume.java
new file mode 100644
index 0000000..8db2e57
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingVolume.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.bounding;
+
+import com.jme3.collision.Collidable;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import com.jme3.math.*;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * <code>BoundingVolume</code> defines an interface for dealing with
+ * containment of a collection of points.
+ *
+ * @author Mark Powell
+ * @version $Id: BoundingVolume.java,v 1.24 2007/09/21 15:45:32 nca Exp $
+ */
+public abstract class BoundingVolume implements Savable, Cloneable, Collidable {
+
+ /**
+ * The type of bounding volume being used.
+ */
+ public enum Type {
+ /**
+ * {@link BoundingSphere}
+ */
+ Sphere,
+
+ /**
+ * {@link BoundingBox}.
+ */
+ AABB,
+
+ /**
+ * {@link com.jme3.bounding.OrientedBoundingBox}
+ */
+ OBB,
+
+ /**
+ * Currently unsupported by jME3.
+ */
+ Capsule;
+ }
+
+ protected int checkPlane = 0;
+ protected Vector3f center = new Vector3f();
+
+ public BoundingVolume() {
+ }
+
+ public BoundingVolume(Vector3f center) {
+ this.center.set(center);
+ }
+
+ /**
+ * Grabs the checkplane we should check first.
+ *
+ */
+ public int getCheckPlane() {
+ return checkPlane;
+ }
+
+ /**
+ * Sets the index of the plane that should be first checked during rendering.
+ *
+ * @param value
+ */
+ public final void setCheckPlane(int value) {
+ checkPlane = value;
+ }
+
+ /**
+ * getType returns the type of bounding volume this is.
+ */
+ public abstract Type getType();
+
+ /**
+ *
+ * <code>transform</code> alters the location of the bounding volume by a
+ * rotation, translation and a scalar.
+ *
+ * @param trans
+ * the transform to affect the bound.
+ * @return the new bounding volume.
+ */
+ public final BoundingVolume transform(Transform trans) {
+ return transform(trans, null);
+ }
+
+ /**
+ *
+ * <code>transform</code> alters the location of the bounding volume by a
+ * rotation, translation and a scalar.
+ *
+ * @param trans
+ * the transform to affect the bound.
+ * @param store
+ * sphere to store result in
+ * @return the new bounding volume.
+ */
+ public abstract BoundingVolume transform(Transform trans, BoundingVolume store);
+
+ public abstract BoundingVolume transform(Matrix4f trans, BoundingVolume store);
+
+ /**
+ *
+ * <code>whichSide</code> returns the side on which the bounding volume
+ * lies on a plane. Possible values are POSITIVE_SIDE, NEGATIVE_SIDE, and
+ * NO_SIDE.
+ *
+ * @param plane
+ * the plane to check against this bounding volume.
+ * @return the side on which this bounding volume lies.
+ */
+ public abstract Plane.Side whichSide(Plane plane);
+
+ /**
+ *
+ * <code>computeFromPoints</code> generates a bounding volume that
+ * encompasses a collection of points.
+ *
+ * @param points
+ * the points to contain.
+ */
+ public abstract void computeFromPoints(FloatBuffer points);
+
+ /**
+ * <code>merge</code> combines two bounding volumes into a single bounding
+ * volume that contains both this bounding volume and the parameter volume.
+ *
+ * @param volume
+ * the volume to combine.
+ * @return the new merged bounding volume.
+ */
+ public abstract BoundingVolume merge(BoundingVolume volume);
+
+ /**
+ * <code>mergeLocal</code> combines two bounding volumes into a single
+ * bounding volume that contains both this bounding volume and the parameter
+ * volume. The result is stored locally.
+ *
+ * @param volume
+ * the volume to combine.
+ * @return this
+ */
+ public abstract BoundingVolume mergeLocal(BoundingVolume volume);
+
+ /**
+ * <code>clone</code> creates a new BoundingVolume object containing the
+ * same data as this one.
+ *
+ * @param store
+ * where to store the cloned information. if null or wrong class,
+ * a new store is created.
+ * @return the new BoundingVolume
+ */
+ public abstract BoundingVolume clone(BoundingVolume store);
+
+ public final Vector3f getCenter() {
+ return center;
+ }
+
+ public final Vector3f getCenter(Vector3f store) {
+ store.set(center);
+ return store;
+ }
+
+ public final void setCenter(Vector3f newCenter) {
+ center.set(newCenter);
+ }
+
+ /**
+ * Find the distance from the center of this Bounding Volume to the given
+ * point.
+ *
+ * @param point
+ * The point to get the distance to
+ * @return distance
+ */
+ public final float distanceTo(Vector3f point) {
+ return center.distance(point);
+ }
+
+ /**
+ * Find the squared distance from the center of this Bounding Volume to the
+ * given point.
+ *
+ * @param point
+ * The point to get the distance to
+ * @return distance
+ */
+ public final float distanceSquaredTo(Vector3f point) {
+ return center.distanceSquared(point);
+ }
+
+ /**
+ * Find the distance from the nearest edge of this Bounding Volume to the given
+ * point.
+ *
+ * @param point
+ * The point to get the distance to
+ * @return distance
+ */
+ public abstract float distanceToEdge(Vector3f point);
+
+ /**
+ * determines if this bounding volume and a second given volume are
+ * intersecting. Intersecting being: one volume contains another, one volume
+ * overlaps another or one volume touches another.
+ *
+ * @param bv
+ * the second volume to test against.
+ * @return true if this volume intersects the given volume.
+ */
+ public abstract boolean intersects(BoundingVolume bv);
+
+ /**
+ * determines if a ray intersects this bounding volume.
+ *
+ * @param ray
+ * the ray to test.
+ * @return true if this volume is intersected by a given ray.
+ */
+ public abstract boolean intersects(Ray ray);
+
+
+ /**
+ * determines if this bounding volume and a given bounding sphere are
+ * intersecting.
+ *
+ * @param bs
+ * the bounding sphere to test against.
+ * @return true if this volume intersects the given bounding sphere.
+ */
+ public abstract boolean intersectsSphere(BoundingSphere bs);
+
+ /**
+ * determines if this bounding volume and a given bounding box are
+ * intersecting.
+ *
+ * @param bb
+ * the bounding box to test against.
+ * @return true if this volume intersects the given bounding box.
+ */
+ public abstract boolean intersectsBoundingBox(BoundingBox bb);
+
+ /**
+ * determines if this bounding volume and a given bounding box are
+ * intersecting.
+ *
+ * @param bb
+ * the bounding box to test against.
+ * @return true if this volume intersects the given bounding box.
+ */
+// public abstract boolean intersectsOrientedBoundingBox(OrientedBoundingBox bb);
+ /**
+ *
+ * determines if a given point is contained within this bounding volume.
+ * If the point is on the edge of the bounding volume, this method will
+ * return false. Use intersects(Vector3f) to check for edge intersection.
+ *
+ * @param point
+ * the point to check
+ * @return true if the point lies within this bounding volume.
+ */
+ public abstract boolean contains(Vector3f point);
+
+ /**
+ * Determines if a given point intersects (touches or is inside) this bounding volume.
+ * @param point the point to check
+ * @return true if the point lies within this bounding volume.
+ */
+ public abstract boolean intersects(Vector3f point);
+
+ public abstract float getVolume();
+
+ @Override
+ public BoundingVolume clone() {
+ try{
+ BoundingVolume clone = (BoundingVolume) super.clone();
+ clone.center = center.clone();
+ return clone;
+ }catch (CloneNotSupportedException ex){
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ e.getCapsule(this).write(center, "center", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ center = (Vector3f) e.getCapsule(this).readSavable("center", Vector3f.ZERO.clone());
+ }
+
+}
+
diff --git a/engine/src/core/com/jme3/bounding/Intersection.java b/engine/src/core/com/jme3/bounding/Intersection.java
new file mode 100644
index 0000000..c53b792
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/Intersection.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bounding;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import com.jme3.util.TempVars;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+/**
+ * This class includes some utility methods for computing intersection
+ * between bounding volumes and triangles.
+ * @author Kirill
+ */
+public class Intersection {
+
+ private static final void findMinMax(float x0, float x1, float x2, Vector3f minMax) {
+ minMax.set(x0, x0, 0);
+ if (x1 < minMax.x) {
+ minMax.setX(x1);
+ }
+ if (x1 > minMax.y) {
+ minMax.setY(x1);
+ }
+ if (x2 < minMax.x) {
+ minMax.setX(x2);
+ }
+ if (x2 > minMax.y) {
+ minMax.setY(x2);
+ }
+ }
+
+// private boolean axisTest(float a, float b, float fa, float fb, Vector3f v0, Vector3f v1, )
+// private boolean axisTestX01(float a, float b, float fa, float fb,
+// Vector3f center, Vector3f ext,
+// Vector3f v1, Vector3f v2, Vector3f v3){
+// float p0 = a * v0.y - b * v0.z;
+// float p2 = a * v2.y - b * v2.z;
+// if(p0 < p2){
+// min = p0;
+// max = p2;
+// } else {
+// min = p2;
+// max = p0;
+// }
+// float rad = fa * boxhalfsize.y + fb * boxhalfsize.z;
+// if(min > rad || max < -rad)
+// return false;
+// }
+ public static boolean intersect(BoundingBox bbox, Vector3f v1, Vector3f v2, Vector3f v3) {
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ TempVars vars = TempVars.get();
+
+
+ Vector3f tmp0 = vars.vect1,
+ tmp1 = vars.vect2,
+ tmp2 = vars.vect3;
+
+ Vector3f e0 = vars.vect4,
+ e1 = vars.vect5,
+ e2 = vars.vect6;
+
+ Vector3f center = bbox.getCenter();
+ Vector3f extent = bbox.getExtent(null);
+
+// float min,max,p0,p1,p2,rad,fex,fey,fez;
+// float normal[3]
+
+ // This is the fastest branch on Sun
+ // move everything so that the boxcenter is in (0,0,0)
+ v1.subtract(center, tmp0);
+ v2.subtract(center, tmp1);
+ v3.subtract(center, tmp2);
+
+ // compute triangle edges
+ tmp1.subtract(tmp0, e0); // tri edge 0
+ tmp2.subtract(tmp1, e1); // tri edge 1
+ tmp0.subtract(tmp2, e2); // tri edge 2
+
+ // Bullet 3:
+ // test the 9 tests first (this was faster)
+ float min, max;
+ float p0, p1, p2, rad;
+ float fex = FastMath.abs(e0.x);
+ float fey = FastMath.abs(e0.y);
+ float fez = FastMath.abs(e0.z);
+
+
+
+ //AXISTEST_X01(e0[Z], e0[Y], fez, fey);
+ p0 = e0.z * tmp0.y - e0.y * tmp0.z;
+ p2 = e0.z * tmp2.y - e0.y * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.y + fey * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Y02(e0[Z], e0[X], fez, fex);
+ p0 = -e0.z * tmp0.x + e0.x * tmp0.z;
+ p2 = -e0.z * tmp2.x + e0.x * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.x + fex * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Z12(e0[Y], e0[X], fey, fex);
+ p1 = e0.y * tmp1.x - e0.x * tmp1.y;
+ p2 = e0.y * tmp2.x - e0.x * tmp2.y;
+ min = min(p1, p2);
+ max = max(p1, p2);
+ rad = fey * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ fex = FastMath.abs(e1.x);
+ fey = FastMath.abs(e1.y);
+ fez = FastMath.abs(e1.z);
+
+// AXISTEST_X01(e1[Z], e1[Y], fez, fey);
+ p0 = e1.z * tmp0.y - e1.y * tmp0.z;
+ p2 = e1.z * tmp2.y - e1.y * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.y + fey * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Y02(e1[Z], e1[X], fez, fex);
+ p0 = -e1.z * tmp0.x + e1.x * tmp0.z;
+ p2 = -e1.z * tmp2.x + e1.x * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.x + fex * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Z0(e1[Y], e1[X], fey, fex);
+ p0 = e1.y * tmp0.x - e1.x * tmp0.y;
+ p1 = e1.y * tmp1.x - e1.x * tmp1.y;
+ min = min(p0, p1);
+ max = max(p0, p1);
+ rad = fey * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+//
+ fex = FastMath.abs(e2.x);
+ fey = FastMath.abs(e2.y);
+ fez = FastMath.abs(e2.z);
+
+ // AXISTEST_X2(e2[Z], e2[Y], fez, fey);
+ p0 = e2.z * tmp0.y - e2.y * tmp0.z;
+ p1 = e2.z * tmp1.y - e2.y * tmp1.z;
+ min = min(p0, p1);
+ max = max(p0, p1);
+ rad = fez * extent.y + fey * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Y1(e2[Z], e2[X], fez, fex);
+ p0 = -e2.z * tmp0.x + e2.x * tmp0.z;
+ p1 = -e2.z * tmp1.x + e2.x * tmp1.z;
+ min = min(p0, p1);
+ max = max(p0, p1);
+ rad = fez * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+// AXISTEST_Z12(e2[Y], e2[X], fey, fex);
+ p1 = e2.y * tmp1.x - e2.x * tmp1.y;
+ p2 = e2.y * tmp2.x - e2.x * tmp2.y;
+ min = min(p1, p2);
+ max = max(p1, p2);
+ rad = fey * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // Bullet 1:
+ // first test overlap in the {x,y,z}-directions
+ // find min, max of the triangle each direction, and test for overlap in
+ // that direction -- this is equivalent to testing a minimal AABB around
+ // the triangle against the AABB
+
+
+ Vector3f minMax = vars.vect7;
+
+ // test in X-direction
+ findMinMax(tmp0.x, tmp1.x, tmp2.x, minMax);
+ if (minMax.x > extent.x || minMax.y < -extent.x) {
+ vars.release();
+ return false;
+ }
+
+ // test in Y-direction
+ findMinMax(tmp0.y, tmp1.y, tmp2.y, minMax);
+ if (minMax.x > extent.y || minMax.y < -extent.y) {
+ vars.release();
+ return false;
+ }
+
+ // test in Z-direction
+ findMinMax(tmp0.z, tmp1.z, tmp2.z, minMax);
+ if (minMax.x > extent.z || minMax.y < -extent.z) {
+ vars.release();
+ return false;
+ }
+
+// // Bullet 2:
+// // test if the box intersects the plane of the triangle
+// // compute plane equation of triangle: normal * x + d = 0
+// Vector3f normal = new Vector3f();
+// e0.cross(e1, normal);
+ Plane p = vars.plane;
+
+ p.setPlanePoints(v1, v2, v3);
+ if (bbox.whichSide(p) == Plane.Side.Negative) {
+ vars.release();
+ return false;
+ }
+//
+// if(!planeBoxOverlap(normal,v0,boxhalfsize)) return false;
+
+ vars.release();
+
+ return true; /* box and triangle overlaps */
+ }
+}
diff --git a/engine/src/core/com/jme3/bounding/OrientedBoundingBox.java b/engine/src/core/com/jme3/bounding/OrientedBoundingBox.java
new file mode 100644
index 0000000..f383a94
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/OrientedBoundingBox.java
@@ -0,0 +1,1522 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.bounding;
+
+/**
+ * NOTE: This class has been commented out as it has too many dependencies.
+ */
+
+
+//
+//import java.io.IOException;
+//import java.nio.FloatBuffer;
+//
+////import com.jme.scene.TriMesh;
+//
+///**
+// * Started Date: Sep 5, 2004 <br>
+// * <br>
+// *
+// * @author Jack Lindamood
+// * @author Joshua Slack (alterations for .9)
+// * @version $Id: OrientedBoundingBox.java,v 1.35 2007/09/21 15:45:31 nca Exp $
+// */
+//public class OrientedBoundingBox extends BoundingVolume {
+//
+// private static final long serialVersionUID = 1L;
+//
+// static private final Vector3f _compVect3 = new Vector3f();
+//
+// static private final Vector3f _compVect4 = new Vector3f();
+//
+// static private final Vector3f _compVect5 = new Vector3f();
+//
+// static private final Vector3f _compVect6 = new Vector3f();
+//
+// static private final Vector3f _compVect7 = new Vector3f();
+//
+// static private final Vector3f _compVect8 = new Vector3f();
+//
+// static private final Vector3f _compVect9 = new Vector3f();
+//
+// static private final Vector3f _compVect10 = new Vector3f();
+//
+// static private final Vector3f tempVe = new Vector3f();
+//
+// static private final Matrix3f tempMa = new Matrix3f();
+//
+// static private final Quaternion tempQa = new Quaternion();
+//
+// static private final Quaternion tempQb = new Quaternion();
+//
+// private static final float[] fWdU = new float[3];
+//
+// private static final float[] fAWdU = new float[3];
+//
+// private static final float[] fDdU = new float[3];
+//
+// private static final float[] fADdU = new float[3];
+//
+// private static final float[] fAWxDdU = new float[3];
+//
+// private static final float[] tempFa = new float[3];
+//
+// private static final float[] tempFb = new float[3];
+//
+// /** X axis of the Oriented Box. */
+// public final Vector3f xAxis = new Vector3f(1, 0, 0);
+//
+// /** Y axis of the Oriented Box. */
+// public final Vector3f yAxis = new Vector3f(0, 1, 0);
+//
+// /** Z axis of the Oriented Box. */
+// public final Vector3f zAxis = new Vector3f(0, 0, 1);
+//
+// /** Extents of the box along the x,y,z axis. */
+// public final Vector3f extent = new Vector3f(0, 0, 0);
+//
+// /** Vector array used to store the array of 8 corners the box has. */
+// public final Vector3f[] vectorStore = new Vector3f[8];
+//
+// private final Vector3f tempVk = new Vector3f();
+// private final Vector3f tempForword = new Vector3f(0, 0, 1);
+// private final Vector3f tempLeft = new Vector3f(1, 0, 0);
+// private final Vector3f tempUp = new Vector3f(0, 1, 0);
+//
+// static private final FloatBuffer _mergeBuf = BufferUtils
+// .createVector3Buffer(16);
+//
+// /**
+// * If true, the box's vectorStore array correctly represents the box's
+// * corners.
+// */
+// public boolean correctCorners = false;
+//
+// public OrientedBoundingBox() {
+// for (int x = 0; x < 8; x++)
+// vectorStore[x] = new Vector3f();
+// }
+//
+// public Type getType() {
+// return Type.OBB;
+// }
+//
+// public BoundingVolume transform(Quaternion rotate, Vector3f translate,
+// Vector3f scale, BoundingVolume store) {
+// rotate.toRotationMatrix(tempMa);
+// return transform(tempMa, translate, scale, store);
+// }
+//
+// public BoundingVolume transform(Matrix3f rotate, Vector3f translate,
+// Vector3f scale, BoundingVolume store) {
+// if (store == null || store.getType() != Type.OBB) {
+// store = new OrientedBoundingBox();
+// }
+// OrientedBoundingBox toReturn = (OrientedBoundingBox) store;
+// toReturn.extent.set(FastMath.abs(extent.x * scale.x),
+// FastMath.abs(extent.y * scale.y),
+// FastMath.abs(extent.z * scale.z));
+// rotate.mult(xAxis, toReturn.xAxis);
+// rotate.mult(yAxis, toReturn.yAxis);
+// rotate.mult(zAxis, toReturn.zAxis);
+// center.mult(scale, toReturn.center);
+// rotate.mult(toReturn.center, toReturn.center);
+// toReturn.center.addLocal(translate);
+// toReturn.correctCorners = false;
+// return toReturn;
+// }
+//
+// public int whichSide(Plane plane) {
+// float fRadius = FastMath.abs(extent.x * (plane.getNormal().dot(xAxis)))
+// + FastMath.abs(extent.y * (plane.getNormal().dot(yAxis)))
+// + FastMath.abs(extent.z * (plane.getNormal().dot(zAxis)));
+// float fDistance = plane.pseudoDistance(center);
+// if (fDistance <= -fRadius)
+// return Plane.NEGATIVE_SIDE;
+// else if (fDistance >= fRadius)
+// return Plane.POSITIVE_SIDE;
+// else
+// return Plane.NO_SIDE;
+// }
+//
+// public void computeFromPoints(FloatBuffer points) {
+// containAABB(points);
+// }
+//
+// /**
+// * Calculates an AABB of the given point values for this OBB.
+// *
+// * @param points
+// * The points this OBB should contain.
+// */
+// private void containAABB(FloatBuffer points) {
+// if (points == null || points.limit() <= 2) { // we need at least a 3
+// // float vector
+// return;
+// }
+//
+// BufferUtils.populateFromBuffer(_compVect1, points, 0);
+// float minX = _compVect1.x, minY = _compVect1.y, minZ = _compVect1.z;
+// float maxX = _compVect1.x, maxY = _compVect1.y, maxZ = _compVect1.z;
+//
+// for (int i = 1, len = points.limit() / 3; i < len; i++) {
+// BufferUtils.populateFromBuffer(_compVect1, points, i);
+//
+// if (_compVect1.x < minX)
+// minX = _compVect1.x;
+// else if (_compVect1.x > maxX)
+// maxX = _compVect1.x;
+//
+// if (_compVect1.y < minY)
+// minY = _compVect1.y;
+// else if (_compVect1.y > maxY)
+// maxY = _compVect1.y;
+//
+// if (_compVect1.z < minZ)
+// minZ = _compVect1.z;
+// else if (_compVect1.z > maxZ)
+// maxZ = _compVect1.z;
+// }
+//
+// center.set(minX + maxX, minY + maxY, minZ + maxZ);
+// center.multLocal(0.5f);
+//
+// extent.set(maxX - center.x, maxY - center.y, maxZ - center.z);
+//
+// xAxis.set(1, 0, 0);
+// yAxis.set(0, 1, 0);
+// zAxis.set(0, 0, 1);
+//
+// correctCorners = false;
+// }
+//
+// public BoundingVolume merge(BoundingVolume volume) {
+// // clone ourselves into a new bounding volume, then merge.
+// return clone(new OrientedBoundingBox()).mergeLocal(volume);
+// }
+//
+// public BoundingVolume mergeLocal(BoundingVolume volume) {
+// if (volume == null)
+// return this;
+//
+// switch (volume.getType()) {
+//
+// case OBB: {
+// return mergeOBB((OrientedBoundingBox) volume);
+// }
+//
+// case AABB: {
+// return mergeAABB((BoundingBox) volume);
+// }
+//
+// case Sphere: {
+// return mergeSphere((BoundingSphere) volume);
+// }
+//
+// default:
+// return null;
+//
+// }
+// }
+//
+// private BoundingVolume mergeSphere(BoundingSphere volume) {
+// BoundingSphere mergeSphere = volume;
+// if (!correctCorners)
+// this.computeCorners();
+//
+// _mergeBuf.rewind();
+// for (int i = 0; i < 8; i++) {
+// _mergeBuf.put(vectorStore[i].x);
+// _mergeBuf.put(vectorStore[i].y);
+// _mergeBuf.put(vectorStore[i].z);
+// }
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// containAABB(_mergeBuf);
+// correctCorners = false;
+// return this;
+// }
+//
+// private BoundingVolume mergeAABB(BoundingBox volume) {
+// BoundingBox mergeBox = volume;
+// if (!correctCorners)
+// this.computeCorners();
+//
+// _mergeBuf.rewind();
+// for (int i = 0; i < 8; i++) {
+// _mergeBuf.put(vectorStore[i].x);
+// _mergeBuf.put(vectorStore[i].y);
+// _mergeBuf.put(vectorStore[i].z);
+// }
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// containAABB(_mergeBuf);
+// correctCorners = false;
+// return this;
+// }
+//
+// private BoundingVolume mergeOBB(OrientedBoundingBox volume) {
+// // OrientedBoundingBox mergeBox=(OrientedBoundingBox) volume;
+// // if (!correctCorners) this.computeCorners();
+// // if (!mergeBox.correctCorners) mergeBox.computeCorners();
+// // Vector3f[] mergeArray=new Vector3f[16];
+// // for (int i=0;i<vectorStore.length;i++){
+// // mergeArray[i*2+0]=this .vectorStore[i];
+// // mergeArray[i*2+1]=mergeBox.vectorStore[i];
+// // }
+// // containAABB(mergeArray);
+// // correctCorners=false;
+// // return this;
+// // construct a box that contains the input boxes
+// // Box3<Real> kBox;
+// OrientedBoundingBox rkBox0 = this;
+// OrientedBoundingBox rkBox1 = volume;
+//
+// // The first guess at the box center. This value will be updated later
+// // after the input box vertices are projected onto axes determined by an
+// // average of box axes.
+// Vector3f kBoxCenter = (rkBox0.center.add(rkBox1.center, _compVect7))
+// .multLocal(.5f);
+//
+// // A box's axes, when viewed as the columns of a matrix, form a rotation
+// // matrix. The input box axes are converted to quaternions. The average
+// // quaternion is computed, then normalized to unit length. The result is
+// // the slerp of the two input quaternions with t-value of 1/2. The
+// // result is converted back to a rotation matrix and its columns are
+// // selected as the merged box axes.
+// Quaternion kQ0 = tempQa, kQ1 = tempQb;
+// kQ0.fromAxes(rkBox0.xAxis, rkBox0.yAxis, rkBox0.zAxis);
+// kQ1.fromAxes(rkBox1.xAxis, rkBox1.yAxis, rkBox1.zAxis);
+//
+// if (kQ0.dot(kQ1) < 0.0f)
+// kQ1.negate();
+//
+// Quaternion kQ = kQ0.addLocal(kQ1);
+// kQ.normalize();
+//
+// Matrix3f kBoxaxis = kQ.toRotationMatrix(tempMa);
+// Vector3f newXaxis = kBoxaxis.getColumn(0, _compVect8);
+// Vector3f newYaxis = kBoxaxis.getColumn(1, _compVect9);
+// Vector3f newZaxis = kBoxaxis.getColumn(2, _compVect10);
+//
+// // Project the input box vertices onto the merged-box axes. Each axis
+// // D[i] containing the current center C has a minimum projected value
+// // pmin[i] and a maximum projected value pmax[i]. The corresponding end
+// // points on the axes are C+pmin[i]*D[i] and C+pmax[i]*D[i]. The point C
+// // is not necessarily the midpoint for any of the intervals. The actual
+// // box center will be adjusted from C to a point C' that is the midpoint
+// // of each interval,
+// // C' = C + sum_{i=0}^1 0.5*(pmin[i]+pmax[i])*D[i]
+// // The box extents are
+// // e[i] = 0.5*(pmax[i]-pmin[i])
+//
+// int i;
+// float fDot;
+// Vector3f kDiff = _compVect4;
+// Vector3f kMin = _compVect5;
+// Vector3f kMax = _compVect6;
+// kMin.zero();
+// kMax.zero();
+//
+// if (!rkBox0.correctCorners)
+// rkBox0.computeCorners();
+// for (i = 0; i < 8; i++) {
+// rkBox0.vectorStore[i].subtract(kBoxCenter, kDiff);
+//
+// fDot = kDiff.dot(newXaxis);
+// if (fDot > kMax.x)
+// kMax.x = fDot;
+// else if (fDot < kMin.x)
+// kMin.x = fDot;
+//
+// fDot = kDiff.dot(newYaxis);
+// if (fDot > kMax.y)
+// kMax.y = fDot;
+// else if (fDot < kMin.y)
+// kMin.y = fDot;
+//
+// fDot = kDiff.dot(newZaxis);
+// if (fDot > kMax.z)
+// kMax.z = fDot;
+// else if (fDot < kMin.z)
+// kMin.z = fDot;
+//
+// }
+//
+// if (!rkBox1.correctCorners)
+// rkBox1.computeCorners();
+// for (i = 0; i < 8; i++) {
+// rkBox1.vectorStore[i].subtract(kBoxCenter, kDiff);
+//
+// fDot = kDiff.dot(newXaxis);
+// if (fDot > kMax.x)
+// kMax.x = fDot;
+// else if (fDot < kMin.x)
+// kMin.x = fDot;
+//
+// fDot = kDiff.dot(newYaxis);
+// if (fDot > kMax.y)
+// kMax.y = fDot;
+// else if (fDot < kMin.y)
+// kMin.y = fDot;
+//
+// fDot = kDiff.dot(newZaxis);
+// if (fDot > kMax.z)
+// kMax.z = fDot;
+// else if (fDot < kMin.z)
+// kMin.z = fDot;
+// }
+//
+// this.xAxis.set(newXaxis);
+// this.yAxis.set(newYaxis);
+// this.zAxis.set(newZaxis);
+//
+// this.extent.x = .5f * (kMax.x - kMin.x);
+// kBoxCenter.addLocal(this.xAxis.mult(.5f * (kMax.x + kMin.x), tempVe));
+//
+// this.extent.y = .5f * (kMax.y - kMin.y);
+// kBoxCenter.addLocal(this.yAxis.mult(.5f * (kMax.y + kMin.y), tempVe));
+//
+// this.extent.z = .5f * (kMax.z - kMin.z);
+// kBoxCenter.addLocal(this.zAxis.mult(.5f * (kMax.z + kMin.z), tempVe));
+//
+// this.center.set(kBoxCenter);
+//
+// this.correctCorners = false;
+// return this;
+// }
+//
+// public BoundingVolume clone(BoundingVolume store) {
+// OrientedBoundingBox toReturn;
+// if (store instanceof OrientedBoundingBox) {
+// toReturn = (OrientedBoundingBox) store;
+// } else {
+// toReturn = new OrientedBoundingBox();
+// }
+// toReturn.extent.set(extent);
+// toReturn.xAxis.set(xAxis);
+// toReturn.yAxis.set(yAxis);
+// toReturn.zAxis.set(zAxis);
+// toReturn.center.set(center);
+// toReturn.checkPlane = checkPlane;
+// for (int x = vectorStore.length; --x >= 0; )
+// toReturn.vectorStore[x].set(vectorStore[x]);
+// toReturn.correctCorners = this.correctCorners;
+// return toReturn;
+// }
+//
+// /**
+// * Sets the vectorStore information to the 8 corners of the box.
+// */
+// public void computeCorners() {
+// Vector3f akEAxis0 = xAxis.mult(extent.x, _compVect1);
+// Vector3f akEAxis1 = yAxis.mult(extent.y, _compVect2);
+// Vector3f akEAxis2 = zAxis.mult(extent.z, _compVect3);
+//
+// vectorStore[0].set(center).subtractLocal(akEAxis0).subtractLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[1].set(center).addLocal(akEAxis0).subtractLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[2].set(center).addLocal(akEAxis0).addLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[3].set(center).subtractLocal(akEAxis0).addLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[4].set(center).subtractLocal(akEAxis0).subtractLocal(akEAxis1).addLocal(akEAxis2);
+// vectorStore[5].set(center).addLocal(akEAxis0).subtractLocal(akEAxis1).addLocal(akEAxis2);
+// vectorStore[6].set(center).addLocal(akEAxis0).addLocal(akEAxis1).addLocal(akEAxis2);
+// vectorStore[7].set(center).subtractLocal(akEAxis0).addLocal(akEAxis1).addLocal(akEAxis2);
+// correctCorners = true;
+// }
+//
+//// public void computeFromTris(int[] indices, TriMesh mesh, int start, int end) {
+//// if (end - start <= 0) {
+//// return;
+//// }
+//// Vector3f[] verts = new Vector3f[3];
+//// Vector3f min = _compVect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+//// Vector3f max = _compVect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+//// Vector3f point;
+//// for (int i = start; i < end; i++) {
+//// mesh.getTriangle(indices[i], verts);
+//// point = verts[0];
+//// if (point.x < min.x)
+//// min.x = point.x;
+//// else if (point.x > max.x)
+//// max.x = point.x;
+//// if (point.y < min.y)
+//// min.y = point.y;
+//// else if (point.y > max.y)
+//// max.y = point.y;
+//// if (point.z < min.z)
+//// min.z = point.z;
+//// else if (point.z > max.z)
+//// max.z = point.z;
+////
+//// point = verts[1];
+//// if (point.x < min.x)
+//// min.x = point.x;
+//// else if (point.x > max.x)
+//// max.x = point.x;
+//// if (point.y < min.y)
+//// min.y = point.y;
+//// else if (point.y > max.y)
+//// max.y = point.y;
+//// if (point.z < min.z)
+//// min.z = point.z;
+//// else if (point.z > max.z)
+//// max.z = point.z;
+////
+//// point = verts[2];
+//// if (point.x < min.x)
+//// min.x = point.x;
+//// else if (point.x > max.x)
+//// max.x = point.x;
+////
+//// if (point.y < min.y)
+//// min.y = point.y;
+//// else if (point.y > max.y)
+//// max.y = point.y;
+////
+//// if (point.z < min.z)
+//// min.z = point.z;
+//// else if (point.z > max.z)
+//// max.z = point.z;
+//// }
+////
+//// center.set(min.addLocal(max));
+//// center.multLocal(0.5f);
+////
+//// extent.set(max.x - center.x, max.y - center.y, max.z - center.z);
+////
+//// xAxis.set(1, 0, 0);
+//// yAxis.set(0, 1, 0);
+//// zAxis.set(0, 0, 1);
+////
+//// correctCorners = false;
+//// }
+//
+// public void computeFromTris(Triangle[] tris, int start, int end) {
+// if (end - start <= 0) {
+// return;
+// }
+//
+// Vector3f min = _compVect1.set(tris[start].get(0));
+// Vector3f max = _compVect2.set(min);
+// Vector3f point;
+// for (int i = start; i < end; i++) {
+//
+// point = tris[i].get(0);
+// if (point.x < min.x)
+// min.x = point.x;
+// else if (point.x > max.x)
+// max.x = point.x;
+// if (point.y < min.y)
+// min.y = point.y;
+// else if (point.y > max.y)
+// max.y = point.y;
+// if (point.z < min.z)
+// min.z = point.z;
+// else if (point.z > max.z)
+// max.z = point.z;
+//
+// point = tris[i].get(1);
+// if (point.x < min.x)
+// min.x = point.x;
+// else if (point.x > max.x)
+// max.x = point.x;
+// if (point.y < min.y)
+// min.y = point.y;
+// else if (point.y > max.y)
+// max.y = point.y;
+// if (point.z < min.z)
+// min.z = point.z;
+// else if (point.z > max.z)
+// max.z = point.z;
+//
+// point = tris[i].get(2);
+// if (point.x < min.x)
+// min.x = point.x;
+// else if (point.x > max.x)
+// max.x = point.x;
+//
+// if (point.y < min.y)
+// min.y = point.y;
+// else if (point.y > max.y)
+// max.y = point.y;
+//
+// if (point.z < min.z)
+// min.z = point.z;
+// else if (point.z > max.z)
+// max.z = point.z;
+// }
+//
+// center.set(min.addLocal(max));
+// center.multLocal(0.5f);
+//
+// extent.set(max.x - center.x, max.y - center.y, max.z - center.z);
+//
+// xAxis.set(1, 0, 0);
+// yAxis.set(0, 1, 0);
+// zAxis.set(0, 0, 1);
+//
+// correctCorners = false;
+// }
+//
+// public boolean intersection(OrientedBoundingBox box1) {
+// // Cutoff for cosine of angles between box axes. This is used to catch
+// // the cases when at least one pair of axes are parallel. If this
+// // happens,
+// // there is no need to test for separation along the Cross(A[i],B[j])
+// // directions.
+// OrientedBoundingBox box0 = this;
+// float cutoff = 0.999999f;
+// boolean parallelPairExists = false;
+// int i;
+//
+// // convenience variables
+// Vector3f akA[] = new Vector3f[] { box0.xAxis, box0.yAxis, box0.zAxis };
+// Vector3f[] akB = new Vector3f[] { box1.xAxis, box1.yAxis, box1.zAxis };
+// Vector3f afEA = box0.extent;
+// Vector3f afEB = box1.extent;
+//
+// // compute difference of box centers, D = C1-C0
+// Vector3f kD = box1.center.subtract(box0.center, _compVect1);
+//
+// float[][] aafC = { fWdU, fAWdU, fDdU };
+//
+// float[][] aafAbsC = { fADdU, fAWxDdU, tempFa };
+//
+// float[] afAD = tempFb;
+// float fR0, fR1, fR; // interval radii and distance between centers
+// float fR01; // = R0 + R1
+//
+// // axis C0+t*A0
+// for (i = 0; i < 3; i++) {
+// aafC[0][i] = akA[0].dot(akB[i]);
+// aafAbsC[0][i] = FastMath.abs(aafC[0][i]);
+// if (aafAbsC[0][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[0] = akA[0].dot(kD);
+// fR = FastMath.abs(afAD[0]);
+// fR1 = afEB.x * aafAbsC[0][0] + afEB.y * aafAbsC[0][1] + afEB.z
+// * aafAbsC[0][2];
+// fR01 = afEA.x + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1
+// for (i = 0; i < 3; i++) {
+// aafC[1][i] = akA[1].dot(akB[i]);
+// aafAbsC[1][i] = FastMath.abs(aafC[1][i]);
+// if (aafAbsC[1][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[1] = akA[1].dot(kD);
+// fR = FastMath.abs(afAD[1]);
+// fR1 = afEB.x * aafAbsC[1][0] + afEB.y * aafAbsC[1][1] + afEB.z
+// * aafAbsC[1][2];
+// fR01 = afEA.y + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2
+// for (i = 0; i < 3; i++) {
+// aafC[2][i] = akA[2].dot(akB[i]);
+// aafAbsC[2][i] = FastMath.abs(aafC[2][i]);
+// if (aafAbsC[2][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[2] = akA[2].dot(kD);
+// fR = FastMath.abs(afAD[2]);
+// fR1 = afEB.x * aafAbsC[2][0] + afEB.y * aafAbsC[2][1] + afEB.z
+// * aafAbsC[2][2];
+// fR01 = afEA.z + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B0
+// fR = FastMath.abs(akB[0].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][0] + afEA.y * aafAbsC[1][0] + afEA.z
+// * aafAbsC[2][0];
+// fR01 = fR0 + afEB.x;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B1
+// fR = FastMath.abs(akB[1].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][1] + afEA.y * aafAbsC[1][1] + afEA.z
+// * aafAbsC[2][1];
+// fR01 = fR0 + afEB.y;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B2
+// fR = FastMath.abs(akB[2].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][2] + afEA.y * aafAbsC[1][2] + afEA.z
+// * aafAbsC[2][2];
+// fR01 = fR0 + afEB.z;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // At least one pair of box axes was parallel, so the separation is
+// // effectively in 2D where checking the "edge" normals is sufficient for
+// // the separation of the boxes.
+// if (parallelPairExists) {
+// return true;
+// }
+//
+// // axis C0+t*A0xB0
+// fR = FastMath.abs(afAD[2] * aafC[1][0] - afAD[1] * aafC[2][0]);
+// fR0 = afEA.y * aafAbsC[2][0] + afEA.z * aafAbsC[1][0];
+// fR1 = afEB.y * aafAbsC[0][2] + afEB.z * aafAbsC[0][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB1
+// fR = FastMath.abs(afAD[2] * aafC[1][1] - afAD[1] * aafC[2][1]);
+// fR0 = afEA.y * aafAbsC[2][1] + afEA.z * aafAbsC[1][1];
+// fR1 = afEB.x * aafAbsC[0][2] + afEB.z * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB2
+// fR = FastMath.abs(afAD[2] * aafC[1][2] - afAD[1] * aafC[2][2]);
+// fR0 = afEA.y * aafAbsC[2][2] + afEA.z * aafAbsC[1][2];
+// fR1 = afEB.x * aafAbsC[0][1] + afEB.y * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB0
+// fR = FastMath.abs(afAD[0] * aafC[2][0] - afAD[2] * aafC[0][0]);
+// fR0 = afEA.x * aafAbsC[2][0] + afEA.z * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[1][2] + afEB.z * aafAbsC[1][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB1
+// fR = FastMath.abs(afAD[0] * aafC[2][1] - afAD[2] * aafC[0][1]);
+// fR0 = afEA.x * aafAbsC[2][1] + afEA.z * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[1][2] + afEB.z * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB2
+// fR = FastMath.abs(afAD[0] * aafC[2][2] - afAD[2] * aafC[0][2]);
+// fR0 = afEA.x * aafAbsC[2][2] + afEA.z * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[1][1] + afEB.y * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB0
+// fR = FastMath.abs(afAD[1] * aafC[0][0] - afAD[0] * aafC[1][0]);
+// fR0 = afEA.x * aafAbsC[1][0] + afEA.y * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[2][2] + afEB.z * aafAbsC[2][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB1
+// fR = FastMath.abs(afAD[1] * aafC[0][1] - afAD[0] * aafC[1][1]);
+// fR0 = afEA.x * aafAbsC[1][1] + afEA.y * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[2][2] + afEB.z * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB2
+// fR = FastMath.abs(afAD[1] * aafC[0][2] - afAD[0] * aafC[1][2]);
+// fR0 = afEA.x * aafAbsC[1][2] + afEA.y * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[2][1] + afEB.y * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// return true;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume)
+// */
+// public boolean intersects(BoundingVolume bv) {
+// if (bv == null)
+// return false;
+//
+// return bv.intersectsOrientedBoundingBox(this);
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
+// */
+// public boolean intersectsSphere(BoundingSphere bs) {
+// if (!Vector3f.isValidVector(center) || !Vector3f.isValidVector(bs.center)) return false;
+//
+// _compVect1.set(bs.getCenter()).subtractLocal(center);
+// tempMa.fromAxes(xAxis, yAxis, zAxis);
+//
+// tempMa.mult(_compVect1, _compVect2);
+//
+// if (FastMath.abs(_compVect2.x) < bs.getRadius() + extent.x
+// && FastMath.abs(_compVect2.y) < bs.getRadius() + extent.y
+// && FastMath.abs(_compVect2.z) < bs.getRadius() + extent.z)
+// return true;
+//
+// return false;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
+// */
+// public boolean intersectsBoundingBox(BoundingBox bb) {
+// if (!Vector3f.isValidVector(center) || !Vector3f.isValidVector(bb.center)) return false;
+//
+// // Cutoff for cosine of angles between box axes. This is used to catch
+// // the cases when at least one pair of axes are parallel. If this
+// // happens,
+// // there is no need to test for separation along the Cross(A[i],B[j])
+// // directions.
+// float cutoff = 0.999999f;
+// boolean parallelPairExists = false;
+// int i;
+//
+// // convenience variables
+// Vector3f akA[] = new Vector3f[] { xAxis, yAxis, zAxis };
+// Vector3f[] akB = new Vector3f[] { tempForword, tempLeft, tempUp };
+// Vector3f afEA = extent;
+// Vector3f afEB = tempVk.set(bb.xExtent, bb.yExtent, bb.zExtent);
+//
+// // compute difference of box centers, D = C1-C0
+// Vector3f kD = bb.getCenter().subtract(center, _compVect1);
+//
+// float[][] aafC = { fWdU, fAWdU, fDdU };
+//
+// float[][] aafAbsC = { fADdU, fAWxDdU, tempFa };
+//
+// float[] afAD = tempFb;
+// float fR0, fR1, fR; // interval radii and distance between centers
+// float fR01; // = R0 + R1
+//
+// // axis C0+t*A0
+// for (i = 0; i < 3; i++) {
+// aafC[0][i] = akA[0].dot(akB[i]);
+// aafAbsC[0][i] = FastMath.abs(aafC[0][i]);
+// if (aafAbsC[0][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[0] = akA[0].dot(kD);
+// fR = FastMath.abs(afAD[0]);
+// fR1 = afEB.x * aafAbsC[0][0] + afEB.y * aafAbsC[0][1] + afEB.z
+// * aafAbsC[0][2];
+// fR01 = afEA.x + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1
+// for (i = 0; i < 3; i++) {
+// aafC[1][i] = akA[1].dot(akB[i]);
+// aafAbsC[1][i] = FastMath.abs(aafC[1][i]);
+// if (aafAbsC[1][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[1] = akA[1].dot(kD);
+// fR = FastMath.abs(afAD[1]);
+// fR1 = afEB.x * aafAbsC[1][0] + afEB.y * aafAbsC[1][1] + afEB.z
+// * aafAbsC[1][2];
+// fR01 = afEA.y + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2
+// for (i = 0; i < 3; i++) {
+// aafC[2][i] = akA[2].dot(akB[i]);
+// aafAbsC[2][i] = FastMath.abs(aafC[2][i]);
+// if (aafAbsC[2][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[2] = akA[2].dot(kD);
+// fR = FastMath.abs(afAD[2]);
+// fR1 = afEB.x * aafAbsC[2][0] + afEB.y * aafAbsC[2][1] + afEB.z
+// * aafAbsC[2][2];
+// fR01 = afEA.z + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B0
+// fR = FastMath.abs(akB[0].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][0] + afEA.y * aafAbsC[1][0] + afEA.z
+// * aafAbsC[2][0];
+// fR01 = fR0 + afEB.x;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B1
+// fR = FastMath.abs(akB[1].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][1] + afEA.y * aafAbsC[1][1] + afEA.z
+// * aafAbsC[2][1];
+// fR01 = fR0 + afEB.y;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B2
+// fR = FastMath.abs(akB[2].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][2] + afEA.y * aafAbsC[1][2] + afEA.z
+// * aafAbsC[2][2];
+// fR01 = fR0 + afEB.z;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // At least one pair of box axes was parallel, so the separation is
+// // effectively in 2D where checking the "edge" normals is sufficient for
+// // the separation of the boxes.
+// if (parallelPairExists) {
+// return true;
+// }
+//
+// // axis C0+t*A0xB0
+// fR = FastMath.abs(afAD[2] * aafC[1][0] - afAD[1] * aafC[2][0]);
+// fR0 = afEA.y * aafAbsC[2][0] + afEA.z * aafAbsC[1][0];
+// fR1 = afEB.y * aafAbsC[0][2] + afEB.z * aafAbsC[0][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB1
+// fR = FastMath.abs(afAD[2] * aafC[1][1] - afAD[1] * aafC[2][1]);
+// fR0 = afEA.y * aafAbsC[2][1] + afEA.z * aafAbsC[1][1];
+// fR1 = afEB.x * aafAbsC[0][2] + afEB.z * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB2
+// fR = FastMath.abs(afAD[2] * aafC[1][2] - afAD[1] * aafC[2][2]);
+// fR0 = afEA.y * aafAbsC[2][2] + afEA.z * aafAbsC[1][2];
+// fR1 = afEB.x * aafAbsC[0][1] + afEB.y * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB0
+// fR = FastMath.abs(afAD[0] * aafC[2][0] - afAD[2] * aafC[0][0]);
+// fR0 = afEA.x * aafAbsC[2][0] + afEA.z * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[1][2] + afEB.z * aafAbsC[1][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB1
+// fR = FastMath.abs(afAD[0] * aafC[2][1] - afAD[2] * aafC[0][1]);
+// fR0 = afEA.x * aafAbsC[2][1] + afEA.z * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[1][2] + afEB.z * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB2
+// fR = FastMath.abs(afAD[0] * aafC[2][2] - afAD[2] * aafC[0][2]);
+// fR0 = afEA.x * aafAbsC[2][2] + afEA.z * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[1][1] + afEB.y * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB0
+// fR = FastMath.abs(afAD[1] * aafC[0][0] - afAD[0] * aafC[1][0]);
+// fR0 = afEA.x * aafAbsC[1][0] + afEA.y * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[2][2] + afEB.z * aafAbsC[2][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB1
+// fR = FastMath.abs(afAD[1] * aafC[0][1] - afAD[0] * aafC[1][1]);
+// fR0 = afEA.x * aafAbsC[1][1] + afEA.y * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[2][2] + afEB.z * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB2
+// fR = FastMath.abs(afAD[1] * aafC[0][2] - afAD[0] * aafC[1][2]);
+// fR0 = afEA.x * aafAbsC[1][2] + afEA.y * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[2][1] + afEB.y * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// return true;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersectsOBB2(com.jme.bounding.OBB2)
+// */
+// public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
+// if (!Vector3f.isValidVector(center) || !Vector3f.isValidVector(obb.center)) return false;
+//
+// // Cutoff for cosine of angles between box axes. This is used to catch
+// // the cases when at least one pair of axes are parallel. If this
+// // happens,
+// // there is no need to test for separation along the Cross(A[i],B[j])
+// // directions.
+// float cutoff = 0.999999f;
+// boolean parallelPairExists = false;
+// int i;
+//
+// // convenience variables
+// Vector3f akA[] = new Vector3f[] { xAxis, yAxis, zAxis };
+// Vector3f[] akB = new Vector3f[] { obb.xAxis, obb.yAxis, obb.zAxis };
+// Vector3f afEA = extent;
+// Vector3f afEB = obb.extent;
+//
+// // compute difference of box centers, D = C1-C0
+// Vector3f kD = obb.center.subtract(center, _compVect1);
+//
+// float[][] aafC = { fWdU, fAWdU, fDdU };
+//
+// float[][] aafAbsC = { fADdU, fAWxDdU, tempFa };
+//
+// float[] afAD = tempFb;
+// float fR0, fR1, fR; // interval radii and distance between centers
+// float fR01; // = R0 + R1
+//
+// // axis C0+t*A0
+// for (i = 0; i < 3; i++) {
+// aafC[0][i] = akA[0].dot(akB[i]);
+// aafAbsC[0][i] = FastMath.abs(aafC[0][i]);
+// if (aafAbsC[0][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[0] = akA[0].dot(kD);
+// fR = FastMath.abs(afAD[0]);
+// fR1 = afEB.x * aafAbsC[0][0] + afEB.y * aafAbsC[0][1] + afEB.z
+// * aafAbsC[0][2];
+// fR01 = afEA.x + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1
+// for (i = 0; i < 3; i++) {
+// aafC[1][i] = akA[1].dot(akB[i]);
+// aafAbsC[1][i] = FastMath.abs(aafC[1][i]);
+// if (aafAbsC[1][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[1] = akA[1].dot(kD);
+// fR = FastMath.abs(afAD[1]);
+// fR1 = afEB.x * aafAbsC[1][0] + afEB.y * aafAbsC[1][1] + afEB.z
+// * aafAbsC[1][2];
+// fR01 = afEA.y + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2
+// for (i = 0; i < 3; i++) {
+// aafC[2][i] = akA[2].dot(akB[i]);
+// aafAbsC[2][i] = FastMath.abs(aafC[2][i]);
+// if (aafAbsC[2][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[2] = akA[2].dot(kD);
+// fR = FastMath.abs(afAD[2]);
+// fR1 = afEB.x * aafAbsC[2][0] + afEB.y * aafAbsC[2][1] + afEB.z
+// * aafAbsC[2][2];
+// fR01 = afEA.z + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B0
+// fR = FastMath.abs(akB[0].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][0] + afEA.y * aafAbsC[1][0] + afEA.z
+// * aafAbsC[2][0];
+// fR01 = fR0 + afEB.x;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B1
+// fR = FastMath.abs(akB[1].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][1] + afEA.y * aafAbsC[1][1] + afEA.z
+// * aafAbsC[2][1];
+// fR01 = fR0 + afEB.y;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B2
+// fR = FastMath.abs(akB[2].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][2] + afEA.y * aafAbsC[1][2] + afEA.z
+// * aafAbsC[2][2];
+// fR01 = fR0 + afEB.z;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // At least one pair of box axes was parallel, so the separation is
+// // effectively in 2D where checking the "edge" normals is sufficient for
+// // the separation of the boxes.
+// if (parallelPairExists) {
+// return true;
+// }
+//
+// // axis C0+t*A0xB0
+// fR = FastMath.abs(afAD[2] * aafC[1][0] - afAD[1] * aafC[2][0]);
+// fR0 = afEA.y * aafAbsC[2][0] + afEA.z * aafAbsC[1][0];
+// fR1 = afEB.y * aafAbsC[0][2] + afEB.z * aafAbsC[0][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB1
+// fR = FastMath.abs(afAD[2] * aafC[1][1] - afAD[1] * aafC[2][1]);
+// fR0 = afEA.y * aafAbsC[2][1] + afEA.z * aafAbsC[1][1];
+// fR1 = afEB.x * aafAbsC[0][2] + afEB.z * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB2
+// fR = FastMath.abs(afAD[2] * aafC[1][2] - afAD[1] * aafC[2][2]);
+// fR0 = afEA.y * aafAbsC[2][2] + afEA.z * aafAbsC[1][2];
+// fR1 = afEB.x * aafAbsC[0][1] + afEB.y * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB0
+// fR = FastMath.abs(afAD[0] * aafC[2][0] - afAD[2] * aafC[0][0]);
+// fR0 = afEA.x * aafAbsC[2][0] + afEA.z * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[1][2] + afEB.z * aafAbsC[1][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB1
+// fR = FastMath.abs(afAD[0] * aafC[2][1] - afAD[2] * aafC[0][1]);
+// fR0 = afEA.x * aafAbsC[2][1] + afEA.z * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[1][2] + afEB.z * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB2
+// fR = FastMath.abs(afAD[0] * aafC[2][2] - afAD[2] * aafC[0][2]);
+// fR0 = afEA.x * aafAbsC[2][2] + afEA.z * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[1][1] + afEB.y * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB0
+// fR = FastMath.abs(afAD[1] * aafC[0][0] - afAD[0] * aafC[1][0]);
+// fR0 = afEA.x * aafAbsC[1][0] + afEA.y * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[2][2] + afEB.z * aafAbsC[2][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB1
+// fR = FastMath.abs(afAD[1] * aafC[0][1] - afAD[0] * aafC[1][1]);
+// fR0 = afEA.x * aafAbsC[1][1] + afEA.y * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[2][2] + afEB.z * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB2
+// fR = FastMath.abs(afAD[1] * aafC[0][2] - afAD[0] * aafC[1][2]);
+// fR0 = afEA.x * aafAbsC[1][2] + afEA.y * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[2][1] + afEB.y * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// return true;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)
+// */
+// public boolean intersects(Ray ray) {
+// if (!Vector3f.isValidVector(center)) return false;
+//
+// float rhs;
+// Vector3f diff = ray.origin.subtract(getCenter(_compVect2), _compVect1);
+//
+// fWdU[0] = ray.getDirection().dot(xAxis);
+// fAWdU[0] = FastMath.abs(fWdU[0]);
+// fDdU[0] = diff.dot(xAxis);
+// fADdU[0] = FastMath.abs(fDdU[0]);
+// if (fADdU[0] > extent.x && fDdU[0] * fWdU[0] >= 0.0) {
+// return false;
+// }
+//
+// fWdU[1] = ray.getDirection().dot(yAxis);
+// fAWdU[1] = FastMath.abs(fWdU[1]);
+// fDdU[1] = diff.dot(yAxis);
+// fADdU[1] = FastMath.abs(fDdU[1]);
+// if (fADdU[1] > extent.y && fDdU[1] * fWdU[1] >= 0.0) {
+// return false;
+// }
+//
+// fWdU[2] = ray.getDirection().dot(zAxis);
+// fAWdU[2] = FastMath.abs(fWdU[2]);
+// fDdU[2] = diff.dot(zAxis);
+// fADdU[2] = FastMath.abs(fDdU[2]);
+// if (fADdU[2] > extent.z && fDdU[2] * fWdU[2] >= 0.0) {
+// return false;
+// }
+//
+// Vector3f wCrossD = ray.getDirection().cross(diff, _compVect2);
+//
+// fAWxDdU[0] = FastMath.abs(wCrossD.dot(xAxis));
+// rhs = extent.y * fAWdU[2] + extent.z * fAWdU[1];
+// if (fAWxDdU[0] > rhs) {
+// return false;
+// }
+//
+// fAWxDdU[1] = FastMath.abs(wCrossD.dot(yAxis));
+// rhs = extent.x * fAWdU[2] + extent.z * fAWdU[0];
+// if (fAWxDdU[1] > rhs) {
+// return false;
+// }
+//
+// fAWxDdU[2] = FastMath.abs(wCrossD.dot(zAxis));
+// rhs = extent.x * fAWdU[1] + extent.y * fAWdU[0];
+// if (fAWxDdU[2] > rhs) {
+// return false;
+//
+// }
+//
+// return true;
+// }
+//
+// /**
+// * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
+// */
+// public IntersectionRecord intersectsWhere(Ray ray) {
+// Vector3f diff = _compVect1.set(ray.origin).subtractLocal(center);
+// // convert ray to box coordinates
+// Vector3f direction = _compVect2.set(ray.direction.x, ray.direction.y,
+// ray.direction.z);
+// float[] t = { 0f, Float.POSITIVE_INFINITY };
+//
+// float saveT0 = t[0], saveT1 = t[1];
+// boolean notEntirelyClipped = clip(+direction.x, -diff.x - extent.x, t)
+// && clip(-direction.x, +diff.x - extent.x, t)
+// && clip(+direction.y, -diff.y - extent.y, t)
+// && clip(-direction.y, +diff.y - extent.y, t)
+// && clip(+direction.z, -diff.z - extent.z, t)
+// && clip(-direction.z, +diff.z - extent.z, t);
+//
+// if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) {
+// if (t[1] > t[0]) {
+// float[] distances = t;
+// Vector3f[] points = new Vector3f[] {
+// new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
+// new Vector3f(ray.direction).multLocal(distances[1]).addLocal(ray.origin)
+// };
+// IntersectionRecord record = new IntersectionRecord(distances, points);
+// return record;
+// }
+//
+// float[] distances = new float[] { t[0] };
+// Vector3f[] points = new Vector3f[] {
+// new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
+// };
+// IntersectionRecord record = new IntersectionRecord(distances, points);
+// return record;
+// }
+//
+// return new IntersectionRecord();
+//
+// }
+//
+// /**
+// * <code>clip</code> determines if a line segment intersects the current
+// * test plane.
+// *
+// * @param denom
+// * the denominator of the line segment.
+// * @param numer
+// * the numerator of the line segment.
+// * @param t
+// * test values of the plane.
+// * @return true if the line segment intersects the plane, false otherwise.
+// */
+// private boolean clip(float denom, float numer, float[] t) {
+// // Return value is 'true' if line segment intersects the current test
+// // plane. Otherwise 'false' is returned in which case the line segment
+// // is entirely clipped.
+// if (denom > 0.0f) {
+// if (numer > denom * t[1])
+// return false;
+// if (numer > denom * t[0])
+// t[0] = numer / denom;
+// return true;
+// } else if (denom < 0.0f) {
+// if (numer > denom * t[0])
+// return false;
+// if (numer > denom * t[1])
+// t[1] = numer / denom;
+// return true;
+// } else {
+// return numer <= 0.0;
+// }
+// }
+//
+// public void setXAxis(Vector3f axis) {
+// xAxis.set(axis);
+// correctCorners = false;
+// }
+//
+// public void setYAxis(Vector3f axis) {
+// yAxis.set(axis);
+// correctCorners = false;
+// }
+//
+// public void setZAxis(Vector3f axis) {
+// zAxis.set(axis);
+// correctCorners = false;
+// }
+//
+// public void setExtent(Vector3f ext) {
+// extent.set(ext);
+// correctCorners = false;
+// }
+//
+// public Vector3f getXAxis() {
+// return xAxis;
+// }
+//
+// public Vector3f getYAxis() {
+// return yAxis;
+// }
+//
+// public Vector3f getZAxis() {
+// return zAxis;
+// }
+//
+// public Vector3f getExtent() {
+// return extent;
+// }
+//
+// @Override
+// public boolean contains(Vector3f point) {
+// _compVect1.set(point).subtractLocal(center);
+// float coeff = _compVect1.dot(xAxis);
+// if (FastMath.abs(coeff) > extent.x) return false;
+//
+// coeff = _compVect1.dot(yAxis);
+// if (FastMath.abs(coeff) > extent.y) return false;
+//
+// coeff = _compVect1.dot(zAxis);
+// if (FastMath.abs(coeff) > extent.z) return false;
+//
+// return true;
+// }
+//
+// @Override
+// public float distanceToEdge(Vector3f point) {
+// // compute coordinates of point in box coordinate system
+// Vector3f diff = point.subtract(center);
+// Vector3f closest = new Vector3f(diff.dot(xAxis), diff.dot(yAxis), diff
+// .dot(zAxis));
+//
+// // project test point onto box
+// float sqrDistance = 0.0f;
+// float delta;
+//
+// if (closest.x < -extent.x) {
+// delta = closest.x + extent.x;
+// sqrDistance += delta * delta;
+// closest.x = -extent.x;
+// } else if (closest.x > extent.x) {
+// delta = closest.x - extent.x;
+// sqrDistance += delta * delta;
+// closest.x = extent.x;
+// }
+//
+// if (closest.y < -extent.y) {
+// delta = closest.y + extent.y;
+// sqrDistance += delta * delta;
+// closest.y = -extent.y;
+// } else if (closest.y > extent.y) {
+// delta = closest.y - extent.y;
+// sqrDistance += delta * delta;
+// closest.y = extent.y;
+// }
+//
+// if (closest.z < -extent.z) {
+// delta = closest.z + extent.z;
+// sqrDistance += delta * delta;
+// closest.z = -extent.z;
+// } else if (closest.z > extent.z) {
+// delta = closest.z - extent.z;
+// sqrDistance += delta * delta;
+// closest.z = extent.z;
+// }
+//
+// return FastMath.sqrt(sqrDistance);
+// }
+//
+// public void write(JMEExporter e) throws IOException {
+// super.write(e);
+// OutputCapsule capsule = e.getCapsule(this);
+// capsule.write(xAxis, "xAxis", Vector3f.UNIT_X);
+// capsule.write(yAxis, "yAxis", Vector3f.UNIT_Y);
+// capsule.write(zAxis, "zAxis", Vector3f.UNIT_Z);
+// capsule.write(extent, "extent", Vector3f.ZERO);
+// }
+//
+// public void read(JMEImporter e) throws IOException {
+// super.read(e);
+// InputCapsule capsule = e.getCapsule(this);
+// xAxis.set((Vector3f) capsule.readSavable("xAxis", Vector3f.UNIT_X.clone()));
+// yAxis.set((Vector3f) capsule.readSavable("yAxis", Vector3f.UNIT_Y.clone()));
+// zAxis.set((Vector3f) capsule.readSavable("zAxis", Vector3f.UNIT_Z.clone()));
+// extent.set((Vector3f) capsule.readSavable("extent", Vector3f.ZERO.clone()));
+// correctCorners = false;
+// }
+//
+// @Override
+// public float getVolume() {
+// return (8*extent.x*extent.y*extent.z);
+// }
+//} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/cinematic/Cinematic.java b/engine/src/core/com/jme3/cinematic/Cinematic.java
new file mode 100644
index 0000000..3477c22
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/Cinematic.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.app.state.AppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.cinematic.events.AbstractCinematicEvent;
+import com.jme3.cinematic.events.CinematicEvent;
+import com.jme3.cinematic.events.CinematicEventListener;
+import com.jme3.export.*;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.CameraNode;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.CameraControl;
+import com.jme3.scene.control.CameraControl.ControlDirection;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Cinematic extends AbstractCinematicEvent implements AppState {
+
+ private static final Logger logger = Logger.getLogger(Application.class.getName());
+ private Node scene;
+ protected TimeLine timeLine = new TimeLine();
+ private int lastFetchedKeyFrame = -1;
+ private List<CinematicEvent> cinematicEvents = new ArrayList<CinematicEvent>();
+ private Map<String, CameraNode> cameras = new HashMap<String, CameraNode>();
+ private CameraNode currentCam;
+ private boolean initialized = false;
+ private Map<String, Map<String, Object>> eventsData;
+
+ public Cinematic() {
+ }
+
+ public Cinematic(Node scene) {
+ this.scene = scene;
+ }
+
+ public Cinematic(Node scene, float initialDuration) {
+ super(initialDuration);
+ this.scene = scene;
+ }
+
+ public Cinematic(Node scene, LoopMode loopMode) {
+ super(loopMode);
+ this.scene = scene;
+ }
+
+ public Cinematic(Node scene, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.scene = scene;
+ }
+
+ @Override
+ public void onPlay() {
+ if (isInitialized()) {
+ if (playState == PlayState.Paused) {
+ for (int i = 0; i < cinematicEvents.size(); i++) {
+ CinematicEvent ce = cinematicEvents.get(i);
+ if (ce.getPlayState() == PlayState.Paused) {
+ ce.play();
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStop() {
+ time = 0;
+ lastFetchedKeyFrame = -1;
+ for (int i = 0; i < cinematicEvents.size(); i++) {
+ CinematicEvent ce = cinematicEvents.get(i);
+ ce.stop();
+ }
+ enableCurrentCam(false);
+ }
+
+ @Override
+ public void onPause() {
+ for (int i = 0; i < cinematicEvents.size(); i++) {
+ CinematicEvent ce = cinematicEvents.get(i);
+ if (ce.getPlayState() == PlayState.Playing) {
+ ce.pause();
+ }
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+
+ oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
+ oc.writeStringSavableMap(cameras, "cameras", null);
+ oc.write(timeLine, "timeLine", null);
+
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+
+ cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
+ cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
+ timeLine = (TimeLine) ic.readSavable("timeLine", null);
+ }
+
+ @Override
+ public void setSpeed(float speed) {
+ super.setSpeed(speed);
+ for (int i = 0; i < cinematicEvents.size(); i++) {
+ CinematicEvent ce = cinematicEvents.get(i);
+ ce.setSpeed(speed);
+ }
+
+
+ }
+
+ public void initialize(AppStateManager stateManager, Application app) {
+ initEvent(app, this);
+ for (CinematicEvent cinematicEvent : cinematicEvents) {
+ cinematicEvent.initEvent(app, this);
+ }
+
+ initialized = true;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (enabled) {
+ play();
+ }
+ }
+
+ public boolean isEnabled() {
+ return playState == PlayState.Playing;
+ }
+
+ public void stateAttached(AppStateManager stateManager) {
+ }
+
+ public void stateDetached(AppStateManager stateManager) {
+ stop();
+ }
+
+ public void update(float tpf) {
+ if (isInitialized()) {
+ internalUpdate(tpf);
+ }
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ for (int i = 0; i < cinematicEvents.size(); i++) {
+ CinematicEvent ce = cinematicEvents.get(i);
+ ce.internalUpdate(tpf);
+ }
+
+ int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
+
+ //iterate to make sure every key frame is triggered
+ for (int i = lastFetchedKeyFrame + 1; i <= keyFrameIndex; i++) {
+ KeyFrame keyFrame = timeLine.get(i);
+ if (keyFrame != null) {
+ keyFrame.trigger();
+ }
+ }
+
+ lastFetchedKeyFrame = keyFrameIndex;
+ }
+
+ @Override
+ public void setTime(float time) {
+ super.setTime(time);
+ int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
+
+ //triggering all the event from start to "time"
+ //then computing timeOffset for each event
+ for (int i = 0; i <= keyFrameIndex; i++) {
+ KeyFrame keyFrame = timeLine.get(i);
+ if (keyFrame != null) {
+ for (CinematicEvent ce : keyFrame.getCinematicEvents()) {
+ ce.play();
+ ce.setTime(time - timeLine.getKeyFrameTime(keyFrame));
+ }
+ }
+ }
+ if (playState != PlayState.Playing) {
+ pause();
+ }
+
+ // step();
+ }
+
+ public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
+ KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
+ if (keyFrame == null) {
+ keyFrame = new KeyFrame();
+ timeLine.addKeyFrameAtTime(timeStamp, keyFrame);
+ }
+ keyFrame.cinematicEvents.add(cinematicEvent);
+ cinematicEvents.add(cinematicEvent);
+ return keyFrame;
+ }
+
+ public void render(RenderManager rm) {
+ }
+
+ public void postRender() {
+ }
+
+ public void cleanup() {
+ }
+
+ /**
+ * fits the duration of the cinamatic to the duration of all its child cinematic events
+ */
+ public void fitDuration() {
+ KeyFrame kf = timeLine.getKeyFrameAtTime(timeLine.getLastKeyFrameIndex());
+ float d = 0;
+ for (int i = 0; i < kf.getCinematicEvents().size(); i++) {
+ CinematicEvent ce = kf.getCinematicEvents().get(i);
+ if (d < (ce.getDuration() * ce.getSpeed())) {
+ d = (ce.getDuration() * ce.getSpeed());
+ }
+ }
+
+ initialDuration = d;
+ }
+
+ public CameraNode bindCamera(String cameraName, Camera cam) {
+ CameraNode node = new CameraNode(cameraName, cam);
+ node.setControlDir(ControlDirection.SpatialToCamera);
+ node.getControl(CameraControl.class).setEnabled(false);
+ cameras.put(cameraName, node);
+ scene.attachChild(node);
+ return node;
+ }
+
+ public CameraNode getCamera(String cameraName) {
+ return cameras.get(cameraName);
+ }
+
+ private void enableCurrentCam(boolean enabled) {
+ if (currentCam != null) {
+ currentCam.getControl(CameraControl.class).setEnabled(enabled);
+ }
+ }
+
+ public void setActiveCamera(String cameraName) {
+ enableCurrentCam(false);
+ currentCam = cameras.get(cameraName);
+ if (currentCam == null) {
+ logger.log(Level.WARNING, "{0} is not a camera bond to the cinematic, cannot activate", cameraName);
+ }
+ enableCurrentCam(true);
+ }
+
+ public void activateCamera(final float timeStamp, final String cameraName) {
+ addCinematicEvent(timeStamp, new AbstractCinematicEvent() {
+
+ @Override
+ public void play() {
+ super.play();
+ stop();
+ }
+
+ @Override
+ public void onPlay() {
+ setActiveCamera(cameraName);
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ }
+
+ @Override
+ public void onStop() {
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ @Override
+ public void setTime(float time) {
+ play();
+ }
+ });
+ }
+
+ public void setScene(Node scene) {
+ this.scene = scene;
+ }
+
+ private Map<String, Map<String, Object>> getEventsData() {
+ if (eventsData == null) {
+ eventsData = new HashMap<String, Map<String, Object>>();
+ }
+ return eventsData;
+ }
+
+ public void putEventData(String type, String name, Object object) {
+ Map<String, Map<String, Object>> data = getEventsData();
+ Map<String, Object> row = data.get(type);
+ if (row == null) {
+ row = new HashMap<String, Object>();
+ }
+ row.put(name, object);
+ }
+
+ public Object getEventData(String type, String name) {
+ if (eventsData != null) {
+ Map<String, Object> row = eventsData.get(type);
+ if (row != null) {
+ return row.get(name);
+ }
+ }
+ return null;
+ }
+
+ public Savable removeEventData(String type, String name) {
+ if (eventsData != null) {
+ Map<String, Object> row = eventsData.get(type);
+ if (row != null) {
+ row.remove(name);
+ }
+ }
+ return null;
+ }
+
+ public Node getScene() {
+ return scene;
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/KeyFrame.java b/engine/src/core/com/jme3/cinematic/KeyFrame.java
new file mode 100644
index 0000000..2a4b200
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/KeyFrame.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic;
+
+import com.jme3.cinematic.events.CinematicEvent;
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class KeyFrame implements Savable {
+
+ List<CinematicEvent> cinematicEvents = new ArrayList<CinematicEvent>();
+ private int index;
+
+ public List<CinematicEvent> getCinematicEvents() {
+ return cinematicEvents;
+ }
+
+ public void setCinematicEvents(List<CinematicEvent> cinematicEvents) {
+ this.cinematicEvents = cinematicEvents;
+ }
+
+ public List<CinematicEvent> trigger() {
+ for (CinematicEvent event : cinematicEvents) {
+ event.play();
+ }
+ return cinematicEvents;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
+ oc.write(index, "index", 0);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
+ index=ic.readInt("index", 0);
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/cinematic/MotionPath.java b/engine/src/core/com/jme3/cinematic/MotionPath.java
new file mode 100644
index 0000000..cdb31d0
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/MotionPath.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.cinematic.events.MotionTrack;
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Spline;
+import com.jme3.math.Spline.SplineType;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Curve;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Motion path is used to create a path between way points.
+ * @author Nehon
+ */
+public class MotionPath implements Savable {
+
+ private Node debugNode;
+ private AssetManager assetManager;
+ private List<MotionPathListener> listeners;
+ private Spline spline = new Spline();
+ private float eps = 0.0001f;
+
+ /**
+ * Create a motion Path
+ */
+ public MotionPath() {
+ }
+
+ /**
+ * interpolate the path giving the time since the beginnin and the motionControl
+ * this methods sets the new localTranslation to the spatial of the motionTrack control.
+ * @param time the time since the animation started
+ * @param control the ocntrol over the moving spatial
+ */
+ public float interpolatePath(float time, MotionTrack control) {
+
+ float traveledDistance = 0;
+ TempVars vars = TempVars.get();
+ Vector3f temp = vars.vect1;
+ Vector3f tmpVector = vars.vect2;
+ //computing traveled distance according to new time
+ traveledDistance = time * (getLength() / control.getInitialDuration());
+
+ //getting waypoint index and current value from new traveled distance
+ Vector2f v = getWayPointIndexForDistance(traveledDistance);
+
+ //setting values
+ control.setCurrentWayPoint((int) v.x);
+ control.setCurrentValue(v.y);
+
+ //interpolating new position
+ getSpline().interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
+ if (control.needsDirection()) {
+ tmpVector.set(temp);
+ control.setDirection(tmpVector.subtractLocal(control.getSpatial().getLocalTranslation()).normalizeLocal());
+ }
+
+ control.getSpatial().setLocalTranslation(temp);
+ vars.release();
+ return traveledDistance;
+ }
+
+ private void attachDebugNode(Node root) {
+ if (debugNode == null) {
+ debugNode = new Node();
+ Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+ for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
+ Vector3f cp = it.next();
+ Geometry geo = new Geometry("box", new Box(cp, 0.3f, 0.3f, 0.3f));
+ geo.setMaterial(m);
+ debugNode.attachChild(geo);
+
+ }
+ switch (spline.getType()) {
+ case CatmullRom:
+ debugNode.attachChild(CreateCatmullRomPath());
+ break;
+ case Linear:
+ debugNode.attachChild(CreateLinearPath());
+ break;
+ default:
+ debugNode.attachChild(CreateLinearPath());
+ break;
+ }
+
+ root.attachChild(debugNode);
+ }
+ }
+
+ private Geometry CreateLinearPath() {
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Blue);
+ Geometry lineGeometry = new Geometry("line", new Curve(spline, 0));
+ lineGeometry.setMaterial(mat);
+ return lineGeometry;
+ }
+
+ private Geometry CreateCatmullRomPath() {
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Blue);
+ Geometry lineGeometry = new Geometry("line", new Curve(spline, 10));
+ lineGeometry.setMaterial(mat);
+ return lineGeometry;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(spline, "spline", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ spline = (Spline) in.readSavable("spline", null);
+
+ }
+
+ /**
+ * compute the index of the waypoint and the interpolation value according to a distance
+ * returns a vector 2 containing the index in the x field and the interpolation value in the y field
+ * @param distance the distance traveled on this path
+ * @return the waypoint index and the interpolation value in a vector2
+ */
+ public Vector2f getWayPointIndexForDistance(float distance) {
+ float sum = 0;
+ distance = distance % spline.getTotalLength();
+ int i = 0;
+ for (Float len : spline.getSegmentsLength()) {
+ if (sum + len >= distance) {
+ return new Vector2f((float) i, (distance - sum) / len);
+ }
+ sum += len;
+ i++;
+ }
+ return new Vector2f((float) spline.getControlPoints().size() - 1, 1.0f);
+ }
+
+ /**
+ * Addsa waypoint to the path
+ * @param wayPoint a position in world space
+ */
+ public void addWayPoint(Vector3f wayPoint) {
+ spline.addControlPoint(wayPoint);
+ }
+
+ /**
+ * retruns the length of the path in world units
+ * @return the length
+ */
+ public float getLength() {
+ return spline.getTotalLength();
+ }
+
+ /**
+ * returns the waypoint at the given index
+ * @param i the index
+ * @return returns the waypoint position
+ */
+ public Vector3f getWayPoint(int i) {
+ return spline.getControlPoints().get(i);
+ }
+
+ /**
+ * remove the waypoint from the path
+ * @param wayPoint the waypoint to remove
+ */
+ public void removeWayPoint(Vector3f wayPoint) {
+ spline.removeControlPoint(wayPoint);
+ }
+
+ /**
+ * remove the waypoint at the given index from the path
+ * @param i the index of the waypoint to remove
+ */
+ public void removeWayPoint(int i) {
+ removeWayPoint(spline.getControlPoints().get(i));
+ }
+
+ /**
+ * returns an iterator on the waypoints collection
+ * @return
+ */
+ public Iterator<Vector3f> iterator() {
+ return spline.getControlPoints().iterator();
+ }
+
+ /**
+ * return the type of spline used for the path interpolation for this path
+ * @return the path interpolation spline type
+ */
+ public SplineType getPathSplineType() {
+ return spline.getType();
+ }
+
+ /**
+ * sets the type of spline used for the path interpolation for this path
+ * @param pathSplineType
+ */
+ public void setPathSplineType(SplineType pathSplineType) {
+ spline.setType(pathSplineType);
+ if (debugNode != null) {
+ Node parent = debugNode.getParent();
+ debugNode.removeFromParent();
+ debugNode.detachAllChildren();
+ debugNode = null;
+ attachDebugNode(parent);
+ }
+ }
+
+ /**
+ * disable the display of the path and the waypoints
+ */
+ public void disableDebugShape() {
+
+ debugNode.detachAllChildren();
+ debugNode = null;
+ assetManager = null;
+ }
+
+ /**
+ * enable the display of the path and the waypoints
+ * @param manager the assetManager
+ * @param rootNode the node where the debug shapes must be attached
+ */
+ public void enableDebugShape(AssetManager manager, Node rootNode) {
+ assetManager = manager;
+ // computeTotalLentgh();
+ attachDebugNode(rootNode);
+ }
+
+ /**
+ * Adds a motion pathListener to the path
+ * @param listener the MotionPathListener to attach
+ */
+ public void addListener(MotionPathListener listener) {
+ if (listeners == null) {
+ listeners = new ArrayList<MotionPathListener>();
+ }
+ listeners.add(listener);
+ }
+
+ /**
+ * remove the given listener
+ * @param listener the listener to remove
+ */
+ public void removeListener(MotionPathListener listener) {
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * return the number of waypoints of this path
+ * @return
+ */
+ public int getNbWayPoints() {
+ return spline.getControlPoints().size();
+ }
+
+ public void triggerWayPointReach(int wayPointIndex, MotionTrack control) {
+ if (listeners != null) {
+ for (Iterator<MotionPathListener> it = listeners.iterator(); it.hasNext();) {
+ MotionPathListener listener = it.next();
+ listener.onWayPointReach(control, wayPointIndex);
+ }
+ }
+ }
+
+ /**
+ * Returns the curve tension
+ * @return
+ */
+ public float getCurveTension() {
+ return spline.getCurveTension();
+ }
+
+ /**
+ * sets the tension of the curve (only for catmull rom) 0.0 will give a linear curve, 1.0 a round curve
+ * @param curveTension
+ */
+ public void setCurveTension(float curveTension) {
+ spline.setCurveTension(curveTension);
+ if (debugNode != null) {
+ Node parent = debugNode.getParent();
+ debugNode.removeFromParent();
+ debugNode.detachAllChildren();
+ debugNode = null;
+ attachDebugNode(parent);
+ }
+ }
+
+ public void clearWayPoints() {
+ spline.clearControlPoints();
+ }
+
+ /**
+ * Sets the path to be a cycle
+ * @param cycle
+ */
+ public void setCycle(boolean cycle) {
+
+ spline.setCycle(cycle);
+ if (debugNode != null) {
+ Node parent = debugNode.getParent();
+ debugNode.removeFromParent();
+ debugNode.detachAllChildren();
+ debugNode = null;
+ attachDebugNode(parent);
+ }
+
+ }
+
+ /**
+ * returns true if the path is a cycle
+ * @return
+ */
+ public boolean isCycle() {
+ return spline.isCycle();
+ }
+
+ public Spline getSpline() {
+ return spline;
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/MotionPathListener.java b/engine/src/core/com/jme3/cinematic/MotionPathListener.java
new file mode 100644
index 0000000..99a3dcb
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/MotionPathListener.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.cinematic;
+
+import com.jme3.cinematic.events.MotionTrack;
+
+/**
+ * Trigger the events appening on an motion path
+ * @author Nehon
+ */
+public interface MotionPathListener {
+
+ /**
+ * Triggers every time the target reach a waypoint on the path
+ * @param motionControl the MotionTrack objects that reached the waypoint
+ * @param wayPointIndex the index of the way point reached
+ */
+ public void onWayPointReach(MotionTrack motionControl,int wayPointIndex);
+
+}
diff --git a/engine/src/core/com/jme3/cinematic/PlayState.java b/engine/src/core/com/jme3/cinematic/PlayState.java
new file mode 100644
index 0000000..648dc3d
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/PlayState.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.cinematic;
+
+/**
+ * The play state of a cinematic event
+ * @author Nehon
+ */
+public enum PlayState {
+
+ /**The CinematicEvent is currently beeing played*/
+ Playing,
+ /**The animatable has been paused*/
+ Paused,
+ /**the animatable is stoped*/
+ Stopped
+}
+
diff --git a/engine/src/core/com/jme3/cinematic/TimeLine.java b/engine/src/core/com/jme3/cinematic/TimeLine.java
new file mode 100644
index 0000000..ae3e06d
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/TimeLine.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TimeLine extends HashMap<Integer, KeyFrame> implements Savable {
+
+ protected int keyFramesPerSeconds = 30;
+ protected int lastKeyFrameIndex = 0;
+
+ public TimeLine() {
+ super();
+ }
+
+ public KeyFrame getKeyFrameAtTime(float time) {
+ return get(getKeyFrameIndexFromTime(time));
+ }
+
+ public KeyFrame getKeyFrameAtIndex(int keyFrameIndex) {
+ return get(keyFrameIndex);
+ }
+
+ public void addKeyFrameAtTime(float time, KeyFrame keyFrame) {
+ addKeyFrameAtIndex(getKeyFrameIndexFromTime(time), keyFrame);
+ }
+
+ public void addKeyFrameAtIndex(int keyFrameIndex, KeyFrame keyFrame) {
+ put(keyFrameIndex, keyFrame);
+ keyFrame.setIndex(keyFrameIndex);
+ if (lastKeyFrameIndex < keyFrameIndex) {
+ lastKeyFrameIndex = keyFrameIndex;
+ }
+ }
+
+ public void removeKeyFrame(int keyFrameIndex) {
+ remove(keyFrameIndex);
+ if (lastKeyFrameIndex == keyFrameIndex) {
+ KeyFrame kf = null;
+ for (int i = keyFrameIndex; kf == null && i >= 0; i--) {
+ kf = getKeyFrameAtIndex(i);
+ lastKeyFrameIndex = i;
+ }
+ }
+ }
+
+ public void removeKeyFrame(float time) {
+ removeKeyFrame(getKeyFrameIndexFromTime(time));
+ }
+
+ public int getKeyFrameIndexFromTime(float time) {
+ return Math.round(time * keyFramesPerSeconds);
+ }
+
+ public float getKeyFrameTime(KeyFrame keyFrame) {
+ return (float)keyFrame.getIndex()/(float)keyFramesPerSeconds;
+ }
+
+ public Collection<KeyFrame> getAllKeyFrames() {
+ return values();
+ }
+
+ public int getLastKeyFrameIndex() {
+ return lastKeyFrameIndex;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ ArrayList list = new ArrayList();
+ list.addAll(values());
+ oc.writeSavableArrayList(list, "keyFrames", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ ArrayList list = ic.readSavableArrayList("keyFrames", null);
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ KeyFrame keyFrame = (KeyFrame) it.next();
+ addKeyFrameAtIndex(keyFrame.getIndex(), keyFrame);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java b/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java
new file mode 100644
index 0000000..ea2d132
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.PlayState;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This calls contains basic behavior of a cinematic event
+ * every cinematic event must extend this class
+ *
+ * A cinematic event must be given an inital duration in seconds (duration of the event at speed = 1) (default is 10)
+ * @author Nehon
+ */
+public abstract class AbstractCinematicEvent implements CinematicEvent {
+
+ protected PlayState playState = PlayState.Stopped;
+ protected float speed = 1;
+ protected float initialDuration = 10;
+ protected LoopMode loopMode = LoopMode.DontLoop;
+ protected float time = 0;
+ protected boolean resuming = false;
+
+ /**
+ * the list of listeners
+ */
+ protected List<CinematicEventListener> listeners;
+
+ /**
+ * contruct a cinematic event
+ */
+ public AbstractCinematicEvent() {
+ }
+
+ /**
+ * contruct a cinematic event wwith the given initial duration
+ * @param initialDuration
+ */
+ public AbstractCinematicEvent(float initialDuration) {
+ this.initialDuration = initialDuration;
+ }
+
+ /**
+ * contruct a cinematic event with the given loopMode
+ * @param loopMode
+ */
+ public AbstractCinematicEvent(LoopMode loopMode) {
+ this.loopMode = loopMode;
+ }
+
+ /**
+ * contruct a cinematic event with the given loopMode and the given initialDuration
+ * @param initialDuration the duration of the event at speed = 1
+ * @param loopMode the loop mode of the event
+ */
+ public AbstractCinematicEvent(float initialDuration, LoopMode loopMode) {
+ this.initialDuration = initialDuration;
+ this.loopMode = loopMode;
+ }
+
+ /**
+ * Play this event
+ */
+ public void play() {
+ onPlay();
+ playState = PlayState.Playing;
+ if (listeners != null) {
+ for (int i = 0; i < listeners.size(); i++) {
+ CinematicEventListener cel = listeners.get(i);
+ cel.onPlay(this);
+ }
+ }
+ }
+
+ /**
+ * Place here the code you want to execute when the event is started
+ */
+ protected abstract void onPlay();
+
+ /**
+ * should be used internally only
+ * @param tpf time per frame
+ */
+ public void internalUpdate(float tpf) {
+ if (playState == PlayState.Playing) {
+ time = time + (tpf * speed);
+ //time = elapsedTimePause + (timer.getTimeInSeconds() - start) * speed;
+
+ onUpdate(tpf);
+ if (time >= initialDuration && loopMode == loopMode.DontLoop) {
+ stop();
+ }
+ }
+
+ }
+
+ /**
+ * Place here the code you want to execute on update (only called when the event is playing)
+ * @param tpf time per frame
+ */
+ protected abstract void onUpdate(float tpf);
+
+ /**
+ * stops the animation, next time play() is called the animation will start from the begining.
+ */
+ public void stop() {
+ onStop();
+ time = 0;
+ playState = PlayState.Stopped;
+ if (listeners != null) {
+ for (int i = 0; i < listeners.size(); i++) {
+ CinematicEventListener cel = listeners.get(i);
+ cel.onStop(this);
+ }
+ }
+ }
+
+ /**
+ * Place here the code you want to execute when the event is stoped.
+ */
+ protected abstract void onStop();
+
+ /**
+ * pause this event
+ */
+ public void pause() {
+ onPause();
+ playState = PlayState.Paused;
+ if (listeners != null) {
+ for (int i = 0; i < listeners.size(); i++) {
+ CinematicEventListener cel = listeners.get(i);
+ cel.onPause(this);
+ }
+ }
+ }
+
+ /**
+ * place here the code you want to execute when the event is paused
+ */
+ public abstract void onPause();
+
+ /**
+ * returns the actual duration of the animtion (initialDuration/speed)
+ * @return
+ */
+ public float getDuration() {
+ return initialDuration / speed;
+ }
+
+ /**
+ * Sets the speed of the animation.
+ * At speed = 1, the animation will last initialDuration seconds,
+ * At speed = 2 the animation will last initialDuraiton/2...
+ * @param speed
+ */
+ public void setSpeed(float speed) {
+ this.speed = speed;
+ }
+
+ /**
+ * returns the speed of the animation.
+ * @return
+ */
+ public float getSpeed() {
+ return speed;
+ }
+
+ /**
+ * Returns the current playstate of the animation
+ * @return
+ */
+ public PlayState getPlayState() {
+ return playState;
+ }
+
+ /**
+ * returns the initial duration of the animation at speed = 1 in seconds.
+ * @return
+ */
+ public float getInitialDuration() {
+ return initialDuration;
+ }
+
+ /**
+ * Sets the duration of the antionamtion at speed = 1 in seconds
+ * @param initialDuration
+ */
+ public void setInitialDuration(float initialDuration) {
+ this.initialDuration = initialDuration;
+ }
+
+ /**
+ * retursthe loopMode of the animation
+ * @see LoopMode
+ * @return
+ */
+ public LoopMode getLoopMode() {
+ return loopMode;
+ }
+
+ /**
+ * Sets the loopMode of the animation
+ * @see LoopMode
+ * @param loopMode
+ */
+ public void setLoopMode(LoopMode loopMode) {
+ this.loopMode = loopMode;
+ }
+
+ /**
+ * for serialization only
+ * @param ex exporter
+ * @throws IOException
+ */
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(playState, "playState", PlayState.Stopped);
+ oc.write(speed, "speed", 1);
+ oc.write(initialDuration, "initalDuration", 10);
+ oc.write(loopMode, "loopMode", LoopMode.DontLoop);
+ }
+
+ /**
+ * for serialization only
+ * @param im importer
+ * @throws IOException
+ */
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ playState = ic.readEnum("playState", PlayState.class, PlayState.Stopped);
+ speed = ic.readFloat("speed", 1);
+ initialDuration = ic.readFloat("initalDuration", 10);
+ loopMode = ic.readEnum("loopMode", LoopMode.class, LoopMode.DontLoop);
+ }
+
+ /**
+ * initialize this event (should be called internally only)
+ * @param app
+ * @param cinematic
+ */
+ public void initEvent(Application app, Cinematic cinematic) {
+ }
+
+ /**
+ * return a list of CinematicEventListener added on this event
+ * @return
+ */
+ private List<CinematicEventListener> getListeners() {
+ if (listeners == null) {
+ listeners = new ArrayList<CinematicEventListener>();
+ }
+ return listeners;
+ }
+
+ /**
+ * Add a CinematicEventListener to this event
+ * @param listener CinematicEventListener
+ */
+ public void addListener(CinematicEventListener listener) {
+ getListeners().add(listener);
+ }
+
+ /**
+ * remove a CinematicEventListener from this event
+ * @param listener CinematicEventListener
+ */
+ public void removeListener(CinematicEventListener listener) {
+ getListeners().remove(listener);
+ }
+
+ /**
+ * When this method is invoked, the event should fast forward to the given time according tim 0 is the start of the event.
+ * @param time the time to fast forward to
+ */
+ public void setTime(float time) {
+ this.time = time / speed;
+ }
+
+ public float getTime() {
+ return time;
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/AnimationTrack.java b/engine/src/core/com/jme3/cinematic/events/AnimationTrack.java
new file mode 100644
index 0000000..e857706
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/AnimationTrack.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.PlayState;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class AnimationTrack extends AbstractCinematicEvent {
+
+ private static final Logger log = Logger.getLogger(AnimationTrack.class.getName());
+ protected AnimChannel channel;
+ protected String animationName;
+ protected String modelName;
+
+ public AnimationTrack() {
+ }
+
+ public AnimationTrack(Spatial model, String animationName) {
+ modelName = model.getName();
+ this.animationName = animationName;
+ initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
+ }
+
+ public AnimationTrack(Spatial model, String animationName, float initialDuration) {
+ super(initialDuration);
+ modelName = model.getName();
+ this.animationName = animationName;
+ }
+
+ public AnimationTrack(Spatial model, String animationName, LoopMode loopMode) {
+ super(loopMode);
+ initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
+ modelName = model.getName();
+ this.animationName = animationName;
+ }
+
+ public AnimationTrack(Spatial model, String animationName, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ modelName = model.getName();
+ this.animationName = animationName;
+ }
+
+ @Override
+ public void initEvent(Application app, Cinematic cinematic) {
+ super.initEvent(app, cinematic);
+ if (channel == null) {
+ Object s = cinematic.getEventData("modelChannels", modelName);
+ if (s != null && s instanceof AnimChannel) {
+ this.channel = (AnimChannel) s;
+ } else if (s == null) {
+ Spatial model = cinematic.getScene().getChild(modelName);
+ if (model != null) {
+ channel = model.getControl(AnimControl.class).createChannel();
+ cinematic.putEventData("modelChannels", modelName, channel);
+ } else {
+ log.log(Level.WARNING, "spatial {0} not found in the scene, cannot perform animation", modelName);
+ }
+ }
+
+ }
+ }
+
+ @Override
+ public void setTime(float time) {
+ super.setTime(time);
+ float t = time;
+ if(loopMode == loopMode.Loop){
+ t = t % channel.getAnimMaxTime();
+ }
+ if(loopMode == loopMode.Cycle){
+ float parity = (float)Math.ceil(time / channel.getAnimMaxTime());
+ if(parity >0 && parity%2 ==0){
+ t = channel.getAnimMaxTime() - t % channel.getAnimMaxTime();
+ }else{
+ t = t % channel.getAnimMaxTime();
+ }
+
+ }
+ channel.setTime(t);
+ channel.getControl().update(0);
+ }
+
+ @Override
+ public void onPlay() {
+ channel.getControl().setEnabled(true);
+ if (playState == PlayState.Stopped) {
+ channel.setAnim(animationName);
+ channel.setSpeed(speed);
+ channel.setLoopMode(loopMode);
+ channel.setTime(time);
+ }
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ }
+
+ @Override
+ public void onStop() {
+ channel.getControl().setEnabled(false);
+ channel.setTime(0);
+ }
+
+ @Override
+ public void onPause() {
+ channel.getControl().setEnabled(false);
+ }
+
+ @Override
+ public void setLoopMode(LoopMode loopMode) {
+ super.setLoopMode(loopMode);
+ channel.setLoopMode(loopMode);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(modelName, "modelName", "");
+ oc.write(animationName, "animationName", "");
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ modelName = ic.readString("modelName", "");
+ animationName = ic.readString("animationName", "");
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java b/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java
new file mode 100644
index 0000000..37e70f9
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.PlayState;
+import com.jme3.export.Savable;
+
+/**
+ *
+ * @author Nehon
+ */
+public interface CinematicEvent extends Savable {
+
+ /**
+ * Starts the animation
+ */
+ public void play();
+
+ /**
+ * Stops the animation
+ */
+ public void stop();
+
+ /**
+ * Pauses the animation
+ */
+ public void pause();
+
+ /**
+ * Returns the actual duration of the animation
+ * @return the duration
+ */
+ public float getDuration();
+
+ /**
+ * Sets the speed of the animation (1 is normal speed, 2 is twice faster)
+ * @param speed
+ */
+ public void setSpeed(float speed);
+
+ /**
+ * returns the speed of the animation
+ * @return the speed
+ */
+ public float getSpeed();
+
+ /**
+ * returns the PlayState of the animation
+ * @return the plat state
+ */
+ public PlayState getPlayState();
+
+ /**
+ * @param loop Set the loop mode for the channel. The loop mode
+ * determines what will happen to the animation once it finishes
+ * playing.
+ *
+ * For more information, see the LoopMode enum class.
+ * @see LoopMode
+ */
+ public void setLoopMode(LoopMode loop);
+
+ /**
+ * @return The loop mode currently set for the animation. The loop mode
+ * determines what will happen to the animation once it finishes
+ * playing.
+ *
+ * For more information, see the LoopMode enum class.
+ * @see LoopMode
+ */
+ public LoopMode getLoopMode();
+
+ /**
+ * returns the initial duration of the animation at speed = 1 in seconds.
+ * @return the initial duration
+ */
+ public float getInitialDuration();
+
+ /**
+ * Sets the duration of the antionamtion at speed = 1 in seconds
+ * @param initialDuration
+ */
+ public void setInitialDuration(float initialDuration);
+
+ /**
+ * called internally in the update method, place here anything you want to run in the update loop
+ * @param tpf time per frame
+ */
+ public void internalUpdate(float tpf);
+
+ /**
+ * initialize this event
+ * @param app the application
+ * @param cinematic the cinematic
+ */
+ public void initEvent(Application app, Cinematic cinematic);
+
+ /**
+ * When this method is invoked, the event should fast forward to the given time according tim 0 is the start of the event.
+ * @param time the time to fast forward to
+ */
+ public void setTime(float time);
+
+ /**
+ * returns the current time of the cinematic event
+ * @return the time
+ */
+ public float getTime();
+
+
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/CinematicEventListener.java b/engine/src/core/com/jme3/cinematic/events/CinematicEventListener.java
new file mode 100644
index 0000000..9a5f5a6
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/CinematicEventListener.java
@@ -0,0 +1,17 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.jme3.cinematic.events;
+
+/**
+ *
+ * @author Nehon
+ */
+public interface CinematicEventListener {
+
+ public void onPlay(CinematicEvent cinematic);
+ public void onPause(CinematicEvent cinematic);
+ public void onStop(CinematicEvent cinematic);
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/MotionTrack.java b/engine/src/core/com/jme3/cinematic/events/MotionTrack.java
new file mode 100644
index 0000000..2995452
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/MotionTrack.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.MotionPath;
+import com.jme3.cinematic.PlayState;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * A MotionTrack is a control over the spatial that manage the position and direction of the spatial while following a motion Path
+ *
+ * You must first create a MotionPath and then create a MotionTrack to associate a spatial and the path.
+ *
+ * @author Nehon
+ */
+public class MotionTrack extends AbstractCinematicEvent implements Control {
+
+ protected Spatial spatial;
+ protected int currentWayPoint;
+ protected float currentValue;
+ protected Vector3f direction = new Vector3f();
+ protected Vector3f lookAt;
+ protected Vector3f upVector;
+ protected Quaternion rotation;
+ protected Direction directionType = Direction.None;
+ protected MotionPath path;
+ private boolean isControl = true;
+ /**
+ * the distance traveled by the spatial on the path
+ */
+ protected float traveledDistance = 0;
+
+ /**
+ * Enum for the different type of target direction behavior
+ */
+ public enum Direction {
+
+ /**
+ * the target stay in the starting direction
+ */
+ None,
+ /**
+ * The target rotates with the direction of the path
+ */
+ Path,
+ /**
+ * The target rotates with the direction of the path but with the additon of a rtotation
+ * you need to use the setRotation mathod when using this Direction
+ */
+ PathAndRotation,
+ /**
+ * The target rotates with the given rotation
+ */
+ Rotation,
+ /**
+ * The target looks at a point
+ * You need to use the setLookAt method when using this direction
+ */
+ LookAt
+ }
+
+ /**
+ * Create MotionTrack,
+ * when using this constructor don't forget to assign spatial and path
+ */
+ public MotionTrack() {
+ super();
+ }
+
+ /**
+ * Creates a MotionPath for the given spatial on the given motion path
+ * @param spatial
+ * @param path
+ */
+ public MotionTrack(Spatial spatial, MotionPath path) {
+ super();
+ this.spatial = spatial;
+ spatial.addControl(this);
+ this.path = path;
+ }
+
+ /**
+ * Creates a MotionPath for the given spatial on the given motion path
+ * @param spatial
+ * @param path
+ */
+ public MotionTrack(Spatial spatial, MotionPath path, float initialDuration) {
+ super(initialDuration);
+ this.spatial = spatial;
+ spatial.addControl(this);
+ this.path = path;
+ }
+
+ /**
+ * Creates a MotionPath for the given spatial on the given motion path
+ * @param spatial
+ * @param path
+ */
+ public MotionTrack(Spatial spatial, MotionPath path, LoopMode loopMode) {
+ super();
+ this.spatial = spatial;
+ spatial.addControl(this);
+ this.path = path;
+ this.loopMode = loopMode;
+ }
+
+ /**
+ * Creates a MotionPath for the given spatial on the given motion path
+ * @param spatial
+ * @param path
+ */
+ public MotionTrack(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) {
+ super(initialDuration);
+ this.spatial = spatial;
+ spatial.addControl(this);
+ this.path = path;
+ this.loopMode = loopMode;
+ }
+
+ public void update(float tpf) {
+ if (isControl) {
+
+ if (playState == PlayState.Playing) {
+ time = time + (tpf * speed);
+
+ if (time >= initialDuration && loopMode == loopMode.DontLoop) {
+ stop();
+ } else {
+ onUpdate(tpf);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void initEvent(Application app, Cinematic cinematic) {
+ super.initEvent(app, cinematic);
+ isControl = false;
+ }
+
+ @Override
+ public void setTime(float time) {
+ super.setTime(time);
+ onUpdate(0);
+ }
+
+ public void onUpdate(float tpf) {
+ traveledDistance = path.interpolatePath(time, this);
+ computeTargetDirection();
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(lookAt, "lookAt", Vector3f.ZERO);
+ oc.write(upVector, "upVector", Vector3f.UNIT_Y);
+ oc.write(rotation, "rotation", Quaternion.IDENTITY);
+ oc.write(directionType, "directionType", Direction.None);
+ oc.write(path, "path", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule in = im.getCapsule(this);
+ lookAt = (Vector3f) in.readSavable("lookAt", Vector3f.ZERO);
+ upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
+ rotation = (Quaternion) in.readSavable("rotation", Quaternion.IDENTITY);
+ directionType = in.readEnum("directionType", Direction.class, Direction.None);
+ path = (MotionPath) in.readSavable("path", null);
+ }
+
+ /**
+ * this method is meant to be called by the motion path only
+ * @return
+ */
+ public boolean needsDirection() {
+ return directionType == Direction.Path || directionType == Direction.PathAndRotation;
+ }
+
+ private void computeTargetDirection() {
+ switch (directionType) {
+ case Path:
+ Quaternion q = new Quaternion();
+ q.lookAt(direction, Vector3f.UNIT_Y);
+ spatial.setLocalRotation(q);
+ break;
+ case LookAt:
+ if (lookAt != null) {
+ spatial.lookAt(lookAt, upVector);
+ }
+ break;
+ case PathAndRotation:
+ if (rotation != null) {
+ Quaternion q2 = new Quaternion();
+ q2.lookAt(direction, Vector3f.UNIT_Y);
+ q2.multLocal(rotation);
+ spatial.setLocalRotation(q2);
+ }
+ break;
+ case Rotation:
+ if (rotation != null) {
+ spatial.setLocalRotation(rotation);
+ }
+ break;
+ case None:
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Clone this control for the given spatial
+ * @param spatial
+ * @return
+ */
+ public Control cloneForSpatial(Spatial spatial) {
+ MotionTrack control = new MotionTrack(spatial, path);
+ control.playState = playState;
+ control.currentWayPoint = currentWayPoint;
+ control.currentValue = currentValue;
+ control.direction = direction.clone();
+ control.lookAt = lookAt.clone();
+ control.upVector = upVector.clone();
+ control.rotation = rotation.clone();
+ control.initialDuration = initialDuration;
+ control.speed = speed;
+ control.loopMode = loopMode;
+ control.directionType = directionType;
+
+ return control;
+ }
+
+ @Override
+ public void onPlay() {
+ traveledDistance = 0;
+ }
+
+ @Override
+ public void onStop() {
+ currentWayPoint = 0;
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ /**
+ * this method is meant to be called by the motion path only
+ * @return
+ */
+ public float getCurrentValue() {
+ return currentValue;
+ }
+
+ /**
+ * this method is meant to be called by the motion path only
+ *
+ */
+ public void setCurrentValue(float currentValue) {
+ this.currentValue = currentValue;
+ }
+
+ /**
+ * this method is meant to be called by the motion path only
+ * @return
+ */
+ public int getCurrentWayPoint() {
+ return currentWayPoint;
+ }
+
+ /**
+ * this method is meant to be called by the motion path only
+ *
+ */
+ public void setCurrentWayPoint(int currentWayPoint) {
+ if (this.currentWayPoint != currentWayPoint) {
+ this.currentWayPoint = currentWayPoint;
+ path.triggerWayPointReach(currentWayPoint, this);
+ }
+ }
+
+ /**
+ * returns the direction the spatial is moving
+ * @return
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * Sets the direction of the spatial
+ * This method is used by the motion path.
+ * @param direction
+ */
+ public void setDirection(Vector3f direction) {
+ this.direction.set(direction);
+ }
+
+ /**
+ * returns the direction type of the target
+ * @return the direction type
+ */
+ public Direction getDirectionType() {
+ return directionType;
+ }
+
+ /**
+ * Sets the direction type of the target
+ * On each update the direction given to the target can have different behavior
+ * See the Direction Enum for explanations
+ * @param directionType the direction type
+ */
+ public void setDirectionType(Direction directionType) {
+ this.directionType = directionType;
+ }
+
+ /**
+ * Set the lookAt for the target
+ * This can be used only if direction Type is Direction.LookAt
+ * @param lookAt the position to look at
+ * @param upVector the up vector
+ */
+ public void setLookAt(Vector3f lookAt, Vector3f upVector) {
+ this.lookAt = lookAt;
+ this.upVector = upVector;
+ }
+
+ /**
+ * returns the rotation of the target
+ * @return the rotation quaternion
+ */
+ public Quaternion getRotation() {
+ return rotation;
+ }
+
+ /**
+ * sets the rotation of the target
+ * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation
+ * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion.
+ * With Rotation the rotation of the target will be set with the given Quaternion.
+ * @param rotation the rotation quaternion
+ */
+ public void setRotation(Quaternion rotation) {
+ this.rotation = rotation;
+ }
+
+ /**
+ * retun the motion path this control follows
+ * @return
+ */
+ public MotionPath getPath() {
+ return path;
+ }
+
+ /**
+ * Sets the motion path to follow
+ * @param path
+ */
+ public void setPath(MotionPath path) {
+ this.path = path;
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (enabled) {
+ play();
+ } else {
+ pause();
+ }
+ }
+
+ public boolean isEnabled() {
+ return playState != PlayState.Stopped;
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ }
+
+ public void setSpatial(Spatial spatial) {
+ this.spatial = spatial;
+ }
+
+ public Spatial getSpatial() {
+ return spatial;
+ }
+
+ /**
+ * return the distance traveled by the spatial on the path
+ * @return
+ */
+ public float getTraveledDistance() {
+ return traveledDistance;
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/PositionTrack.java b/engine/src/core/com/jme3/cinematic/events/PositionTrack.java
new file mode 100644
index 0000000..abab712
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/PositionTrack.java
@@ -0,0 +1,122 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ * @deprecated use spatial animation instead.
+ */
+@Deprecated
+public class PositionTrack extends AbstractCinematicEvent {
+
+ private static final Logger log = Logger.getLogger(PositionTrack.class.getName());
+ private Vector3f startPosition;
+ private Vector3f endPosition;
+ private Spatial spatial;
+ private String spatialName = "";
+ private float value = 0;
+
+ public PositionTrack() {
+ }
+
+ public PositionTrack(Spatial spatial, Vector3f endPosition) {
+ this.endPosition = endPosition;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ @Override
+ public void initEvent(Application app, Cinematic cinematic) {
+ super.initEvent(app, cinematic);
+ if (spatial == null) {
+ spatial = cinematic.getScene().getChild(spatialName);
+ if (spatial == null) {
+ } else {
+ log.log(Level.WARNING, "spatial {0} not found in the scene", spatialName);
+ }
+ }
+ }
+
+ public PositionTrack(Spatial spatial, Vector3f endPosition, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.endPosition = endPosition;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public PositionTrack(Spatial spatial, Vector3f endPosition, LoopMode loopMode) {
+ super(loopMode);
+ this.endPosition = endPosition;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public PositionTrack(Spatial spatial, Vector3f endPosition, float initialDuration) {
+ super(initialDuration);
+ this.endPosition = endPosition;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ @Override
+ public void onPlay() {
+ if (playState != playState.Paused) {
+ startPosition = spatial.getWorldTranslation().clone();
+ }
+ if (initialDuration == 0 && spatial != null) {
+
+ spatial.setLocalTranslation(endPosition);
+ }
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ if (spatial != null) {
+ value = Math.min(time / initialDuration, 1.0f);
+ Vector3f pos = FastMath.interpolateLinear(value, startPosition, endPosition);
+ spatial.setLocalTranslation(pos);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ value = 0;
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(spatialName, "spatialName", "");
+ oc.write(endPosition, "endPosition", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ spatialName = ic.readString("spatialName", "");
+ endPosition = (Vector3f) ic.readSavable("endPosition", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/RotationTrack.java b/engine/src/core/com/jme3/cinematic/events/RotationTrack.java
new file mode 100644
index 0000000..60acf9d
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/RotationTrack.java
@@ -0,0 +1,126 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.scene.Spatial;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ * @deprecated use spatial animation instead.
+ */
+@Deprecated
+public class RotationTrack extends AbstractCinematicEvent {
+
+ private static final Logger log = Logger.getLogger(RotationTrack.class.getName());
+ private Quaternion startRotation = new Quaternion();
+ private Quaternion endRotation = new Quaternion();
+ private Spatial spatial;
+ private String spatialName = "";
+ private float value = 0;
+
+ @Override
+ public void initEvent(Application app, Cinematic cinematic) {
+ super.initEvent(app, cinematic);
+ if (spatial == null) {
+ spatial = cinematic.getScene().getChild(spatialName);
+ if (spatial == null) {
+ } else {
+ log.log(Level.WARNING, "spatial {0} not found in the scene", spatialName);
+ }
+ }
+ }
+
+ public RotationTrack() {
+ }
+
+ public RotationTrack(Spatial spatial, Quaternion endRotation) {
+ this.endRotation.set(endRotation);
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public RotationTrack(Spatial spatial, Quaternion endRotation, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.endRotation.set(endRotation);
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public RotationTrack(Spatial spatial, Quaternion endRotation, LoopMode loopMode) {
+ super(loopMode);
+ this.endRotation.set(endRotation);
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public RotationTrack(Spatial spatial, Quaternion endRotation, float initialDuration) {
+ super(initialDuration);
+ this.endRotation.set(endRotation);
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ @Override
+ public void onPlay() {
+ if (playState != playState.Paused) {
+ startRotation.set(spatial.getWorldRotation());
+ }
+ if (initialDuration == 0 && spatial != null) {
+ spatial.setLocalRotation(endRotation);
+ stop();
+ }
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ if (spatial != null) {
+ value = Math.min(time / initialDuration, 1.0f);
+ TempVars vars = TempVars.get();
+ Quaternion q = vars.quat1;
+ q.set(startRotation).slerp(endRotation, value);
+
+ spatial.setLocalRotation(q);
+ vars.release();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ value = 0;
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(spatialName, "spatialName", "");
+ oc.write(endRotation, "endRotation", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ spatialName = ic.readString("spatialName", "");
+ endRotation = (Quaternion) ic.readSavable("endRotation", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/ScaleTrack.java b/engine/src/core/com/jme3/cinematic/events/ScaleTrack.java
new file mode 100644
index 0000000..582adb5
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/ScaleTrack.java
@@ -0,0 +1,121 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ * @deprecated use spatial animation instead.
+ */
+@Deprecated
+public class ScaleTrack extends AbstractCinematicEvent {
+
+ private static final Logger log = Logger.getLogger(RotationTrack.class.getName());
+ private Vector3f startScale;
+ private Vector3f endScale;
+ private Spatial spatial;
+ private String spatialName = "";
+ private float value = 0;
+
+ @Override
+ public void initEvent(Application app, Cinematic cinematic) {
+ super.initEvent(app, cinematic);
+ if (spatial == null) {
+ spatial = cinematic.getScene().getChild(spatialName);
+ if (spatial == null) {
+ } else {
+ log.log(Level.WARNING, "spatial {0} not found in the scene", spatialName);
+ }
+ }
+ }
+
+ public ScaleTrack() {
+ }
+
+ public ScaleTrack(Spatial spatial, Vector3f endScale) {
+ this.endScale = endScale;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public ScaleTrack(Spatial spatial, Vector3f endScale, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.endScale = endScale;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public ScaleTrack(Spatial spatial, Vector3f endScale, LoopMode loopMode) {
+ super(loopMode);
+ this.endScale = endScale;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ public ScaleTrack(Spatial spatial, Vector3f endScale, float initialDuration) {
+ super(initialDuration);
+ this.endScale = endScale;
+ this.spatial = spatial;
+ spatialName = spatial.getName();
+ }
+
+ @Override
+ public void onPlay() {
+ if (playState != playState.Paused) {
+ startScale = spatial.getWorldScale().clone();
+ }
+ if (initialDuration == 0 && spatial != null) {
+ spatial.setLocalScale(endScale);
+ stop();
+ }
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ if (spatial != null) {
+ value = Math.min(time / initialDuration, 1.0f);
+ spatial.setLocalScale(FastMath.interpolateLinear(value, startScale, endScale));
+ }
+ }
+
+ @Override
+ public void onStop() {
+ value = 0;
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(spatialName, "spatialName", "");
+ oc.write(endScale, "endScale", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ spatialName = ic.readString("spatialName", "");
+ endScale = (Vector3f) ic.readSavable("endScale", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/cinematic/events/SoundTrack.java b/engine/src/core/com/jme3/cinematic/events/SoundTrack.java
new file mode 100644
index 0000000..77aae31
--- /dev/null
+++ b/engine/src/core/com/jme3/cinematic/events/SoundTrack.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.audio.AudioNode;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * A sound track to be played in a cinematic.
+ * @author Nehon
+ */
+public class SoundTrack extends AbstractCinematicEvent {
+
+ protected String path;
+ protected AudioNode audioNode;
+ protected boolean stream = false;
+
+ /**
+ * creates a sound track from the given resource path
+ * @param path the path to an audi file (ie : "Sounds/mySound.wav")
+ */
+ public SoundTrack(String path) {
+ this.path = path;
+ }
+
+ /**
+ * creates a sound track from the given resource path
+ * @param path the path to an audi file (ie : "Sounds/mySound.wav")
+ * @param stream true to make the audio data streamed
+ */
+ public SoundTrack(String path, boolean stream) {
+ this(path);
+ this.stream = stream;
+ }
+
+ public SoundTrack(String path, boolean stream, float initialDuration) {
+ super(initialDuration);
+ this.path = path;
+ this.stream = stream;
+ }
+
+ public SoundTrack(String path, boolean stream, LoopMode loopMode) {
+ super(loopMode);
+ this.path = path;
+ this.stream = stream;
+ }
+
+ public SoundTrack(String path, boolean stream, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.path = path;
+ this.stream = stream;
+ }
+
+ public SoundTrack(String path, float initialDuration) {
+ super(initialDuration);
+ this.path = path;
+ }
+
+ public SoundTrack(String path, LoopMode loopMode) {
+ super(loopMode);
+ this.path = path;
+ }
+
+ public SoundTrack(String path, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.path = path;
+ }
+
+ public SoundTrack() {
+ }
+
+ @Override
+ public void initEvent(Application app, Cinematic cinematic) {
+ super.initEvent(app, cinematic);
+ audioNode = new AudioNode(app.getAssetManager(), path, stream);
+ setLoopMode(loopMode);
+ }
+
+ @Override
+ public void setTime(float time) {
+ super.setTime(time);
+ //can occur on rewind
+ if (time < 0) {
+ stop();
+ }
+ audioNode.setTimeOffset(time);
+ }
+
+ @Override
+ public void onPlay() {
+ audioNode.play();
+ }
+
+ @Override
+ public void onStop() {
+ audioNode.stop();
+
+ }
+
+ @Override
+ public void onPause() {
+ audioNode.pause();
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ if (audioNode.getStatus() == AudioNode.Status.Stopped) {
+ stop();
+ }
+ }
+
+ /**
+ * Returns the underlying audion node of this sound track
+ * @return
+ */
+ public AudioNode getAudioNode() {
+ return audioNode;
+ }
+
+ @Override
+ public void setLoopMode(LoopMode loopMode) {
+ super.setLoopMode(loopMode);
+
+ if (loopMode != LoopMode.DontLoop) {
+ audioNode.setLooping(true);
+ } else {
+ audioNode.setLooping(false);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(path, "path", "");
+ oc.write(stream, "stream", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ path = ic.readString("path", "");
+ stream = ic.readBoolean("stream", false);
+
+ }
+}
diff --git a/engine/src/core/com/jme3/collision/Collidable.java b/engine/src/core/com/jme3/collision/Collidable.java
new file mode 100644
index 0000000..65c0631
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/Collidable.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision;
+
+/**
+ * Interface for Collidable objects.
+ * Classes that implement this interface are marked as collidable, meaning
+ * they support collision detection between other objects that are also
+ * collidable.
+ *
+ * @author Kirill Vainer
+ */
+public interface Collidable {
+
+ /**
+ * Check collision with another Collidable.
+ *
+ * @param other The object to check collision against
+ * @param results Will contain the list of {@link CollisionResult}s.
+ * @return how many collisions were found between this and other
+ */
+ public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException;
+}
diff --git a/engine/src/core/com/jme3/collision/CollisionResult.java b/engine/src/core/com/jme3/collision/CollisionResult.java
new file mode 100644
index 0000000..aef3936
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/CollisionResult.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision;
+
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+
+/**
+ * A <code>CollisionResult</code> represents a single collision instance
+ * between two {@link Collidable}. A collision check can result in many
+ * collision instances (places where collision has occured).
+ *
+ * @author Kirill Vainer
+ */
+public class CollisionResult implements Comparable<CollisionResult> {
+
+ private Geometry geometry;
+ private Vector3f contactPoint;
+ private Vector3f contactNormal;
+ private float distance;
+ private int triangleIndex;
+
+ public CollisionResult(Geometry geometry, Vector3f contactPoint, float distance, int triangleIndex) {
+ this.geometry = geometry;
+ this.contactPoint = contactPoint;
+ this.distance = distance;
+ this.triangleIndex = triangleIndex;
+ }
+
+ public CollisionResult(Vector3f contactPoint, float distance) {
+ this.contactPoint = contactPoint;
+ this.distance = distance;
+ }
+
+ public CollisionResult(){
+ }
+
+ public void setGeometry(Geometry geom){
+ this.geometry = geom;
+ }
+
+ public void setContactNormal(Vector3f norm){
+ this.contactNormal = norm;
+ }
+
+ public void setContactPoint(Vector3f point){
+ this.contactPoint = point;
+ }
+
+ public void setDistance(float dist){
+ this.distance = dist;
+ }
+
+ public void setTriangleIndex(int index){
+ this.triangleIndex = index;
+ }
+
+ public Triangle getTriangle(Triangle store){
+ if (store == null)
+ store = new Triangle();
+
+ Mesh m = geometry.getMesh();
+ m.getTriangle(triangleIndex, store);
+ store.calculateCenter();
+ store.calculateNormal();
+ return store;
+ }
+
+ public int compareTo(CollisionResult other) {
+ if (distance < other.distance)
+ return -1;
+ else if (distance > other.distance)
+ return 1;
+ else
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof CollisionResult){
+ return ((CollisionResult)obj).compareTo(this) == 0;
+ }
+ return super.equals(obj);
+ }
+
+ public Vector3f getContactPoint() {
+ return contactPoint;
+ }
+
+ public Vector3f getContactNormal() {
+ return contactNormal;
+ }
+
+ public float getDistance() {
+ return distance;
+ }
+
+ public Geometry getGeometry() {
+ return geometry;
+ }
+
+ public int getTriangleIndex() {
+ return triangleIndex;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/collision/CollisionResults.java b/engine/src/core/com/jme3/collision/CollisionResults.java
new file mode 100644
index 0000000..ec691c1
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/CollisionResults.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * <code>CollisionResults</code> is a collection returned as a result of a
+ * collision detection operation done by {@link Collidable}.
+ *
+ * @author Kirill Vainer
+ */
+public class CollisionResults implements Iterable<CollisionResult> {
+
+ private final ArrayList<CollisionResult> results = new ArrayList<CollisionResult>();
+ private boolean sorted = true;
+
+ /**
+ * Clears all collision results added to this list
+ */
+ public void clear(){
+ results.clear();
+ }
+
+ /**
+ * Iterator for iterating over the collision results.
+ *
+ * @return the iterator
+ */
+ public Iterator<CollisionResult> iterator() {
+ if (!sorted){
+ Collections.sort(results);
+ sorted = true;
+ }
+
+ return results.iterator();
+ }
+
+ public void addCollision(CollisionResult result){
+ results.add(result);
+ sorted = false;
+ }
+
+ public int size(){
+ return results.size();
+ }
+
+ public CollisionResult getClosestCollision(){
+ if (size() == 0)
+ return null;
+
+ if (!sorted){
+ Collections.sort(results);
+ sorted = true;
+ }
+
+ return results.get(0);
+ }
+
+ public CollisionResult getFarthestCollision(){
+ if (size() == 0)
+ return null;
+
+ if (!sorted){
+ Collections.sort(results);
+ sorted = true;
+ }
+
+ return results.get(size()-1);
+ }
+
+ public CollisionResult getCollision(int index){
+ if (!sorted){
+ Collections.sort(results);
+ sorted = true;
+ }
+
+ return results.get(index);
+ }
+
+ /**
+ * Internal use only.
+ * @param index
+ * @return
+ */
+ public CollisionResult getCollisionDirect(int index){
+ return results.get(index);
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ sb.append("CollisionResults[");
+ for (CollisionResult result : results){
+ sb.append(result).append(", ");
+ }
+ if (results.size() > 0)
+ sb.setLength(sb.length()-2);
+
+ sb.append("]");
+ return sb.toString();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/collision/MotionAllowedListener.java b/engine/src/core/com/jme3/collision/MotionAllowedListener.java
new file mode 100644
index 0000000..7703e5a
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/MotionAllowedListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision;
+
+import com.jme3.math.Vector3f;
+
+public interface MotionAllowedListener {
+
+ /**
+ * Check if motion allowed. Modify position and velocity vectors
+ * appropriately if not allowed..
+ *
+ * @param position
+ * @param velocity
+ */
+ public void checkMotionAllowed(Vector3f position, Vector3f velocity);
+
+}
diff --git a/engine/src/core/com/jme3/collision/SweepSphere.java b/engine/src/core/com/jme3/collision/SweepSphere.java
new file mode 100644
index 0000000..9d43f89
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/SweepSphere.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision;
+
+import com.jme3.math.*;
+
+/**
+ * No longer public ..
+ *
+ * @author Kirill Vainer
+ */
+@Deprecated
+class SweepSphere implements Collidable {
+
+ private Vector3f velocity = new Vector3f();
+ private Vector3f center = new Vector3f();
+ private Vector3f dimension = new Vector3f();
+ private Vector3f invDim = new Vector3f();
+
+ private final Triangle scaledTri = new Triangle();
+ private final Plane triPlane = new Plane();
+ private final Vector3f temp1 = new Vector3f(),
+ temp2 = new Vector3f(),
+ temp3 = new Vector3f();
+ private final Vector3f sVelocity = new Vector3f(),
+ sCenter = new Vector3f();
+
+ public Vector3f getCenter() {
+ return center;
+ }
+
+ public void setCenter(Vector3f center) {
+ this.center.set(center);
+ }
+
+ public Vector3f getDimension() {
+ return dimension;
+ }
+
+ public void setDimension(Vector3f dimension) {
+ this.dimension.set(dimension);
+ this.invDim.set(1,1,1).divideLocal(dimension);
+ }
+
+ public void setDimension(float x, float y, float z){
+ this.dimension.set(x,y,z);
+ this.invDim.set(1,1,1).divideLocal(dimension);
+ }
+
+ public void setDimension(float dim){
+ this.dimension.set(dim, dim, dim);
+ this.invDim.set(1,1,1).divideLocal(dimension);
+ }
+
+ public Vector3f getVelocity() {
+ return velocity;
+ }
+
+ public void setVelocity(Vector3f velocity) {
+ this.velocity.set(velocity);
+ }
+
+ private boolean pointsOnSameSide(Vector3f p1, Vector3f p2, Vector3f line1, Vector3f line2) {
+ // V1 = (line2 - line1) x (p1 - line1)
+ // V2 = (p2 - line1) x (line2 - line1)
+
+ temp1.set(line2).subtractLocal(line1);
+ temp3.set(temp1);
+ temp2.set(p1).subtractLocal(line1);
+ temp1.crossLocal(temp2);
+
+ temp2.set(p2).subtractLocal(line1);
+ temp3.crossLocal(temp2);
+
+ // V1 . V2 >= 0
+ return temp1.dot(temp3) >= 0;
+ }
+
+ private boolean isPointInTriangle(Vector3f point, AbstractTriangle tri) {
+ if (pointsOnSameSide(point, tri.get1(), tri.get2(), tri.get3())
+ && pointsOnSameSide(point, tri.get2(), tri.get1(), tri.get3())
+ && pointsOnSameSide(point, tri.get3(), tri.get1(), tri.get2()))
+ return true;
+ return false;
+ }
+
+ private static float getLowestRoot(float a, float b, float c, float maxR) {
+ float determinant = b * b - 4f * a * c;
+ if (determinant < 0){
+ return Float.NaN;
+ }
+
+ float sqrtd = FastMath.sqrt(determinant);
+ float r1 = (-b - sqrtd) / (2f * a);
+ float r2 = (-b + sqrtd) / (2f * a);
+
+ if (r1 > r2){
+ float temp = r2;
+ r2 = r1;
+ r1 = temp;
+ }
+
+ if (r1 > 0 && r1 < maxR){
+ return r1;
+ }
+
+ if (r2 > 0 && r2 < maxR){
+ return r2;
+ }
+
+ return Float.NaN;
+ }
+
+ private float collideWithVertex(Vector3f sCenter, Vector3f sVelocity,
+ float velocitySquared, Vector3f vertex, float t) {
+ // A = velocity * velocity
+ // B = 2 * (velocity . (center - vertex));
+ // C = ||vertex - center||^2 - 1f;
+
+ temp1.set(sCenter).subtractLocal(vertex);
+ float a = velocitySquared;
+ float b = 2f * sVelocity.dot(temp1);
+ float c = temp1.negateLocal().lengthSquared() - 1f;
+ float newT = getLowestRoot(a, b, c, t);
+
+// float A = velocitySquared;
+// float B = sCenter.subtract(vertex).dot(sVelocity) * 2f;
+// float C = vertex.subtract(sCenter).lengthSquared() - 1f;
+//
+// float newT = getLowestRoot(A, B, C, Float.MAX_VALUE);
+// if (newT > 1.0f)
+// newT = Float.NaN;
+
+ return newT;
+ }
+
+ private float collideWithSegment(Vector3f sCenter,
+ Vector3f sVelocity,
+ float velocitySquared,
+ Vector3f l1,
+ Vector3f l2,
+ float t,
+ Vector3f store) {
+ Vector3f edge = temp1.set(l2).subtractLocal(l1);
+ Vector3f base = temp2.set(l1).subtractLocal(sCenter);
+
+ float edgeSquared = edge.lengthSquared();
+ float baseSquared = base.lengthSquared();
+
+ float EdotV = edge.dot(sVelocity);
+ float EdotB = edge.dot(base);
+
+ float a = (edgeSquared * -velocitySquared) + EdotV * EdotV;
+ float b = (edgeSquared * 2f * sVelocity.dot(base))
+ - (2f * EdotV * EdotB);
+ float c = (edgeSquared * (1f - baseSquared)) + EdotB * EdotB;
+ float newT = getLowestRoot(a, b, c, t);
+ if (!Float.isNaN(newT)){
+ float f = (EdotV * newT - EdotB) / edgeSquared;
+ if (f >= 0f && f < 1f){
+ store.scaleAdd(f, edge, l1);
+ return newT;
+ }
+ }
+ return Float.NaN;
+ }
+
+ private CollisionResult collideWithTriangle(AbstractTriangle tri){
+ // scale scaledTriangle based on dimension
+ scaledTri.get1().set(tri.get1()).multLocal(invDim);
+ scaledTri.get2().set(tri.get2()).multLocal(invDim);
+ scaledTri.get3().set(tri.get3()).multLocal(invDim);
+// Vector3f sVelocity = velocity.mult(invDim);
+// Vector3f sCenter = center.mult(invDim);
+ velocity.mult(invDim, sVelocity);
+ center.mult(invDim, sCenter);
+
+ triPlane.setPlanePoints(scaledTri);
+
+ float normalDotVelocity = triPlane.getNormal().dot(sVelocity);
+ // XXX: sVelocity.normalize() needed?
+ // back facing scaledTriangles not considered
+ if (normalDotVelocity > 0f)
+ return null;
+
+ float t0, t1;
+ boolean embedded = false;
+
+ float signedDistanceToPlane = triPlane.pseudoDistance(sCenter);
+ if (normalDotVelocity == 0.0f){
+ // we are travelling exactly parrallel to the plane
+ if (FastMath.abs(signedDistanceToPlane) >= 1.0f){
+ // no collision possible
+ return null;
+ }else{
+ // we are embedded
+ t0 = 0;
+ t1 = 1;
+ embedded = true;
+ System.out.println("EMBEDDED");
+ return null;
+ }
+ }else{
+ t0 = (-1f - signedDistanceToPlane) / normalDotVelocity;
+ t1 = ( 1f - signedDistanceToPlane) / normalDotVelocity;
+
+ if (t0 > t1){
+ float tf = t1;
+ t1 = t0;
+ t0 = tf;
+ }
+
+ if (t0 > 1.0f || t1 < 0.0f){
+ // collision is out of this sVelocity range
+ return null;
+ }
+
+ // clamp the interval to [0, 1]
+ t0 = Math.max(t0, 0.0f);
+ t1 = Math.min(t1, 1.0f);
+ }
+
+ boolean foundCollision = false;
+ float minT = 1f;
+
+ Vector3f contactPoint = new Vector3f();
+ Vector3f contactNormal = new Vector3f();
+
+// if (!embedded){
+ // check against the inside of the scaledTriangle
+ // contactPoint = sCenter - p.normal + t0 * sVelocity
+ contactPoint.set(sVelocity);
+ contactPoint.multLocal(t0);
+ contactPoint.addLocal(sCenter);
+ contactPoint.subtractLocal(triPlane.getNormal());
+
+ // test to see if the collision is on a scaledTriangle interior
+ if (isPointInTriangle(contactPoint, scaledTri) && !embedded){
+ foundCollision = true;
+
+ minT = t0;
+
+ // scale collision point back into R3
+ contactPoint.multLocal(dimension);
+ contactNormal.set(velocity).multLocal(t0);
+ contactNormal.addLocal(center);
+ contactNormal.subtractLocal(contactPoint).normalizeLocal();
+
+// contactNormal.set(triPlane.getNormal());
+
+ CollisionResult result = new CollisionResult();
+ result.setContactPoint(contactPoint);
+ result.setContactNormal(contactNormal);
+ result.setDistance(minT * velocity.length());
+ return result;
+ }
+// }
+
+ float velocitySquared = sVelocity.lengthSquared();
+
+ Vector3f v1 = scaledTri.get1();
+ Vector3f v2 = scaledTri.get2();
+ Vector3f v3 = scaledTri.get3();
+
+ // vertex 1
+ float newT;
+ newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v1, minT);
+ if (!Float.isNaN(newT)){
+ minT = newT;
+ contactPoint.set(v1);
+ foundCollision = true;
+ }
+
+ // vertex 2
+ newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v2, minT);
+ if (!Float.isNaN(newT)){
+ minT = newT;
+ contactPoint.set(v2);
+ foundCollision = true;
+ }
+
+ // vertex 3
+ newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v3, minT);
+ if (!Float.isNaN(newT)){
+ minT = newT;
+ contactPoint.set(v3);
+ foundCollision = true;
+ }
+
+ // edge 1-2
+ newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v1, v2, minT, contactPoint);
+ if (!Float.isNaN(newT)){
+ minT = newT;
+ foundCollision = true;
+ }
+
+ // edge 2-3
+ newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v2, v3, minT, contactPoint);
+ if (!Float.isNaN(newT)){
+ minT = newT;
+ foundCollision = true;
+ }
+
+ // edge 3-1
+ newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v3, v1, minT, contactPoint);
+ if (!Float.isNaN(newT)){
+ minT = newT;
+ foundCollision = true;
+ }
+
+ if (foundCollision){
+ // compute contact normal based on minimum t
+ contactPoint.multLocal(dimension);
+ contactNormal.set(velocity).multLocal(t0);
+ contactNormal.addLocal(center);
+ contactNormal.subtractLocal(contactPoint).normalizeLocal();
+
+ CollisionResult result = new CollisionResult();
+ result.setContactPoint(contactPoint);
+ result.setContactNormal(contactNormal);
+ result.setDistance(minT * velocity.length());
+
+ return result;
+ }else{
+ return null;
+ }
+ }
+
+ public CollisionResult collideWithSweepSphere(SweepSphere other){
+ temp1.set(velocity).subtractLocal(other.velocity);
+ temp2.set(center).subtractLocal(other.center);
+ temp3.set(dimension).addLocal(other.dimension);
+ // delta V
+ // delta C
+ // Rsum
+
+ float a = temp1.lengthSquared();
+ float b = 2f * temp1.dot(temp2);
+ float c = temp2.lengthSquared() - temp3.getX() * temp3.getX();
+ float t = getLowestRoot(a, b, c, 1);
+
+ // no collision
+ if (Float.isNaN(t))
+ return null;
+
+ CollisionResult result = new CollisionResult();
+ result.setDistance(velocity.length() * t);
+
+ temp1.set(velocity).multLocal(t).addLocal(center);
+ temp2.set(other.velocity).multLocal(t).addLocal(other.center);
+ temp3.set(temp2).subtractLocal(temp1);
+ // temp3 contains center to other.center vector
+
+ // normalize it to get normal
+ temp2.set(temp3).normalizeLocal();
+ result.setContactNormal(new Vector3f(temp2));
+
+ // temp3 is contact point
+ temp3.set(temp2).multLocal(dimension).addLocal(temp1);
+ result.setContactPoint(new Vector3f(temp3));
+
+ return result;
+ }
+
+ public static void main(String[] args){
+ SweepSphere ss = new SweepSphere();
+ ss.setCenter(Vector3f.ZERO);
+ ss.setDimension(1);
+ ss.setVelocity(new Vector3f(10, 10, 10));
+
+ SweepSphere ss2 = new SweepSphere();
+ ss2.setCenter(new Vector3f(5, 5, 5));
+ ss2.setDimension(1);
+ ss2.setVelocity(new Vector3f(-10, -10, -10));
+
+ CollisionResults cr = new CollisionResults();
+ ss.collideWith(ss2, cr);
+ if (cr.size() > 0){
+ CollisionResult c = cr.getClosestCollision();
+ System.out.println("D = "+c.getDistance());
+ System.out.println("P = "+c.getContactPoint());
+ System.out.println("N = "+c.getContactNormal());
+ }
+ }
+
+ public int collideWith(Collidable other, CollisionResults results)
+ throws UnsupportedCollisionException {
+ if (other instanceof AbstractTriangle){
+ AbstractTriangle tri = (AbstractTriangle) other;
+ CollisionResult result = collideWithTriangle(tri);
+ if (result != null){
+ results.addCollision(result);
+ return 1;
+ }
+ return 0;
+ }else if (other instanceof SweepSphere){
+ SweepSphere sph = (SweepSphere) other;
+
+ CollisionResult result = collideWithSweepSphere(sph);
+ if (result != null){
+ results.addCollision(result);
+ return 1;
+ }
+ return 0;
+ }else{
+ throw new UnsupportedCollisionException();
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/collision/UnsupportedCollisionException.java b/engine/src/core/com/jme3/collision/UnsupportedCollisionException.java
new file mode 100644
index 0000000..ddd6779
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/UnsupportedCollisionException.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision;
+
+/**
+ * Thrown by {@link Collidable} when the requested collision query could not
+ * be completed because one of the collidables does not support colliding with
+ * the other.
+ *
+ * @author Kirill Vainer
+ */
+public class UnsupportedCollisionException extends UnsupportedOperationException {
+
+ public UnsupportedCollisionException(Throwable arg0) {
+ super(arg0);
+ }
+
+ public UnsupportedCollisionException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ public UnsupportedCollisionException(String arg0) {
+ super(arg0);
+ }
+
+ public UnsupportedCollisionException() {
+ super();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/collision/bih/BIHNode.java b/engine/src/core/com/jme3/collision/bih/BIHNode.java
new file mode 100644
index 0000000..0f5c1c8
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/bih/BIHNode.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.collision.bih;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.export.*;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Ray;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector3f;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import java.util.ArrayList;
+
+/**
+ * Bounding Interval Hierarchy.
+ * Based on:
+ *
+ * Instant Ray Tracing: The Bounding Interval Hierarchy
+ * By Carsten Wächter and Alexander Keller
+ */
+public final class BIHNode implements Savable {
+
+ private int leftIndex, rightIndex;
+ private BIHNode left;
+ private BIHNode right;
+ private float leftPlane;
+ private float rightPlane;
+ private int axis;
+
+ //Do not do this: It increases memory usage of each BIHNode by at least 56 bytes!
+ //
+ //private Triangle tmpTriangle = new Triangle();
+
+ public BIHNode(int l, int r) {
+ leftIndex = l;
+ rightIndex = r;
+ axis = 3; // indicates leaf
+ }
+
+ public BIHNode(int axis) {
+ this.axis = axis;
+ }
+
+ public BIHNode() {
+ }
+
+ public BIHNode getLeftChild() {
+ return left;
+ }
+
+ public void setLeftChild(BIHNode left) {
+ this.left = left;
+ }
+
+ public float getLeftPlane() {
+ return leftPlane;
+ }
+
+ public void setLeftPlane(float leftPlane) {
+ this.leftPlane = leftPlane;
+ }
+
+ public BIHNode getRightChild() {
+ return right;
+ }
+
+ public void setRightChild(BIHNode right) {
+ this.right = right;
+ }
+
+ public float getRightPlane() {
+ return rightPlane;
+ }
+
+ public void setRightPlane(float rightPlane) {
+ this.rightPlane = rightPlane;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(leftIndex, "left_index", 0);
+ oc.write(rightIndex, "right_index", 0);
+ oc.write(leftPlane, "left_plane", 0);
+ oc.write(rightPlane, "right_plane", 0);
+ oc.write(axis, "axis", 0);
+ oc.write(left, "left_node", null);
+ oc.write(right, "right_node", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ leftIndex = ic.readInt("left_index", 0);
+ rightIndex = ic.readInt("right_index", 0);
+ leftPlane = ic.readFloat("left_plane", 0);
+ rightPlane = ic.readFloat("right_plane", 0);
+ axis = ic.readInt("axis", 0);
+ left = (BIHNode) ic.readSavable("left_node", null);
+ right = (BIHNode) ic.readSavable("right_node", null);
+ }
+
+ public static final class BIHStackData {
+
+ private final BIHNode node;
+ private final float min, max;
+
+ BIHStackData(BIHNode node, float min, float max) {
+ this.node = node;
+ this.min = min;
+ this.max = max;
+ }
+ }
+
+ public final int intersectWhere(Collidable col,
+ BoundingBox box,
+ Matrix4f worldMatrix,
+ BIHTree tree,
+ CollisionResults results) {
+
+ TempVars vars = TempVars.get();
+ ArrayList<BIHStackData> stack = vars.bihStack;
+ stack.clear();
+
+ float[] minExts = {box.getCenter().x - box.getXExtent(),
+ box.getCenter().y - box.getYExtent(),
+ box.getCenter().z - box.getZExtent()};
+
+ float[] maxExts = {box.getCenter().x + box.getXExtent(),
+ box.getCenter().y + box.getYExtent(),
+ box.getCenter().z + box.getZExtent()};
+
+ stack.add(new BIHStackData(this, 0, 0));
+
+ Triangle t = new Triangle();
+ int cols = 0;
+
+ stackloop:
+ while (stack.size() > 0) {
+ BIHNode node = stack.remove(stack.size() - 1).node;
+
+ while (node.axis != 3) {
+ int a = node.axis;
+
+ float maxExt = maxExts[a];
+ float minExt = minExts[a];
+
+ if (node.leftPlane < node.rightPlane) {
+ // means there's a gap in the middle
+ // if the box is in that gap, we stop there
+ if (minExt > node.leftPlane
+ && maxExt < node.rightPlane) {
+ continue stackloop;
+ }
+ }
+
+ if (maxExt < node.rightPlane) {
+ node = node.left;
+ } else if (minExt > node.leftPlane) {
+ node = node.right;
+ } else {
+ stack.add(new BIHStackData(node.right, 0, 0));
+ node = node.left;
+ }
+// if (maxExt < node.leftPlane
+// && maxExt < node.rightPlane){
+// node = node.left;
+// }else if (minExt > node.leftPlane
+// && minExt > node.rightPlane){
+// node = node.right;
+// }else{
+
+// }
+ }
+
+ for (int i = node.leftIndex; i <= node.rightIndex; i++) {
+ tree.getTriangle(i, t.get1(), t.get2(), t.get3());
+ if (worldMatrix != null) {
+ worldMatrix.mult(t.get1(), t.get1());
+ worldMatrix.mult(t.get2(), t.get2());
+ worldMatrix.mult(t.get3(), t.get3());
+ }
+
+ int added = col.collideWith(t, results);
+
+ if (added > 0) {
+ int index = tree.getTriangleIndex(i);
+ int start = results.size() - added;
+
+ for (int j = start; j < results.size(); j++) {
+ CollisionResult cr = results.getCollisionDirect(j);
+ cr.setTriangleIndex(index);
+ }
+
+ cols += added;
+ }
+ }
+ }
+ vars.release();
+ return cols;
+ }
+
+ public final int intersectBrute(Ray r,
+ Matrix4f worldMatrix,
+ BIHTree tree,
+ float sceneMin,
+ float sceneMax,
+ CollisionResults results) {
+ float tHit = Float.POSITIVE_INFINITY;
+
+ TempVars vars = TempVars.get();
+
+ Vector3f v1 = vars.vect1,
+ v2 = vars.vect2,
+ v3 = vars.vect3;
+
+ int cols = 0;
+
+ ArrayList<BIHStackData> stack = vars.bihStack;
+ stack.clear();
+ stack.add(new BIHStackData(this, 0, 0));
+ stackloop:
+ while (stack.size() > 0) {
+
+ BIHStackData data = stack.remove(stack.size() - 1);
+ BIHNode node = data.node;
+
+ leafloop:
+ while (node.axis != 3) { // while node is not a leaf
+ BIHNode nearNode, farNode;
+ nearNode = node.left;
+ farNode = node.right;
+
+ stack.add(new BIHStackData(farNode, 0, 0));
+ node = nearNode;
+ }
+
+ // a leaf
+ for (int i = node.leftIndex; i <= node.rightIndex; i++) {
+ tree.getTriangle(i, v1, v2, v3);
+
+ if (worldMatrix != null) {
+ worldMatrix.mult(v1, v1);
+ worldMatrix.mult(v2, v2);
+ worldMatrix.mult(v3, v3);
+ }
+
+ float t = r.intersects(v1, v2, v3);
+ if (t < tHit) {
+ tHit = t;
+ Vector3f contactPoint = new Vector3f(r.direction).multLocal(tHit).addLocal(r.origin);
+ CollisionResult cr = new CollisionResult(contactPoint, tHit);
+ cr.setTriangleIndex(tree.getTriangleIndex(i));
+ results.addCollision(cr);
+ cols++;
+ }
+ }
+ }
+ vars.release();
+ return cols;
+ }
+
+ public final int intersectWhere(Ray r,
+ Matrix4f worldMatrix,
+ BIHTree tree,
+ float sceneMin,
+ float sceneMax,
+ CollisionResults results) {
+
+ TempVars vars = TempVars.get();
+ ArrayList<BIHStackData> stack = vars.bihStack;
+ stack.clear();
+
+// float tHit = Float.POSITIVE_INFINITY;
+
+ Vector3f o = vars.vect1.set(r.getOrigin());
+ Vector3f d = vars.vect2.set(r.getDirection());
+
+ Matrix4f inv =vars.tempMat4.set(worldMatrix).invertLocal();
+
+ inv.mult(r.getOrigin(), r.getOrigin());
+
+ // Fixes rotation collision bug
+ inv.multNormal(r.getDirection(), r.getDirection());
+// inv.multNormalAcross(r.getDirection(), r.getDirection());
+
+ float[] origins = {r.getOrigin().x,
+ r.getOrigin().y,
+ r.getOrigin().z};
+
+ float[] invDirections = {1f / r.getDirection().x,
+ 1f / r.getDirection().y,
+ 1f / r.getDirection().z};
+
+ r.getDirection().normalizeLocal();
+
+ Vector3f v1 = vars.vect3,
+ v2 = vars.vect4,
+ v3 = vars.vect5;
+ int cols = 0;
+
+ stack.add(new BIHStackData(this, sceneMin, sceneMax));
+ stackloop:
+ while (stack.size() > 0) {
+
+ BIHStackData data = stack.remove(stack.size() - 1);
+ BIHNode node = data.node;
+ float tMin = data.min,
+ tMax = data.max;
+
+ if (tMax < tMin) {
+ continue;
+ }
+
+ leafloop:
+ while (node.axis != 3) { // while node is not a leaf
+ int a = node.axis;
+
+ // find the origin and direction value for the given axis
+ float origin = origins[a];
+ float invDirection = invDirections[a];
+
+ float tNearSplit, tFarSplit;
+ BIHNode nearNode, farNode;
+
+ tNearSplit = (node.leftPlane - origin) * invDirection;
+ tFarSplit = (node.rightPlane - origin) * invDirection;
+ nearNode = node.left;
+ farNode = node.right;
+
+ if (invDirection < 0) {
+ float tmpSplit = tNearSplit;
+ tNearSplit = tFarSplit;
+ tFarSplit = tmpSplit;
+
+ BIHNode tmpNode = nearNode;
+ nearNode = farNode;
+ farNode = tmpNode;
+ }
+
+ if (tMin > tNearSplit && tMax < tFarSplit) {
+ continue stackloop;
+ }
+
+ if (tMin > tNearSplit) {
+ tMin = max(tMin, tFarSplit);
+ node = farNode;
+ } else if (tMax < tFarSplit) {
+ tMax = min(tMax, tNearSplit);
+ node = nearNode;
+ } else {
+ stack.add(new BIHStackData(farNode, max(tMin, tFarSplit), tMax));
+ tMax = min(tMax, tNearSplit);
+ node = nearNode;
+ }
+ }
+
+// if ( (node.rightIndex - node.leftIndex) > minTrisPerNode){
+// // on demand subdivision
+// node.subdivide();
+// stack.add(new BIHStackData(node, tMin, tMax));
+// continue stackloop;
+// }
+
+ // a leaf
+ for (int i = node.leftIndex; i <= node.rightIndex; i++) {
+ tree.getTriangle(i, v1, v2, v3);
+
+ float t = r.intersects(v1, v2, v3);
+ if (!Float.isInfinite(t)) {
+ if (worldMatrix != null) {
+ worldMatrix.mult(v1, v1);
+ worldMatrix.mult(v2, v2);
+ worldMatrix.mult(v3, v3);
+ float t_world = new Ray(o, d).intersects(v1, v2, v3);
+ t = t_world;
+ }
+
+ Vector3f contactNormal = Triangle.computeTriangleNormal(v1, v2, v3, null);
+ Vector3f contactPoint = new Vector3f(d).multLocal(t).addLocal(o);
+ float worldSpaceDist = o.distance(contactPoint);
+
+ CollisionResult cr = new CollisionResult(contactPoint, worldSpaceDist);
+ cr.setContactNormal(contactNormal);
+ cr.setTriangleIndex(tree.getTriangleIndex(i));
+ results.addCollision(cr);
+ cols++;
+ }
+ }
+ }
+ vars.release();
+ r.setOrigin(o);
+ r.setDirection(d);
+
+ return cols;
+ }
+}
diff --git a/engine/src/core/com/jme3/collision/bih/BIHTree.java b/engine/src/core/com/jme3/collision/bih/BIHTree.java
new file mode 100644
index 0000000..3660922
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/bih/BIHTree.java
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.collision.bih;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.CollisionData;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.scene.mesh.VirtualIndexBuffer;
+import com.jme3.scene.mesh.WrappedIndexBuffer;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import static java.lang.Math.max;
+import java.nio.FloatBuffer;
+
+public class BIHTree implements CollisionData {
+
+ public static final int MAX_TREE_DEPTH = 100;
+ public static final int MAX_TRIS_PER_NODE = 21;
+ private Mesh mesh;
+ private BIHNode root;
+ private int maxTrisPerNode;
+ private int numTris;
+ private float[] pointData;
+ private int[] triIndices;
+
+ private transient CollisionResults boundResults = new CollisionResults();
+ private transient float[] bihSwapTmp;
+
+ private static final TriangleAxisComparator[] comparators = new TriangleAxisComparator[]
+ {
+ new TriangleAxisComparator(0),
+ new TriangleAxisComparator(1),
+ new TriangleAxisComparator(2)
+ };
+
+ private void initTriList(FloatBuffer vb, IndexBuffer ib) {
+ pointData = new float[numTris * 3 * 3];
+ int p = 0;
+ for (int i = 0; i < numTris * 3; i += 3) {
+ int vert = ib.get(i) * 3;
+ pointData[p++] = vb.get(vert++);
+ pointData[p++] = vb.get(vert++);
+ pointData[p++] = vb.get(vert);
+
+ vert = ib.get(i + 1) * 3;
+ pointData[p++] = vb.get(vert++);
+ pointData[p++] = vb.get(vert++);
+ pointData[p++] = vb.get(vert);
+
+ vert = ib.get(i + 2) * 3;
+ pointData[p++] = vb.get(vert++);
+ pointData[p++] = vb.get(vert++);
+ pointData[p++] = vb.get(vert);
+ }
+
+ triIndices = new int[numTris];
+ for (int i = 0; i < numTris; i++) {
+ triIndices[i] = i;
+ }
+ }
+
+ public BIHTree(Mesh mesh, int maxTrisPerNode) {
+ this.mesh = mesh;
+ this.maxTrisPerNode = maxTrisPerNode;
+
+ if (maxTrisPerNode < 1 || mesh == null) {
+ throw new IllegalArgumentException();
+ }
+
+ bihSwapTmp = new float[9];
+
+ FloatBuffer vb = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ IndexBuffer ib = mesh.getIndexBuffer();
+ if (ib == null) {
+ ib = new VirtualIndexBuffer(mesh.getVertexCount(), mesh.getMode());
+ } else if (mesh.getMode() != Mode.Triangles) {
+ ib = new WrappedIndexBuffer(mesh);
+ }
+
+ numTris = ib.size() / 3;
+ initTriList(vb, ib);
+ }
+
+ public BIHTree(Mesh mesh) {
+ this(mesh, MAX_TRIS_PER_NODE);
+ }
+
+ public BIHTree() {
+ }
+
+ public void construct() {
+ BoundingBox sceneBbox = createBox(0, numTris - 1);
+ root = createNode(0, numTris - 1, sceneBbox, 0);
+ }
+
+ private BoundingBox createBox(int l, int r) {
+ TempVars vars = TempVars.get();
+
+ Vector3f min = vars.vect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+ Vector3f max = vars.vect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+
+ Vector3f v1 = vars.vect3,
+ v2 = vars.vect4,
+ v3 = vars.vect5;
+
+ for (int i = l; i <= r; i++) {
+ getTriangle(i, v1, v2, v3);
+ BoundingBox.checkMinMax(min, max, v1);
+ BoundingBox.checkMinMax(min, max, v2);
+ BoundingBox.checkMinMax(min, max, v3);
+ }
+
+ BoundingBox bbox = new BoundingBox(min, max);
+ vars.release();
+ return bbox;
+ }
+
+ int getTriangleIndex(int triIndex) {
+ return triIndices[triIndex];
+ }
+
+ private int sortTriangles(int l, int r, float split, int axis) {
+ int pivot = l;
+ int j = r;
+
+ TempVars vars = TempVars.get();
+
+ Vector3f v1 = vars.vect1,
+ v2 = vars.vect2,
+ v3 = vars.vect3;
+
+ while (pivot <= j) {
+ getTriangle(pivot, v1, v2, v3);
+ v1.addLocal(v2).addLocal(v3).multLocal(FastMath.ONE_THIRD);
+ if (v1.get(axis) > split) {
+ swapTriangles(pivot, j);
+ --j;
+ } else {
+ ++pivot;
+ }
+ }
+
+ vars.release();
+ pivot = (pivot == l && j < pivot) ? j : pivot;
+ return pivot;
+ }
+
+ private void setMinMax(BoundingBox bbox, boolean doMin, int axis, float value) {
+ Vector3f min = bbox.getMin(null);
+ Vector3f max = bbox.getMax(null);
+
+ if (doMin) {
+ min.set(axis, value);
+ } else {
+ max.set(axis, value);
+ }
+
+ bbox.setMinMax(min, max);
+ }
+
+ private float getMinMax(BoundingBox bbox, boolean doMin, int axis) {
+ if (doMin) {
+ return bbox.getMin(null).get(axis);
+ } else {
+ return bbox.getMax(null).get(axis);
+ }
+ }
+
+// private BIHNode createNode2(int l, int r, BoundingBox nodeBbox, int depth){
+// if ((r - l) < maxTrisPerNode || depth > 100)
+// return createLeaf(l, r);
+//
+// BoundingBox currentBox = createBox(l, r);
+// int axis = depth % 3;
+// float split = currentBox.getCenter().get(axis);
+//
+// TriangleAxisComparator comparator = comparators[axis];
+// Arrays.sort(tris, l, r, comparator);
+// int splitIndex = -1;
+//
+// float leftPlane, rightPlane = Float.POSITIVE_INFINITY;
+// leftPlane = tris[l].getExtreme(axis, false);
+// for (int i = l; i <= r; i++){
+// BIHTriangle tri = tris[i];
+// if (splitIndex == -1){
+// float v = tri.getCenter().get(axis);
+// if (v > split){
+// if (i == 0){
+// // no left plane
+// splitIndex = -2;
+// }else{
+// splitIndex = i;
+// // first triangle assigned to right
+// rightPlane = tri.getExtreme(axis, true);
+// }
+// }else{
+// // triangle assigned to left
+// float ex = tri.getExtreme(axis, false);
+// if (ex > leftPlane)
+// leftPlane = ex;
+// }
+// }else{
+// float ex = tri.getExtreme(axis, true);
+// if (ex < rightPlane)
+// rightPlane = ex;
+// }
+// }
+//
+// if (splitIndex < 0){
+// splitIndex = (r - l) / 2;
+//
+// leftPlane = Float.NEGATIVE_INFINITY;
+// rightPlane = Float.POSITIVE_INFINITY;
+//
+// for (int i = l; i < splitIndex; i++){
+// float ex = tris[i].getExtreme(axis, false);
+// if (ex > leftPlane){
+// leftPlane = ex;
+// }
+// }
+// for (int i = splitIndex; i <= r; i++){
+// float ex = tris[i].getExtreme(axis, true);
+// if (ex < rightPlane){
+// rightPlane = ex;
+// }
+// }
+// }
+//
+// BIHNode node = new BIHNode(axis);
+// node.leftPlane = leftPlane;
+// node.rightPlane = rightPlane;
+//
+// node.leftIndex = l;
+// node.rightIndex = r;
+//
+// BoundingBox leftBbox = new BoundingBox(currentBox);
+// setMinMax(leftBbox, false, axis, split);
+// node.left = createNode2(l, splitIndex-1, leftBbox, depth+1);
+//
+// BoundingBox rightBbox = new BoundingBox(currentBox);
+// setMinMax(rightBbox, true, axis, split);
+// node.right = createNode2(splitIndex, r, rightBbox, depth+1);
+//
+// return node;
+// }
+ private BIHNode createNode(int l, int r, BoundingBox nodeBbox, int depth) {
+ if ((r - l) < maxTrisPerNode || depth > MAX_TREE_DEPTH) {
+ return new BIHNode(l, r);
+ }
+
+ BoundingBox currentBox = createBox(l, r);
+
+ Vector3f exteriorExt = nodeBbox.getExtent(null);
+ Vector3f interiorExt = currentBox.getExtent(null);
+ exteriorExt.subtractLocal(interiorExt);
+
+ int axis = 0;
+ if (exteriorExt.x > exteriorExt.y) {
+ if (exteriorExt.x > exteriorExt.z) {
+ axis = 0;
+ } else {
+ axis = 2;
+ }
+ } else {
+ if (exteriorExt.y > exteriorExt.z) {
+ axis = 1;
+ } else {
+ axis = 2;
+ }
+ }
+ if (exteriorExt.equals(Vector3f.ZERO)) {
+ axis = 0;
+ }
+
+// Arrays.sort(tris, l, r, comparators[axis]);
+ float split = currentBox.getCenter().get(axis);
+ int pivot = sortTriangles(l, r, split, axis);
+ if (pivot == l || pivot == r) {
+ pivot = (r + l) / 2;
+ }
+
+ //If one of the partitions is empty, continue with recursion: same level but different bbox
+ if (pivot < l) {
+ //Only right
+ BoundingBox rbbox = new BoundingBox(currentBox);
+ setMinMax(rbbox, true, axis, split);
+ return createNode(l, r, rbbox, depth + 1);
+ } else if (pivot > r) {
+ //Only left
+ BoundingBox lbbox = new BoundingBox(currentBox);
+ setMinMax(lbbox, false, axis, split);
+ return createNode(l, r, lbbox, depth + 1);
+ } else {
+ //Build the node
+ BIHNode node = new BIHNode(axis);
+
+ //Left child
+ BoundingBox lbbox = new BoundingBox(currentBox);
+ setMinMax(lbbox, false, axis, split);
+
+ //The left node right border is the plane most right
+ node.setLeftPlane(getMinMax(createBox(l, max(l, pivot - 1)), false, axis));
+ node.setLeftChild(createNode(l, max(l, pivot - 1), lbbox, depth + 1)); //Recursive call
+
+ //Right Child
+ BoundingBox rbbox = new BoundingBox(currentBox);
+ setMinMax(rbbox, true, axis, split);
+ //The right node left border is the plane most left
+ node.setRightPlane(getMinMax(createBox(pivot, r), true, axis));
+ node.setRightChild(createNode(pivot, r, rbbox, depth + 1)); //Recursive call
+
+ return node;
+ }
+ }
+
+ public void getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3) {
+ int pointIndex = index * 9;
+
+ v1.x = pointData[pointIndex++];
+ v1.y = pointData[pointIndex++];
+ v1.z = pointData[pointIndex++];
+
+ v2.x = pointData[pointIndex++];
+ v2.y = pointData[pointIndex++];
+ v2.z = pointData[pointIndex++];
+
+ v3.x = pointData[pointIndex++];
+ v3.y = pointData[pointIndex++];
+ v3.z = pointData[pointIndex++];
+ }
+
+ public void swapTriangles(int index1, int index2) {
+ int p1 = index1 * 9;
+ int p2 = index2 * 9;
+
+ // store p1 in tmp
+ System.arraycopy(pointData, p1, bihSwapTmp, 0, 9);
+
+ // copy p2 to p1
+ System.arraycopy(pointData, p2, pointData, p1, 9);
+
+ // copy tmp to p2
+ System.arraycopy(bihSwapTmp, 0, pointData, p2, 9);
+
+ // swap indices
+ int tmp2 = triIndices[index1];
+ triIndices[index1] = triIndices[index2];
+ triIndices[index2] = tmp2;
+ }
+
+ private int collideWithRay(Ray r,
+ Matrix4f worldMatrix,
+ BoundingVolume worldBound,
+ CollisionResults results) {
+
+ boundResults.clear();
+ worldBound.collideWith(r, boundResults);
+ if (boundResults.size() > 0) {
+ float tMin = boundResults.getClosestCollision().getDistance();
+ float tMax = boundResults.getFarthestCollision().getDistance();
+
+ if (tMax <= 0) {
+ tMax = Float.POSITIVE_INFINITY;
+ } else if (tMin == tMax) {
+ tMin = 0;
+ }
+
+ if (tMin <= 0) {
+ tMin = 0;
+ }
+
+ if (r.getLimit() < Float.POSITIVE_INFINITY) {
+ tMax = Math.min(tMax, r.getLimit());
+ if (tMin > tMax){
+ return 0;
+ }
+ }
+
+// return root.intersectBrute(r, worldMatrix, this, tMin, tMax, results);
+ return root.intersectWhere(r, worldMatrix, this, tMin, tMax, results);
+ }
+ return 0;
+ }
+
+ private int collideWithBoundingVolume(BoundingVolume bv,
+ Matrix4f worldMatrix,
+ CollisionResults results) {
+ BoundingBox bbox;
+ if (bv instanceof BoundingSphere) {
+ BoundingSphere sphere = (BoundingSphere) bv;
+ bbox = new BoundingBox(bv.getCenter().clone(), sphere.getRadius(),
+ sphere.getRadius(),
+ sphere.getRadius());
+ } else if (bv instanceof BoundingBox) {
+ bbox = new BoundingBox((BoundingBox) bv);
+ } else {
+ throw new UnsupportedCollisionException();
+ }
+
+ bbox.transform(worldMatrix.invert(), bbox);
+ return root.intersectWhere(bv, bbox, worldMatrix, this, results);
+ }
+
+ public int collideWith(Collidable other,
+ Matrix4f worldMatrix,
+ BoundingVolume worldBound,
+ CollisionResults results) {
+
+ if (other instanceof Ray) {
+ Ray ray = (Ray) other;
+ return collideWithRay(ray, worldMatrix, worldBound, results);
+ } else if (other instanceof BoundingVolume) {
+ BoundingVolume bv = (BoundingVolume) other;
+ return collideWithBoundingVolume(bv, worldMatrix, results);
+ } else {
+ throw new UnsupportedCollisionException();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(mesh, "mesh", null);
+ oc.write(root, "root", null);
+ oc.write(maxTrisPerNode, "tris_per_node", 0);
+ oc.write(pointData, "points", null);
+ oc.write(triIndices, "indices", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ mesh = (Mesh) ic.readSavable("mesh", null);
+ root = (BIHNode) ic.readSavable("root", null);
+ maxTrisPerNode = ic.readInt("tris_per_node", 0);
+ pointData = ic.readFloatArray("points", null);
+ triIndices = ic.readIntArray("indices", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/collision/bih/BIHTriangle.java b/engine/src/core/com/jme3/collision/bih/BIHTriangle.java
new file mode 100644
index 0000000..2229da0
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/bih/BIHTriangle.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision.bih;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+
+public final class BIHTriangle {
+
+ private final Vector3f pointa = new Vector3f();
+ private final Vector3f pointb = new Vector3f();
+ private final Vector3f pointc = new Vector3f();
+ private final Vector3f center = new Vector3f();
+
+ public BIHTriangle(Vector3f p1, Vector3f p2, Vector3f p3) {
+ pointa.set(p1);
+ pointb.set(p2);
+ pointc.set(p3);
+ center.set(pointa);
+ center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);
+ }
+
+ public Vector3f get1(){
+ return pointa;
+ }
+
+ public Vector3f get2(){
+ return pointb;
+ }
+
+ public Vector3f get3(){
+ return pointc;
+ }
+
+ public Vector3f getCenter() {
+ return center;
+ }
+
+ public Vector3f getNormal(){
+ Vector3f normal = new Vector3f(pointb);
+ normal.subtractLocal(pointa).crossLocal(pointc.x-pointa.x, pointc.y-pointa.y, pointc.z-pointa.z);
+ normal.normalizeLocal();
+ return normal;
+ }
+
+ public float getExtreme(int axis, boolean left){
+ float v1, v2, v3;
+ switch (axis){
+ case 0: v1 = pointa.x; v2 = pointb.x; v3 = pointc.x; break;
+ case 1: v1 = pointa.y; v2 = pointb.y; v3 = pointc.y; break;
+ case 2: v1 = pointa.z; v2 = pointb.z; v3 = pointc.z; break;
+ default: assert false; return 0;
+ }
+ if (left){
+ if (v1 < v2){
+ if (v1 < v3)
+ return v1;
+ else
+ return v3;
+ }else{
+ if (v2 < v3)
+ return v2;
+ else
+ return v3;
+ }
+ }else{
+ if (v1 > v2){
+ if (v1 > v3)
+ return v1;
+ else
+ return v3;
+ }else{
+ if (v2 > v3)
+ return v2;
+ else
+ return v3;
+ }
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/collision/bih/TriangleAxisComparator.java b/engine/src/core/com/jme3/collision/bih/TriangleAxisComparator.java
new file mode 100644
index 0000000..895dd5a
--- /dev/null
+++ b/engine/src/core/com/jme3/collision/bih/TriangleAxisComparator.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.collision.bih;
+
+import com.jme3.math.Vector3f;
+import java.util.Comparator;
+
+public class TriangleAxisComparator implements Comparator<BIHTriangle> {
+
+ private final int axis;
+
+ public TriangleAxisComparator(int axis){
+ this.axis = axis;
+ }
+
+ public int compare(BIHTriangle o1, BIHTriangle o2) {
+ float v1, v2;
+ Vector3f c1 = o1.getCenter();
+ Vector3f c2 = o2.getCenter();
+ switch (axis){
+ case 0: v1 = c1.x; v2 = c2.x; break;
+ case 1: v1 = c1.y; v2 = c2.y; break;
+ case 2: v1 = c1.z; v2 = c2.z; break;
+ default: assert false; return 0;
+ }
+ if (v1 > v2)
+ return 1;
+ else if (v1 < v2)
+ return -1;
+ else
+ return 0;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/effect/Particle.java b/engine/src/core/com/jme3/effect/Particle.java
new file mode 100644
index 0000000..8e976f9
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/Particle.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.effect;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+
+/**
+ * Represents a single particle in a {@link ParticleEmitter}.
+ *
+ * @author Kirill Vainer
+ */
+public class Particle {
+
+ /**
+ * Particle velocity.
+ */
+ public final Vector3f velocity = new Vector3f();
+
+ /**
+ * Current particle position
+ */
+ public final Vector3f position = new Vector3f();
+
+ /**
+ * Particle color
+ */
+ public final ColorRGBA color = new ColorRGBA(0,0,0,0);
+
+ /**
+ * Particle size or radius.
+ */
+ public float size;
+
+ /**
+ * Particle remaining life, in seconds.
+ */
+ public float life;
+
+ /**
+ * The initial particle life
+ */
+ public float startlife;
+
+ /**
+ * Particle rotation angle (in radians).
+ */
+ public float angle;
+
+ /**
+ * Particle rotation angle speed (in radians).
+ */
+ public float rotateSpeed;
+
+ /**
+ * Particle image index.
+ */
+ public int imageIndex = 0;
+
+ /**
+ * Distance to camera. Only used for sorted particles.
+ */
+ //public float distToCam;
+}
diff --git a/engine/src/core/com/jme3/effect/ParticleComparator.java b/engine/src/core/com/jme3/effect/ParticleComparator.java
new file mode 100644
index 0000000..dbe52dd
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/ParticleComparator.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.effect;
+
+import com.jme3.renderer.Camera;
+import java.util.Comparator;
+
+@Deprecated
+class ParticleComparator implements Comparator<Particle> {
+
+ private Camera cam;
+
+ public void setCamera(Camera cam){
+ this.cam = cam;
+ }
+
+ public int compare(Particle p1, Particle p2) {
+ return 0; // unused
+ /*
+ if (p1.life <= 0 || p2.life <= 0)
+ return 0;
+
+// if (p1.life <= 0)
+// return 1;
+// else if (p2.life <= 0)
+// return -1;
+
+ float d1 = p1.distToCam, d2 = p2.distToCam;
+
+ if (d1 == -1){
+ d1 = cam.distanceToNearPlane(p1.position);
+ p1.distToCam = d1;
+ }
+ if (d2 == -1){
+ d2 = cam.distanceToNearPlane(p2.position);
+ p2.distToCam = d2;
+ }
+
+ if (d1 < d2)
+ return 1;
+ else if (d1 > d2)
+ return -1;
+ else
+ return 0;
+ */
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/effect/ParticleEmitter.java b/engine/src/core/com/jme3/effect/ParticleEmitter.java
new file mode 100644
index 0000000..c00e66e
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/ParticleEmitter.java
@@ -0,0 +1,1206 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.effect;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.influencers.DefaultParticleInfluencer;
+import com.jme3.effect.influencers.ParticleInfluencer;
+import com.jme3.effect.shapes.EmitterPointShape;
+import com.jme3.effect.shapes.EmitterShape;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * <code>ParticleEmitter</code> is a special kind of geometry which simulates
+ * a particle system.
+ * <p>
+ * Particle emitters can be used to simulate various kinds of phenomena,
+ * such as fire, smoke, explosions and much more.
+ * <p>
+ * Particle emitters have many properties which are used to control the
+ * simulation. The interpretation of these properties depends on the
+ * {@link ParticleInfluencer} that has been assigned to the emitter via
+ * {@link ParticleEmitter#setParticleInfluencer(com.jme3.effect.influencers.ParticleInfluencer) }.
+ * By default the implementation {@link DefaultParticleInfluencer} is used.
+ *
+ * @author Kirill Vainer
+ */
+public class ParticleEmitter extends Geometry {
+
+ private boolean enabled = true;
+ private static final EmitterShape DEFAULT_SHAPE = new EmitterPointShape(Vector3f.ZERO);
+ private static final ParticleInfluencer DEFAULT_INFLUENCER = new DefaultParticleInfluencer();
+ private ParticleEmitterControl control;
+ private EmitterShape shape = DEFAULT_SHAPE;
+ private ParticleMesh particleMesh;
+ private ParticleInfluencer particleInfluencer = DEFAULT_INFLUENCER;
+ private ParticleMesh.Type meshType;
+ private Particle[] particles;
+ private int firstUnUsed;
+ private int lastUsed;
+// private int next = 0;
+// private ArrayList<Integer> unusedIndices = new ArrayList<Integer>();
+ private boolean randomAngle;
+ private boolean selectRandomImage;
+ private boolean facingVelocity;
+ private float particlesPerSec = 20;
+ private float timeDifference = 0;
+ private float lowLife = 3f;
+ private float highLife = 7f;
+ private Vector3f gravity = new Vector3f(0.0f, 0.1f, 0.0f);
+ private float rotateSpeed;
+ private Vector3f faceNormal = new Vector3f(Vector3f.NAN);
+ private int imagesX = 1;
+ private int imagesY = 1;
+
+ private ColorRGBA startColor = new ColorRGBA(0.4f, 0.4f, 0.4f, 0.5f);
+ private ColorRGBA endColor = new ColorRGBA(0.1f, 0.1f, 0.1f, 0.0f);
+ private float startSize = 0.2f;
+ private float endSize = 2f;
+ private boolean worldSpace = true;
+ //variable that helps with computations
+ private transient Vector3f temp = new Vector3f();
+
+ public static class ParticleEmitterControl implements Control {
+
+ ParticleEmitter parentEmitter;
+
+ public ParticleEmitterControl() {
+ }
+
+ public ParticleEmitterControl(ParticleEmitter parentEmitter) {
+ this.parentEmitter = parentEmitter;
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ return this; // WARNING: Sets wrong control on spatial. Will be
+ // fixed automatically by ParticleEmitter.clone() method.
+ }
+
+ public void setSpatial(Spatial spatial) {
+ }
+
+ public void setEnabled(boolean enabled) {
+ parentEmitter.setEnabled(enabled);
+ }
+
+ public boolean isEnabled() {
+ return parentEmitter.isEnabled();
+ }
+
+ public void update(float tpf) {
+ parentEmitter.updateFromControl(tpf);
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ parentEmitter.renderFromControl(rm, vp);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ }
+ }
+
+ @Override
+ public ParticleEmitter clone() {
+ return clone(true);
+ }
+
+ @Override
+ public ParticleEmitter clone(boolean cloneMaterial) {
+ ParticleEmitter clone = (ParticleEmitter) super.clone(cloneMaterial);
+ clone.shape = shape.deepClone();
+
+ // Reinitialize particle list
+ clone.setNumParticles(particles.length);
+
+ clone.faceNormal = faceNormal.clone();
+ clone.startColor = startColor.clone();
+ clone.endColor = endColor.clone();
+ clone.particleInfluencer = particleInfluencer.clone();
+
+ // remove wrong control
+ clone.controls.remove(control);
+
+ // put correct control
+ clone.controls.add(new ParticleEmitterControl(clone));
+
+ // Reinitialize particle mesh
+ switch (meshType) {
+ case Point:
+ clone.particleMesh = new ParticlePointMesh();
+ clone.setMesh(clone.particleMesh);
+ break;
+ case Triangle:
+ clone.particleMesh = new ParticleTriMesh();
+ clone.setMesh(clone.particleMesh);
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized particle type: " + meshType);
+ }
+ clone.particleMesh.initParticleData(clone, clone.particles.length);
+ clone.particleMesh.setImagesXY(clone.imagesX, clone.imagesY);
+
+ return clone;
+ }
+
+ public ParticleEmitter(String name, Type type, int numParticles) {
+ super(name);
+
+ // ignore world transform, unless user sets inLocalSpace
+ this.setIgnoreTransform(true);
+
+ // particles neither receive nor cast shadows
+ this.setShadowMode(ShadowMode.Off);
+
+ // particles are usually transparent
+ this.setQueueBucket(Bucket.Transparent);
+
+ meshType = type;
+
+ // Must create clone of shape/influencer so that a reference to a static is
+ // not maintained
+ shape = shape.deepClone();
+ particleInfluencer = particleInfluencer.clone();
+
+ control = new ParticleEmitterControl(this);
+ controls.add(control);
+
+ switch (meshType) {
+ case Point:
+ particleMesh = new ParticlePointMesh();
+ this.setMesh(particleMesh);
+ break;
+ case Triangle:
+ particleMesh = new ParticleTriMesh();
+ this.setMesh(particleMesh);
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized particle type: " + meshType);
+ }
+ this.setNumParticles(numParticles);
+// particleMesh.initParticleData(this, particles.length);
+ }
+
+ /**
+ * For serialization only. Do not use.
+ */
+ public ParticleEmitter() {
+ super();
+ }
+
+ public void setShape(EmitterShape shape) {
+ this.shape = shape;
+ }
+
+ public EmitterShape getShape() {
+ return shape;
+ }
+
+ /**
+ * Set the {@link ParticleInfluencer} to influence this particle emitter.
+ *
+ * @param particleInfluencer the {@link ParticleInfluencer} to influence
+ * this particle emitter.
+ *
+ * @see ParticleInfluencer
+ */
+ public void setParticleInfluencer(ParticleInfluencer particleInfluencer) {
+ this.particleInfluencer = particleInfluencer;
+ }
+
+ /**
+ * Returns the {@link ParticleInfluencer} that influences this
+ * particle emitter.
+ *
+ * @return the {@link ParticleInfluencer} that influences this
+ * particle emitter.
+ *
+ * @see ParticleInfluencer
+ */
+ public ParticleInfluencer getParticleInfluencer() {
+ return particleInfluencer;
+ }
+
+ /**
+ * Returns the mesh type used by the particle emitter.
+ *
+ *
+ * @return the mesh type used by the particle emitter.
+ *
+ * @see #setMeshType(com.jme3.effect.ParticleMesh.Type)
+ * @see ParticleEmitter#ParticleEmitter(java.lang.String, com.jme3.effect.ParticleMesh.Type, int)
+ */
+ public ParticleMesh.Type getMeshType() {
+ return meshType;
+ }
+
+ /**
+ * Sets the type of mesh used by the particle emitter.
+ * @param meshType The mesh type to use
+ */
+ public void setMeshType(ParticleMesh.Type meshType) {
+ this.meshType = meshType;
+ switch (meshType) {
+ case Point:
+ particleMesh = new ParticlePointMesh();
+ this.setMesh(particleMesh);
+ break;
+ case Triangle:
+ particleMesh = new ParticleTriMesh();
+ this.setMesh(particleMesh);
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized particle type: " + meshType);
+ }
+ this.setNumParticles(particles.length);
+ }
+
+ /**
+ * Returns true if particles should spawn in world space.
+ *
+ * @return true if particles should spawn in world space.
+ *
+ * @see ParticleEmitter#setInWorldSpace(boolean)
+ */
+ public boolean isInWorldSpace() {
+ return worldSpace;
+ }
+
+ /**
+ * Set to true if particles should spawn in world space.
+ *
+ * <p>If set to true and the particle emitter is moved in the scene,
+ * then particles that have already spawned won't be effected by this
+ * motion. If set to false, the particles will emit in local space
+ * and when the emitter is moved, so are all the particles that
+ * were emitted previously.
+ *
+ * @param worldSpace true if particles should spawn in world space.
+ */
+ public void setInWorldSpace(boolean worldSpace) {
+ this.setIgnoreTransform(worldSpace);
+ this.worldSpace = worldSpace;
+ }
+
+ /**
+ * Returns the number of visible particles (spawned but not dead).
+ *
+ * @return the number of visible particles
+ */
+ public int getNumVisibleParticles() {
+// return unusedIndices.size() + next;
+ return lastUsed + 1;
+ }
+
+ /**
+ * Set the maximum amount of particles that
+ * can exist at the same time with this emitter.
+ * Calling this method many times is not recommended.
+ *
+ * @param numParticles the maximum amount of particles that
+ * can exist at the same time with this emitter.
+ */
+ public final void setNumParticles(int numParticles) {
+ particles = new Particle[numParticles];
+ for (int i = 0; i < numParticles; i++) {
+ particles[i] = new Particle();
+ }
+ //We have to reinit the mesh's buffers with the new size
+ particleMesh.initParticleData(this, particles.length);
+ particleMesh.setImagesXY(this.imagesX, this.imagesY);
+ firstUnUsed = 0;
+ lastUsed = -1;
+ }
+
+ public int getMaxNumParticles() {
+ return particles.length;
+ }
+
+ /**
+ * Returns a list of all particles (shouldn't be used in most cases).
+ *
+ * <p>
+ * This includes both existing and non-existing particles.
+ * The size of the array is set to the <code>numParticles</code> value
+ * specified in the constructor or {@link ParticleEmitter#setNumParticles(int) }
+ * method.
+ *
+ * @return a list of all particles.
+ */
+ public Particle[] getParticles() {
+ return particles;
+ }
+
+ /**
+ * Get the normal which particles are facing.
+ *
+ * @return the normal which particles are facing.
+ *
+ * @see ParticleEmitter#setFaceNormal(com.jme3.math.Vector3f)
+ */
+ public Vector3f getFaceNormal() {
+ if (Vector3f.isValidVector(faceNormal)) {
+ return faceNormal;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the normal which particles are facing.
+ *
+ * <p>By default, particles
+ * will face the camera, but for some effects (e.g shockwave) it may
+ * be necessary to face a specific direction instead. To restore
+ * normal functionality, provide <code>null</code> as the argument for
+ * <code>faceNormal</code>.
+ *
+ * @param faceNormal The normals particles should face, or <code>null</code>
+ * if particles should face the camera.
+ */
+ public void setFaceNormal(Vector3f faceNormal) {
+ if (faceNormal == null || !Vector3f.isValidVector(faceNormal)) {
+ this.faceNormal.set(Vector3f.NAN);
+ } else {
+ this.faceNormal = faceNormal;
+ }
+ }
+
+ /**
+ * Returns the rotation speed in radians/sec for particles.
+ *
+ * @return the rotation speed in radians/sec for particles.
+ *
+ * @see ParticleEmitter#setRotateSpeed(float)
+ */
+ public float getRotateSpeed() {
+ return rotateSpeed;
+ }
+
+ /**
+ * Set the rotation speed in radians/sec for particles
+ * spawned after the invocation of this method.
+ *
+ * @param rotateSpeed the rotation speed in radians/sec for particles
+ * spawned after the invocation of this method.
+ */
+ public void setRotateSpeed(float rotateSpeed) {
+ this.rotateSpeed = rotateSpeed;
+ }
+
+ /**
+ * Returns true if every particle spawned
+ * should have a random facing angle.
+ *
+ * @return true if every particle spawned
+ * should have a random facing angle.
+ *
+ * @see ParticleEmitter#setRandomAngle(boolean)
+ */
+ public boolean isRandomAngle() {
+ return randomAngle;
+ }
+
+ /**
+ * Set to true if every particle spawned
+ * should have a random facing angle.
+ *
+ * @param randomAngle if every particle spawned
+ * should have a random facing angle.
+ */
+ public void setRandomAngle(boolean randomAngle) {
+ this.randomAngle = randomAngle;
+ }
+
+ /**
+ * Returns true if every particle spawned should get a random
+ * image.
+ *
+ * @return True if every particle spawned should get a random
+ * image.
+ *
+ * @see ParticleEmitter#setSelectRandomImage(boolean)
+ */
+ public boolean isSelectRandomImage() {
+ return selectRandomImage;
+ }
+
+ /**
+ * Set to true if every particle spawned
+ * should get a random image from a pool of images constructed from
+ * the texture, with X by Y possible images.
+ *
+ * <p>By default, X and Y are equal
+ * to 1, thus allowing only 1 possible image to be selected, but if the
+ * particle is configured with multiple images by using {@link ParticleEmitter#setImagesX(int) }
+ * and {#link ParticleEmitter#setImagesY(int) } methods, then multiple images
+ * can be selected. Setting to false will cause each particle to have an animation
+ * of images displayed, starting at image 1, and going until image X*Y when
+ * the particle reaches its end of life.
+ *
+ * @param selectRandomImage True if every particle spawned should get a random
+ * image.
+ */
+ public void setSelectRandomImage(boolean selectRandomImage) {
+ this.selectRandomImage = selectRandomImage;
+ }
+
+ /**
+ * Check if particles spawned should face their velocity.
+ *
+ * @return True if particles spawned should face their velocity.
+ *
+ * @see ParticleEmitter#setFacingVelocity(boolean)
+ */
+ public boolean isFacingVelocity() {
+ return facingVelocity;
+ }
+
+ /**
+ * Set to true if particles spawned should face
+ * their velocity (or direction to which they are moving towards).
+ *
+ * <p>This is typically used for e.g spark effects.
+ *
+ * @param followVelocity True if particles spawned should face their velocity.
+ *
+ */
+ public void setFacingVelocity(boolean followVelocity) {
+ this.facingVelocity = followVelocity;
+ }
+
+ /**
+ * Get the end color of the particles spawned.
+ *
+ * @return the end color of the particles spawned.
+ *
+ * @see ParticleEmitter#setEndColor(com.jme3.math.ColorRGBA)
+ */
+ public ColorRGBA getEndColor() {
+ return endColor;
+ }
+
+ /**
+ * Set the end color of the particles spawned.
+ *
+ * <p>The
+ * particle color at any time is determined by blending the start color
+ * and end color based on the particle's current time of life relative
+ * to its end of life.
+ *
+ * @param endColor the end color of the particles spawned.
+ */
+ public void setEndColor(ColorRGBA endColor) {
+ this.endColor.set(endColor);
+ }
+
+ /**
+ * Get the end size of the particles spawned.
+ *
+ * @return the end size of the particles spawned.
+ *
+ * @see ParticleEmitter#setEndSize(float)
+ */
+ public float getEndSize() {
+ return endSize;
+ }
+
+ /**
+ * Set the end size of the particles spawned.
+ *
+ * <p>The
+ * particle size at any time is determined by blending the start size
+ * and end size based on the particle's current time of life relative
+ * to its end of life.
+ *
+ * @param endSize the end size of the particles spawned.
+ */
+ public void setEndSize(float endSize) {
+ this.endSize = endSize;
+ }
+
+ /**
+ * Get the gravity vector.
+ *
+ * @return the gravity vector.
+ *
+ * @see ParticleEmitter#setGravity(com.jme3.math.Vector3f)
+ */
+ public Vector3f getGravity() {
+ return gravity;
+ }
+
+ /**
+ * This method sets the gravity vector.
+ *
+ * @param gravity the gravity vector
+ */
+ public void setGravity(Vector3f gravity) {
+ this.gravity.set(gravity);
+ }
+
+ /**
+ * Sets the gravity vector.
+ *
+ * @param x the x component of the gravity vector
+ * @param y the y component of the gravity vector
+ * @param z the z component of the gravity vector
+ */
+ public void setGravity(float x, float y, float z) {
+ this.gravity.x = x;
+ this.gravity.y = y;
+ this.gravity.z = z;
+ }
+
+ /**
+ * Get the high value of life.
+ *
+ * @return the high value of life.
+ *
+ * @see ParticleEmitter#setHighLife(float)
+ */
+ public float getHighLife() {
+ return highLife;
+ }
+
+ /**
+ * Set the high value of life.
+ *
+ * <p>The particle's lifetime/expiration
+ * is determined by randomly selecting a time between low life and high life.
+ *
+ * @param highLife the high value of life.
+ */
+ public void setHighLife(float highLife) {
+ this.highLife = highLife;
+ }
+
+ /**
+ * Get the number of images along the X axis (width).
+ *
+ * @return the number of images along the X axis (width).
+ *
+ * @see ParticleEmitter#setImagesX(int)
+ */
+ public int getImagesX() {
+ return imagesX;
+ }
+
+ /**
+ * Set the number of images along the X axis (width).
+ *
+ * <p>To determine
+ * how multiple particle images are selected and used, see the
+ * {@link ParticleEmitter#setSelectRandomImage(boolean) } method.
+ *
+ * @param imagesX the number of images along the X axis (width).
+ */
+ public void setImagesX(int imagesX) {
+ this.imagesX = imagesX;
+ particleMesh.setImagesXY(this.imagesX, this.imagesY);
+ }
+
+ /**
+ * Get the number of images along the Y axis (height).
+ *
+ * @return the number of images along the Y axis (height).
+ *
+ * @see ParticleEmitter#setImagesY(int)
+ */
+ public int getImagesY() {
+ return imagesY;
+ }
+
+ /**
+ * Set the number of images along the Y axis (height).
+ *
+ * <p>To determine how multiple particle images are selected and used, see the
+ * {@link ParticleEmitter#setSelectRandomImage(boolean) } method.
+ *
+ * @param imagesY the number of images along the Y axis (height).
+ */
+ public void setImagesY(int imagesY) {
+ this.imagesY = imagesY;
+ particleMesh.setImagesXY(this.imagesX, this.imagesY);
+ }
+
+ /**
+ * Get the low value of life.
+ *
+ * @return the low value of life.
+ *
+ * @see ParticleEmitter#setLowLife(float)
+ */
+ public float getLowLife() {
+ return lowLife;
+ }
+
+ /**
+ * Set the low value of life.
+ *
+ * <p>The particle's lifetime/expiration
+ * is determined by randomly selecting a time between low life and high life.
+ *
+ * @param lowLife the low value of life.
+ */
+ public void setLowLife(float lowLife) {
+ this.lowLife = lowLife;
+ }
+
+ /**
+ * Get the number of particles to spawn per
+ * second.
+ *
+ * @return the number of particles to spawn per
+ * second.
+ *
+ * @see ParticleEmitter#setParticlesPerSec(float)
+ */
+ public float getParticlesPerSec() {
+ return particlesPerSec;
+ }
+
+ /**
+ * Set the number of particles to spawn per
+ * second.
+ *
+ * @param particlesPerSec the number of particles to spawn per
+ * second.
+ */
+ public void setParticlesPerSec(float particlesPerSec) {
+ this.particlesPerSec = particlesPerSec;
+ }
+
+ /**
+ * Get the start color of the particles spawned.
+ *
+ * @return the start color of the particles spawned.
+ *
+ * @see ParticleEmitter#setStartColor(com.jme3.math.ColorRGBA)
+ */
+ public ColorRGBA getStartColor() {
+ return startColor;
+ }
+
+ /**
+ * Set the start color of the particles spawned.
+ *
+ * <p>The particle color at any time is determined by blending the start color
+ * and end color based on the particle's current time of life relative
+ * to its end of life.
+ *
+ * @param startColor the start color of the particles spawned
+ */
+ public void setStartColor(ColorRGBA startColor) {
+ this.startColor.set(startColor);
+ }
+
+ /**
+ * Get the start color of the particles spawned.
+ *
+ * @return the start color of the particles spawned.
+ *
+ * @see ParticleEmitter#setStartSize(float)
+ */
+ public float getStartSize() {
+ return startSize;
+ }
+
+ /**
+ * Set the start size of the particles spawned.
+ *
+ * <p>The particle size at any time is determined by blending the start size
+ * and end size based on the particle's current time of life relative
+ * to its end of life.
+ *
+ * @param startSize the start size of the particles spawned.
+ */
+ public void setStartSize(float startSize) {
+ this.startSize = startSize;
+ }
+
+ /**
+ * @deprecated Use ParticleEmitter.getParticleInfluencer().getInitialVelocity() instead.
+ */
+ @Deprecated
+ public Vector3f getInitialVelocity() {
+ return particleInfluencer.getInitialVelocity();
+ }
+
+ /**
+ * @param initialVelocity Set the initial velocity a particle is spawned with,
+ * the initial velocity given in the parameter will be varied according
+ * to the velocity variation set in {@link ParticleEmitter#setVelocityVariation(float) }.
+ * A particle will move toward its velocity unless it is effected by the
+ * gravity.
+ *
+ * @deprecated
+ * This method is deprecated.
+ * Use ParticleEmitter.getParticleInfluencer().setInitialVelocity(initialVelocity); instead.
+ *
+ * @see ParticleEmitter#setVelocityVariation(float)
+ * @see ParticleEmitter#setGravity(float)
+ */
+ @Deprecated
+ public void setInitialVelocity(Vector3f initialVelocity) {
+ this.particleInfluencer.setInitialVelocity(initialVelocity);
+ }
+
+ /**
+ * @deprecated
+ * This method is deprecated.
+ * Use ParticleEmitter.getParticleInfluencer().getVelocityVariation(); instead.
+ * @return the initial velocity variation factor
+ */
+ @Deprecated
+ public float getVelocityVariation() {
+ return particleInfluencer.getVelocityVariation();
+ }
+
+ /**
+ * @param variation Set the variation by which the initial velocity
+ * of the particle is determined. <code>variation</code> should be a value
+ * from 0 to 1, where 0 means particles are to spawn with exactly
+ * the velocity given in {@link ParticleEmitter#setStartVel(com.jme3.math.Vector3f) },
+ * and 1 means particles are to spawn with a completely random velocity.
+ *
+ * @deprecated
+ * This method is deprecated.
+ * Use ParticleEmitter.getParticleInfluencer().setVelocityVariation(variation); instead.
+ */
+ @Deprecated
+ public void setVelocityVariation(float variation) {
+ this.particleInfluencer.setVelocityVariation(variation);
+ }
+
+ private Particle emitParticle(Vector3f min, Vector3f max) {
+ int idx = lastUsed + 1;
+ if (idx >= particles.length) {
+ return null;
+ }
+
+ Particle p = particles[idx];
+ if (selectRandomImage) {
+ p.imageIndex = FastMath.nextRandomInt(0, imagesY - 1) * imagesX + FastMath.nextRandomInt(0, imagesX - 1);
+ }
+
+ p.startlife = lowLife + FastMath.nextRandomFloat() * (highLife - lowLife);
+ p.life = p.startlife;
+ p.color.set(startColor);
+ p.size = startSize;
+ //shape.getRandomPoint(p.position);
+ particleInfluencer.influenceParticle(p, shape);
+ if (worldSpace) {
+ worldTransform.transformVector(p.position, p.position);
+ worldTransform.getRotation().mult(p.velocity, p.velocity);
+ // TODO: Make scale relevant somehow??
+ }
+ if (randomAngle) {
+ p.angle = FastMath.nextRandomFloat() * FastMath.TWO_PI;
+ }
+ if (rotateSpeed != 0) {
+ p.rotateSpeed = rotateSpeed * (0.2f + (FastMath.nextRandomFloat() * 2f - 1f) * .8f);
+ }
+
+ temp.set(p.position).addLocal(p.size, p.size, p.size);
+ max.maxLocal(temp);
+ temp.set(p.position).subtractLocal(p.size, p.size, p.size);
+ min.minLocal(temp);
+
+ ++lastUsed;
+ firstUnUsed = idx + 1;
+ return p;
+ }
+
+ /**
+ * Instantly emits all the particles possible to be emitted. Any particles
+ * which are currently inactive will be spawned immediately.
+ */
+ public void emitAllParticles() {
+ // Force world transform to update
+ this.getWorldTransform();
+
+ TempVars vars = TempVars.get();
+
+ BoundingBox bbox = (BoundingBox) this.getMesh().getBound();
+
+ Vector3f min = vars.vect1;
+ Vector3f max = vars.vect2;
+
+ bbox.getMin(min);
+ bbox.getMax(max);
+
+ if (!Vector3f.isValidVector(min)) {
+ min.set(Vector3f.POSITIVE_INFINITY);
+ }
+ if (!Vector3f.isValidVector(max)) {
+ max.set(Vector3f.NEGATIVE_INFINITY);
+ }
+
+ while (emitParticle(min, max) != null);
+
+ bbox.setMinMax(min, max);
+ this.setBoundRefresh();
+
+ vars.release();
+ }
+
+ /**
+ * Instantly kills all active particles, after this method is called, all
+ * particles will be dead and no longer visible.
+ */
+ public void killAllParticles() {
+ for (int i = 0; i < particles.length; ++i) {
+ if (particles[i].life > 0) {
+ this.freeParticle(i);
+ }
+ }
+ }
+
+ /**
+ * Kills the particle at the given index.
+ *
+ * @param index The index of the particle to kill
+ * @see #getParticles()
+ */
+ public void killParticle(int index){
+ freeParticle(index);
+ }
+
+ private void freeParticle(int idx) {
+ Particle p = particles[idx];
+ p.life = 0;
+ p.size = 0f;
+ p.color.set(0, 0, 0, 0);
+ p.imageIndex = 0;
+ p.angle = 0;
+ p.rotateSpeed = 0;
+
+ if (idx == lastUsed) {
+ while (lastUsed >= 0 && particles[lastUsed].life == 0) {
+ lastUsed--;
+ }
+ }
+ if (idx < firstUnUsed) {
+ firstUnUsed = idx;
+ }
+ }
+
+ private void swap(int idx1, int idx2) {
+ Particle p1 = particles[idx1];
+ particles[idx1] = particles[idx2];
+ particles[idx2] = p1;
+ }
+
+ private void updateParticle(Particle p, float tpf, Vector3f min, Vector3f max){
+ // applying gravity
+ p.velocity.x -= gravity.x * tpf;
+ p.velocity.y -= gravity.y * tpf;
+ p.velocity.z -= gravity.z * tpf;
+ temp.set(p.velocity).multLocal(tpf);
+ p.position.addLocal(temp);
+
+ // affecting color, size and angle
+ float b = (p.startlife - p.life) / p.startlife;
+ p.color.interpolate(startColor, endColor, b);
+ p.size = FastMath.interpolateLinear(b, startSize, endSize);
+ p.angle += p.rotateSpeed * tpf;
+
+ // Computing bounding volume
+ temp.set(p.position).addLocal(p.size, p.size, p.size);
+ max.maxLocal(temp);
+ temp.set(p.position).subtractLocal(p.size, p.size, p.size);
+ min.minLocal(temp);
+
+ if (!selectRandomImage) {
+ p.imageIndex = (int) (b * imagesX * imagesY);
+ }
+ }
+
+ private void updateParticleState(float tpf) {
+ // Force world transform to update
+ this.getWorldTransform();
+
+ TempVars vars = TempVars.get();
+
+ Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY);
+ Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY);
+
+ for (int i = 0; i < particles.length; ++i) {
+ Particle p = particles[i];
+ if (p.life == 0) { // particle is dead
+// assert i <= firstUnUsed;
+ continue;
+ }
+
+ p.life -= tpf;
+ if (p.life <= 0) {
+ this.freeParticle(i);
+ continue;
+ }
+
+ updateParticle(p, tpf, min, max);
+
+ if (firstUnUsed < i) {
+ this.swap(firstUnUsed, i);
+ if (i == lastUsed) {
+ lastUsed = firstUnUsed;
+ }
+ firstUnUsed++;
+ }
+ }
+
+ // Spawns particles within the tpf timeslot with proper age
+ float interval = 1f / particlesPerSec;
+ tpf += timeDifference;
+ while (tpf > interval){
+ tpf -= interval;
+ Particle p = emitParticle(min, max);
+ if (p != null){
+ p.life -= tpf;
+ if (p.life <= 0){
+ freeParticle(lastUsed);
+ }else{
+ updateParticle(p, tpf, min, max);
+ }
+ }
+ }
+ timeDifference = tpf;
+
+ BoundingBox bbox = (BoundingBox) this.getMesh().getBound();
+ bbox.setMinMax(min, max);
+ this.setBoundRefresh();
+
+ vars.release();
+ }
+
+ /**
+ * Set to enable or disable the particle emitter
+ *
+ * <p>When a particle is
+ * disabled, it will be "frozen in time" and not update.
+ *
+ * @param enabled True to enable the particle emitter
+ */
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Check if a particle emitter is enabled for update.
+ *
+ * @return True if a particle emitter is enabled for update.
+ *
+ * @see ParticleEmitter#setEnabled(boolean)
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Callback from Control.update(), do not use.
+ * @param tpf
+ */
+ public void updateFromControl(float tpf) {
+ if (enabled) {
+ this.updateParticleState(tpf);
+ }
+ }
+
+ /**
+ * Callback from Control.render(), do not use.
+ *
+ * @param rm
+ * @param vp
+ */
+ private void renderFromControl(RenderManager rm, ViewPort vp) {
+ Camera cam = vp.getCamera();
+
+ if (meshType == ParticleMesh.Type.Point) {
+ float C = cam.getProjectionMatrix().m00;
+ C *= cam.getWidth() * 0.5f;
+
+ // send attenuation params
+ this.getMaterial().setFloat("Quadratic", C);
+ }
+
+ Matrix3f inverseRotation = Matrix3f.IDENTITY;
+ TempVars vars = null;
+ if (!worldSpace) {
+ vars = TempVars.get();
+
+ inverseRotation = this.getWorldRotation().toRotationMatrix(vars.tempMat3).invertLocal();
+ }
+ particleMesh.updateParticleData(particles, cam, inverseRotation);
+ if (!worldSpace) {
+ vars.release();
+ }
+ }
+
+ public void preload(RenderManager rm, ViewPort vp) {
+ this.updateParticleState(0);
+ particleMesh.updateParticleData(particles, vp.getCamera(), Matrix3f.IDENTITY);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(shape, "shape", DEFAULT_SHAPE);
+ oc.write(meshType, "meshType", ParticleMesh.Type.Triangle);
+ oc.write(enabled, "enabled", true);
+ oc.write(particles.length, "numParticles", 0);
+ oc.write(particlesPerSec, "particlesPerSec", 0);
+ oc.write(lowLife, "lowLife", 0);
+ oc.write(highLife, "highLife", 0);
+ oc.write(gravity, "gravity", null);
+ oc.write(imagesX, "imagesX", 1);
+ oc.write(imagesY, "imagesY", 1);
+
+ oc.write(startColor, "startColor", null);
+ oc.write(endColor, "endColor", null);
+ oc.write(startSize, "startSize", 0);
+ oc.write(endSize, "endSize", 0);
+ oc.write(worldSpace, "worldSpace", false);
+ oc.write(facingVelocity, "facingVelocity", false);
+ oc.write(faceNormal, "faceNormal", new Vector3f(Vector3f.NAN));
+ oc.write(selectRandomImage, "selectRandomImage", false);
+ oc.write(randomAngle, "randomAngle", false);
+ oc.write(rotateSpeed, "rotateSpeed", 0);
+
+ oc.write(particleInfluencer, "influencer", DEFAULT_INFLUENCER);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ shape = (EmitterShape) ic.readSavable("shape", DEFAULT_SHAPE);
+
+ if (shape == DEFAULT_SHAPE) {
+ // Prevent reference to static
+ shape = shape.deepClone();
+ }
+
+ meshType = ic.readEnum("meshType", ParticleMesh.Type.class, ParticleMesh.Type.Triangle);
+ int numParticles = ic.readInt("numParticles", 0);
+
+
+ enabled = ic.readBoolean("enabled", true);
+ particlesPerSec = ic.readFloat("particlesPerSec", 0);
+ lowLife = ic.readFloat("lowLife", 0);
+ highLife = ic.readFloat("highLife", 0);
+ gravity = (Vector3f) ic.readSavable("gravity", null);
+ imagesX = ic.readInt("imagesX", 1);
+ imagesY = ic.readInt("imagesY", 1);
+
+ startColor = (ColorRGBA) ic.readSavable("startColor", null);
+ endColor = (ColorRGBA) ic.readSavable("endColor", null);
+ startSize = ic.readFloat("startSize", 0);
+ endSize = ic.readFloat("endSize", 0);
+ worldSpace = ic.readBoolean("worldSpace", false);
+ this.setIgnoreTransform(worldSpace);
+ facingVelocity = ic.readBoolean("facingVelocity", false);
+ faceNormal = (Vector3f)ic.readSavable("faceNormal", new Vector3f(Vector3f.NAN));
+ selectRandomImage = ic.readBoolean("selectRandomImage", false);
+ randomAngle = ic.readBoolean("randomAngle", false);
+ rotateSpeed = ic.readFloat("rotateSpeed", 0);
+
+ switch (meshType) {
+ case Point:
+ particleMesh = new ParticlePointMesh();
+ this.setMesh(particleMesh);
+ break;
+ case Triangle:
+ particleMesh = new ParticleTriMesh();
+ this.setMesh(particleMesh);
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized particle type: " + meshType);
+ }
+ this.setNumParticles(numParticles);
+// particleMesh.initParticleData(this, particles.length);
+// particleMesh.setImagesXY(imagesX, imagesY);
+
+ particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER);
+ if (particleInfluencer == DEFAULT_INFLUENCER) {
+ particleInfluencer = particleInfluencer.clone();
+ }
+
+ if (im.getFormatVersion() == 0) {
+ // compatibility before the control inside particle emitter
+ // was changed:
+ // find it in the controls and take it out, then add the proper one in
+ for (int i = 0; i < controls.size(); i++) {
+ Object obj = controls.get(i);
+ if (obj instanceof ParticleEmitter) {
+ controls.remove(i);
+ // now add the proper one in
+ controls.add(new ParticleEmitterControl(this));
+ break;
+ }
+ }
+
+ // compatability before gravity was not a vector but a float
+ if (gravity == null) {
+ gravity = new Vector3f();
+ gravity.y = ic.readFloat("gravity", 0);
+ }
+ } else {
+ // since the parentEmitter is not loaded, it must be
+ // loaded separately
+ control = getControl(ParticleEmitterControl.class);
+ control.parentEmitter = this;
+
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/ParticleMesh.java b/engine/src/core/com/jme3/effect/ParticleMesh.java
new file mode 100644
index 0000000..adace45
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/ParticleMesh.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.effect;
+
+import com.jme3.material.RenderState;
+import com.jme3.math.Matrix3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Mesh;
+
+/**
+ * The <code>ParticleMesh</code> is the underlying visual implementation of a
+ * {@link ParticleEmitter particle emitter}.
+ *
+ * @author Kirill Vainer
+ */
+public abstract class ParticleMesh extends Mesh {
+
+ /**
+ * Type of particle mesh
+ */
+ public enum Type {
+ /**
+ * The particle mesh is composed of points. Each particle is a point.
+ * This can be used in conjuction with {@link RenderState#setPointSprite(boolean) point sprites}
+ * to render particles the usual way.
+ */
+ Point,
+
+ /**
+ * The particle mesh is composed of triangles. Each particle is
+ * two triangles making a single quad.
+ */
+ Triangle;
+ }
+
+ /**
+ * Initialize mesh data.
+ *
+ * @param emitter The emitter which will use this <code>ParticleMesh</code>.
+ * @param numParticles The maxmimum number of particles to simulate
+ */
+ public abstract void initParticleData(ParticleEmitter emitter, int numParticles);
+
+ /**
+ * Set the images on the X and Y coordinates
+ * @param imagesX Images on the X coordinate
+ * @param imagesY Images on the Y coordinate
+ */
+ public abstract void setImagesXY(int imagesX, int imagesY);
+
+ /**
+ * Update the particle visual data. Typically called every frame.
+ */
+ public abstract void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation);
+
+}
diff --git a/engine/src/core/com/jme3/effect/ParticlePointMesh.java b/engine/src/core/com/jme3/effect/ParticlePointMesh.java
new file mode 100644
index 0000000..28db3e6
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/ParticlePointMesh.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.effect;
+
+import com.jme3.math.Matrix3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+public class ParticlePointMesh extends ParticleMesh {
+
+ private ParticleEmitter emitter;
+
+ private int imagesX = 1;
+ private int imagesY = 1;
+
+ @Override
+ public void setImagesXY(int imagesX, int imagesY) {
+ this.imagesX = imagesX;
+ this.imagesY = imagesY;
+ }
+
+ @Override
+ public void initParticleData(ParticleEmitter emitter, int numParticles) {
+ setMode(Mode.Points);
+
+ this.emitter = emitter;
+
+ // set positions
+ FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles);
+ VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
+ pvb.setupData(Usage.Stream, 3, Format.Float, pb);
+
+ //if the buffer is already set only update the data
+ VertexBuffer buf = getBuffer(VertexBuffer.Type.Position);
+ if (buf != null) {
+ buf.updateData(pb);
+ } else {
+ setBuffer(pvb);
+ }
+
+ // set colors
+ ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4);
+ VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color);
+ cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb);
+ cvb.setNormalized(true);
+
+ buf = getBuffer(VertexBuffer.Type.Color);
+ if (buf != null) {
+ buf.updateData(cb);
+ } else {
+ setBuffer(cvb);
+ }
+
+ // set sizes
+ FloatBuffer sb = BufferUtils.createFloatBuffer(numParticles);
+ VertexBuffer svb = new VertexBuffer(VertexBuffer.Type.Size);
+ svb.setupData(Usage.Stream, 1, Format.Float, sb);
+
+ buf = getBuffer(VertexBuffer.Type.Size);
+ if (buf != null) {
+ buf.updateData(sb);
+ } else {
+ setBuffer(svb);
+ }
+
+ // set UV-scale
+ FloatBuffer tb = BufferUtils.createFloatBuffer(numParticles*4);
+ VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord);
+ tvb.setupData(Usage.Stream, 4, Format.Float, tb);
+
+ buf = getBuffer(VertexBuffer.Type.TexCoord);
+ if (buf != null) {
+ buf.updateData(tb);
+ } else {
+ setBuffer(tvb);
+ }
+ }
+
+ @Override
+ public void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation) {
+ VertexBuffer pvb = getBuffer(VertexBuffer.Type.Position);
+ FloatBuffer positions = (FloatBuffer) pvb.getData();
+
+ VertexBuffer cvb = getBuffer(VertexBuffer.Type.Color);
+ ByteBuffer colors = (ByteBuffer) cvb.getData();
+
+ VertexBuffer svb = getBuffer(VertexBuffer.Type.Size);
+ FloatBuffer sizes = (FloatBuffer) svb.getData();
+
+ VertexBuffer tvb = getBuffer(VertexBuffer.Type.TexCoord);
+ FloatBuffer texcoords = (FloatBuffer) tvb.getData();
+
+ float sizeScale = emitter.getWorldScale().x;
+
+ // update data in vertex buffers
+ positions.rewind();
+ colors.rewind();
+ sizes.rewind();
+ texcoords.rewind();
+ for (int i = 0; i < particles.length; i++){
+ Particle p = particles[i];
+
+ positions.put(p.position.x)
+ .put(p.position.y)
+ .put(p.position.z);
+
+ sizes.put(p.size * sizeScale);
+ colors.putInt(p.color.asIntABGR());
+
+ int imgX = p.imageIndex % imagesX;
+ int imgY = (p.imageIndex - imgX) / imagesY;
+
+ float startX = ((float) imgX) / imagesX;
+ float startY = ((float) imgY) / imagesY;
+ float endX = startX + (1f / imagesX);
+ float endY = startY + (1f / imagesY);
+
+ texcoords.put(startX).put(startY).put(endX).put(endY);
+ }
+ positions.flip();
+ colors.flip();
+ sizes.flip();
+ texcoords.flip();
+
+ // force renderer to re-send data to GPU
+ pvb.updateData(positions);
+ cvb.updateData(colors);
+ svb.updateData(sizes);
+ tvb.updateData(texcoords);
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/ParticleTriMesh.java b/engine/src/core/com/jme3/effect/ParticleTriMesh.java
new file mode 100644
index 0000000..8d27838
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/ParticleTriMesh.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.effect;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.SortUtil;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+public class ParticleTriMesh extends ParticleMesh {
+
+ private int imagesX = 1;
+ private int imagesY = 1;
+ private boolean uniqueTexCoords = false;
+ private ParticleComparator comparator = new ParticleComparator();
+ private ParticleEmitter emitter;
+ private Particle[] particlesCopy;
+
+ @Override
+ public void initParticleData(ParticleEmitter emitter, int numParticles) {
+ setMode(Mode.Triangles);
+
+ this.emitter = emitter;
+
+ particlesCopy = new Particle[numParticles];
+
+ // set positions
+ FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles * 4);
+ VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
+ pvb.setupData(Usage.Stream, 3, Format.Float, pb);
+
+ //if the buffer is already set only update the data
+ VertexBuffer buf = getBuffer(VertexBuffer.Type.Position);
+ if (buf != null) {
+ buf.updateData(pb);
+ } else {
+ setBuffer(pvb);
+ }
+
+ // set colors
+ ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4 * 4);
+ VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color);
+ cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb);
+ cvb.setNormalized(true);
+
+ buf = getBuffer(VertexBuffer.Type.Color);
+ if (buf != null) {
+ buf.updateData(cb);
+ } else {
+ setBuffer(cvb);
+ }
+
+ // set texcoords
+ VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord);
+ FloatBuffer tb = BufferUtils.createVector2Buffer(numParticles * 4);
+
+ uniqueTexCoords = false;
+ for (int i = 0; i < numParticles; i++){
+ tb.put(0f).put(1f);
+ tb.put(1f).put(1f);
+ tb.put(0f).put(0f);
+ tb.put(1f).put(0f);
+ }
+ tb.flip();
+ tvb.setupData(Usage.Static, 2, Format.Float, tb);
+
+ buf = getBuffer(VertexBuffer.Type.TexCoord);
+ if (buf != null) {
+ buf.updateData(tb);
+ } else {
+ setBuffer(tvb);
+ }
+
+ // set indices
+ ShortBuffer ib = BufferUtils.createShortBuffer(numParticles * 6);
+ for (int i = 0; i < numParticles; i++){
+ int startIdx = (i * 4);
+
+ // triangle 1
+ ib.put((short)(startIdx + 1))
+ .put((short)(startIdx + 0))
+ .put((short)(startIdx + 2));
+
+ // triangle 2
+ ib.put((short)(startIdx + 1))
+ .put((short)(startIdx + 2))
+ .put((short)(startIdx + 3));
+ }
+ ib.flip();
+
+ VertexBuffer ivb = new VertexBuffer(VertexBuffer.Type.Index);
+ ivb.setupData(Usage.Static, 3, Format.UnsignedShort, ib);
+
+ buf = getBuffer(VertexBuffer.Type.Index);
+ if (buf != null) {
+ buf.updateData(ib);
+ } else {
+ setBuffer(ivb);
+ }
+
+ }
+
+ @Override
+ public void setImagesXY(int imagesX, int imagesY) {
+ this.imagesX = imagesX;
+ this.imagesY = imagesY;
+ if (imagesX != 1 || imagesY != 1){
+ uniqueTexCoords = true;
+ getBuffer(VertexBuffer.Type.TexCoord).setUsage(Usage.Stream);
+ }
+ }
+
+ @Override
+ public void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation) {
+ System.arraycopy(particles, 0, particlesCopy, 0, particlesCopy.length);
+ comparator.setCamera(cam);
+// Arrays.sort(particlesCopy, comparator);
+// SortUtil.qsort(particlesCopy, comparator);
+ SortUtil.msort(particles, particlesCopy, comparator);
+ particles = particlesCopy;
+
+ VertexBuffer pvb = getBuffer(VertexBuffer.Type.Position);
+ FloatBuffer positions = (FloatBuffer) pvb.getData();
+
+ VertexBuffer cvb = getBuffer(VertexBuffer.Type.Color);
+ ByteBuffer colors = (ByteBuffer) cvb.getData();
+
+ VertexBuffer tvb = getBuffer(VertexBuffer.Type.TexCoord);
+ FloatBuffer texcoords = (FloatBuffer) tvb.getData();
+
+ Vector3f camUp = cam.getUp();
+ Vector3f camLeft = cam.getLeft();
+ Vector3f camDir = cam.getDirection();
+
+ inverseRotation.multLocal(camUp);
+ inverseRotation.multLocal(camLeft);
+ inverseRotation.multLocal(camDir);
+
+ boolean facingVelocity = emitter.isFacingVelocity();
+
+ Vector3f up = new Vector3f(),
+ left = new Vector3f();
+
+ if (!facingVelocity){
+ up.set(camUp);
+ left.set(camLeft);
+ }
+
+ // update data in vertex buffers
+ positions.clear();
+ colors.clear();
+ texcoords.clear();
+ Vector3f faceNormal = emitter.getFaceNormal();
+
+ for (int i = 0; i < particles.length; i++){
+ Particle p = particles[i];
+ boolean dead = p.life == 0;
+ if (dead){
+ positions.put(0).put(0).put(0);
+ positions.put(0).put(0).put(0);
+ positions.put(0).put(0).put(0);
+ positions.put(0).put(0).put(0);
+ continue;
+ }
+
+ if (facingVelocity){
+ left.set(p.velocity).normalizeLocal();
+ camDir.cross(left, up);
+ up.multLocal(p.size);
+ left.multLocal(p.size);
+ }else if (faceNormal != null){
+ up.set(faceNormal).crossLocal(Vector3f.UNIT_X);
+ faceNormal.cross(up, left);
+ up.multLocal(p.size);
+ left.multLocal(p.size);
+ }else if (p.angle != 0){
+ float cos = FastMath.cos(p.angle) * p.size;
+ float sin = FastMath.sin(p.angle) * p.size;
+
+ left.x = camLeft.x * cos + camUp.x * sin;
+ left.y = camLeft.y * cos + camUp.y * sin;
+ left.z = camLeft.z * cos + camUp.z * sin;
+
+ up.x = camLeft.x * -sin + camUp.x * cos;
+ up.y = camLeft.y * -sin + camUp.y * cos;
+ up.z = camLeft.z * -sin + camUp.z * cos;
+ }else{
+ up.set(camUp);
+ left.set(camLeft);
+ up.multLocal(p.size);
+ left.multLocal(p.size);
+ }
+
+ positions.put(p.position.x + left.x + up.x)
+ .put(p.position.y + left.y + up.y)
+ .put(p.position.z + left.z + up.z);
+
+ positions.put(p.position.x - left.x + up.x)
+ .put(p.position.y - left.y + up.y)
+ .put(p.position.z - left.z + up.z);
+
+ positions.put(p.position.x + left.x - up.x)
+ .put(p.position.y + left.y - up.y)
+ .put(p.position.z + left.z - up.z);
+
+ positions.put(p.position.x - left.x - up.x)
+ .put(p.position.y - left.y - up.y)
+ .put(p.position.z - left.z - up.z);
+
+ if (uniqueTexCoords){
+ int imgX = p.imageIndex % imagesX;
+ int imgY = (p.imageIndex - imgX) / imagesY;
+
+ float startX = ((float) imgX) / imagesX;
+ float startY = ((float) imgY) / imagesY;
+ float endX = startX + (1f / imagesX);
+ float endY = startY + (1f / imagesY);
+
+ texcoords.put(startX).put(endY);
+ texcoords.put(endX).put(endY);
+ texcoords.put(startX).put(startY);
+ texcoords.put(endX).put(startY);
+ }
+
+ int abgr = p.color.asIntABGR();
+ colors.putInt(abgr);
+ colors.putInt(abgr);
+ colors.putInt(abgr);
+ colors.putInt(abgr);
+ }
+
+ positions.clear();
+ colors.clear();
+ if (!uniqueTexCoords)
+ texcoords.clear();
+ else{
+ texcoords.clear();
+ tvb.updateData(texcoords);
+ }
+
+ // force renderer to re-send data to GPU
+ pvb.updateData(positions);
+ cvb.updateData(colors);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java b/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java
new file mode 100644
index 0000000..80f52d9
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java
@@ -0,0 +1,92 @@
+package com.jme3.effect.influencers;
+
+import com.jme3.effect.Particle;
+import com.jme3.effect.shapes.EmitterShape;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * This emitter influences the particles so that they move all in the same direction.
+ * The direction may vary a little if the velocity variation is non zero.
+ * This influencer is default for the particle emitter.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class DefaultParticleInfluencer implements ParticleInfluencer {
+
+ /** Temporary variable used to help with calculations. */
+ protected transient Vector3f temp = new Vector3f();
+ /** The initial velocity of the particles. */
+ protected Vector3f startVelocity = new Vector3f();
+ /** The velocity's variation of the particles. */
+ protected float velocityVariation = 0.2f;
+
+ @Override
+ public void influenceParticle(Particle particle, EmitterShape emitterShape) {
+ emitterShape.getRandomPoint(particle.position);
+ this.applyVelocityVariation(particle);
+ }
+
+ /**
+ * This method applies the variation to the particle with already set velocity.
+ * @param particle
+ * the particle to be affected
+ */
+ protected void applyVelocityVariation(Particle particle) {
+ particle.velocity.set(startVelocity);
+ temp.set(FastMath.nextRandomFloat(), FastMath.nextRandomFloat(), FastMath.nextRandomFloat());
+ temp.multLocal(2f);
+ temp.subtractLocal(1f, 1f, 1f);
+ temp.multLocal(startVelocity.length());
+ particle.velocity.interpolate(temp, velocityVariation);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(startVelocity, "startVelocity", Vector3f.ZERO);
+ oc.write(velocityVariation, "variation", 0.2f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ startVelocity = (Vector3f) ic.readSavable("startVelocity", Vector3f.ZERO.clone());
+ velocityVariation = ic.readFloat("variation", 0.2f);
+ }
+
+ @Override
+ public ParticleInfluencer clone() {
+ try {
+ DefaultParticleInfluencer clone = (DefaultParticleInfluencer) super.clone();
+ clone.startVelocity = startVelocity.clone();
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void setInitialVelocity(Vector3f initialVelocity) {
+ this.startVelocity.set(initialVelocity);
+ }
+
+ @Override
+ public Vector3f getInitialVelocity() {
+ return startVelocity;
+ }
+
+ @Override
+ public void setVelocityVariation(float variation) {
+ this.velocityVariation = variation;
+ }
+
+ @Override
+ public float getVelocityVariation() {
+ return velocityVariation;
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/influencers/EmptyParticleInfluencer.java b/engine/src/core/com/jme3/effect/influencers/EmptyParticleInfluencer.java
new file mode 100644
index 0000000..013a5db
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/influencers/EmptyParticleInfluencer.java
@@ -0,0 +1,55 @@
+package com.jme3.effect.influencers;
+
+import com.jme3.effect.Particle;
+import com.jme3.effect.shapes.EmitterShape;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * This influencer does not influence particle at all.
+ * It makes particles not to move.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class EmptyParticleInfluencer implements ParticleInfluencer {
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ }
+
+ @Override
+ public void influenceParticle(Particle particle, EmitterShape emitterShape) {
+ }
+
+ @Override
+ public void setInitialVelocity(Vector3f initialVelocity) {
+ }
+
+ @Override
+ public Vector3f getInitialVelocity() {
+ return null;
+ }
+
+ @Override
+ public void setVelocityVariation(float variation) {
+ }
+
+ @Override
+ public float getVelocityVariation() {
+ return 0;
+ }
+
+ @Override
+ public ParticleInfluencer clone() {
+ try {
+ return (ParticleInfluencer) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/influencers/NewtonianParticleInfluencer.java b/engine/src/core/com/jme3/effect/influencers/NewtonianParticleInfluencer.java
new file mode 100644
index 0000000..a2701be
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/influencers/NewtonianParticleInfluencer.java
@@ -0,0 +1,142 @@
+package com.jme3.effect.influencers;
+
+import com.jme3.effect.Particle;
+import com.jme3.effect.shapes.EmitterShape;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import java.io.IOException;
+
+/**
+ * This influencer calculates initial velocity with the use of the emitter's shape.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class NewtonianParticleInfluencer extends DefaultParticleInfluencer {
+
+ /** Normal to emitter's shape factor. */
+ protected float normalVelocity;
+ /** Emitter's surface tangent factor. */
+ protected float surfaceTangentFactor;
+ /** Emitters tangent rotation factor. */
+ protected float surfaceTangentRotation;
+
+ /**
+ * Constructor. Sets velocity variation to 0.0f.
+ */
+ public NewtonianParticleInfluencer() {
+ this.velocityVariation = 0.0f;
+ }
+
+ @Override
+ public void influenceParticle(Particle particle, EmitterShape emitterShape) {
+ emitterShape.getRandomPointAndNormal(particle.position, particle.velocity);
+ // influencing the particle's velocity
+ if (surfaceTangentFactor == 0.0f) {
+ particle.velocity.multLocal(normalVelocity);
+ } else {
+ // calculating surface tangent (velocity contains the 'normal' value)
+ temp.set(particle.velocity.z * surfaceTangentFactor, particle.velocity.y * surfaceTangentFactor, -particle.velocity.x * surfaceTangentFactor);
+ if (surfaceTangentRotation != 0.0f) {// rotating the tangent
+ Matrix3f m = new Matrix3f();
+ m.fromAngleNormalAxis(FastMath.PI * surfaceTangentRotation, particle.velocity);
+ temp = m.multLocal(temp);
+ }
+ // applying normal factor (this must be done first)
+ particle.velocity.multLocal(normalVelocity);
+ // adding tangent vector
+ particle.velocity.addLocal(temp);
+ }
+ if (velocityVariation != 0.0f) {
+ this.applyVelocityVariation(particle);
+ }
+ }
+
+ /**
+ * This method returns the normal velocity factor.
+ * @return the normal velocity factor
+ */
+ public float getNormalVelocity() {
+ return normalVelocity;
+ }
+
+ /**
+ * This method sets the normal velocity factor.
+ * @param normalVelocity
+ * the normal velocity factor
+ */
+ public void setNormalVelocity(float normalVelocity) {
+ this.normalVelocity = normalVelocity;
+ }
+
+ /**
+ * This method sets the surface tangent factor.
+ * @param surfaceTangentFactor
+ * the surface tangent factor
+ */
+ public void setSurfaceTangentFactor(float surfaceTangentFactor) {
+ this.surfaceTangentFactor = surfaceTangentFactor;
+ }
+
+ /**
+ * This method returns the surface tangent factor.
+ * @return the surface tangent factor
+ */
+ public float getSurfaceTangentFactor() {
+ return surfaceTangentFactor;
+ }
+
+ /**
+ * This method sets the surface tangent rotation factor.
+ * @param surfaceTangentRotation
+ * the surface tangent rotation factor
+ */
+ public void setSurfaceTangentRotation(float surfaceTangentRotation) {
+ this.surfaceTangentRotation = surfaceTangentRotation;
+ }
+
+ /**
+ * This method returns the surface tangent rotation factor.
+ * @return the surface tangent rotation factor
+ */
+ public float getSurfaceTangentRotation() {
+ return surfaceTangentRotation;
+ }
+
+ @Override
+ protected void applyVelocityVariation(Particle particle) {
+ temp.set(FastMath.nextRandomFloat() * velocityVariation, FastMath.nextRandomFloat() * velocityVariation, FastMath.nextRandomFloat() * velocityVariation);
+ particle.velocity.addLocal(temp);
+ }
+
+ @Override
+ public ParticleInfluencer clone() {
+ NewtonianParticleInfluencer result = new NewtonianParticleInfluencer();
+ result.normalVelocity = normalVelocity;
+ result.startVelocity = startVelocity;
+ result.velocityVariation = velocityVariation;
+ result.surfaceTangentFactor = surfaceTangentFactor;
+ result.surfaceTangentRotation = surfaceTangentRotation;
+ return result;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(normalVelocity, "normalVelocity", 0.0f);
+ oc.write(surfaceTangentFactor, "surfaceTangentFactor", 0.0f);
+ oc.write(surfaceTangentRotation, "surfaceTangentRotation", 0.0f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ normalVelocity = ic.readFloat("normalVelocity", 0.0f);
+ surfaceTangentFactor = ic.readFloat("surfaceTangentFactor", 0.0f);
+ surfaceTangentRotation = ic.readFloat("surfaceTangentRotation", 0.0f);
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/influencers/ParticleInfluencer.java b/engine/src/core/com/jme3/effect/influencers/ParticleInfluencer.java
new file mode 100644
index 0000000..56c8cf9
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/influencers/ParticleInfluencer.java
@@ -0,0 +1,61 @@
+package com.jme3.effect.influencers;
+
+import com.jme3.effect.Particle;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.shapes.EmitterShape;
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+
+/**
+ * An interface that defines the methods to affect initial velocity of the particles.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public interface ParticleInfluencer extends Savable, Cloneable {
+
+ /**
+ * This method influences the particle.
+ * @param particle
+ * particle to be influenced
+ * @param emitterShape
+ * the shape of it emitter
+ */
+ void influenceParticle(Particle particle, EmitterShape emitterShape);
+
+ /**
+ * This method clones the influencer instance.
+ * @return cloned instance
+ */
+ public ParticleInfluencer clone();
+
+ /**
+ * @param initialVelocity
+ * Set the initial velocity a particle is spawned with,
+ * the initial velocity given in the parameter will be varied according
+ * to the velocity variation set in {@link ParticleEmitter#setVelocityVariation(float) }.
+ * A particle will move toward its velocity unless it is effected by the
+ * gravity.
+ */
+ void setInitialVelocity(Vector3f initialVelocity);
+
+ /**
+ * This method returns the initial velocity.
+ * @return the initial velocity
+ */
+ Vector3f getInitialVelocity();
+
+ /**
+ * @param variation
+ * Set the variation by which the initial velocity
+ * of the particle is determined. <code>variation</code> should be a value
+ * from 0 to 1, where 0 means particles are to spawn with exactly
+ * the velocity given in {@link ParticleEmitter#setStartVel(com.jme3.math.Vector3f) },
+ * and 1 means particles are to spawn with a completely random velocity.
+ */
+ void setVelocityVariation(float variation);
+
+ /**
+ * This method returns the velocity variation.
+ * @return the velocity variation
+ */
+ float getVelocityVariation();
+}
diff --git a/engine/src/core/com/jme3/effect/package.html b/engine/src/core/com/jme3/effect/package.html
new file mode 100644
index 0000000..dd16da7
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/package.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.effect</code> package allows particle emitter effects to be
+used with a jME3 application. <br/>
+<p>
+ The <code>ParticleEmitter</code> class is the primary class used to create
+ particle emitter effects. See the <code>jme3test.effect</code> package
+ for examples on how to use <code>ParticleEmitter</code>s.
+
+</body>
+</html>
+
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterBoxShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterBoxShape.java
new file mode 100644
index 0000000..9838dd2
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterBoxShape.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.effect.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+public class EmitterBoxShape implements EmitterShape {
+
+ private Vector3f min, len;
+
+ public EmitterBoxShape() {
+ }
+
+ public EmitterBoxShape(Vector3f min, Vector3f max) {
+ if (min == null || max == null) {
+ throw new NullPointerException();
+ }
+
+ this.min = min;
+ this.len = new Vector3f();
+ this.len.set(max).subtractLocal(min);
+ }
+
+ @Override
+ public void getRandomPoint(Vector3f store) {
+ store.x = min.x + len.x * FastMath.nextRandomFloat();
+ store.y = min.y + len.y * FastMath.nextRandomFloat();
+ store.z = min.z + len.z * FastMath.nextRandomFloat();
+ }
+
+ /**
+ * This method fills the point with data.
+ * It does not fill the normal.
+ * @param store the variable to store the point data
+ * @param normal not used in this class
+ */
+ @Override
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
+ this.getRandomPoint(store);
+ }
+
+ @Override
+ public EmitterShape deepClone() {
+ try {
+ EmitterBoxShape clone = (EmitterBoxShape) super.clone();
+ clone.min = min.clone();
+ clone.len = len.clone();
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ public Vector3f getMin() {
+ return min;
+ }
+
+ public void setMin(Vector3f min) {
+ this.min = min;
+ }
+
+ public Vector3f getLen() {
+ return len;
+ }
+
+ public void setLen(Vector3f len) {
+ this.len = len;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(min, "min", null);
+ oc.write(len, "length", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ min = (Vector3f) ic.readSavable("min", null);
+ len = (Vector3f) ic.readSavable("length", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java
new file mode 100644
index 0000000..1c5d687
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java
@@ -0,0 +1,63 @@
+package com.jme3.effect.shapes;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import java.util.List;
+
+/**
+ * This emiter shape emits the particles from the given shape's interior constrained by its convex hull
+ * (a geometry that tightly wraps the mesh). So in case of multiple meshes some vertices may appear
+ * in a space between them.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class EmitterMeshConvexHullShape extends EmitterMeshFaceShape {
+
+ /**
+ * Empty constructor. Sets nothing.
+ */
+ public EmitterMeshConvexHullShape() {
+ }
+
+ /**
+ * Constructor. It stores a copy of vertex list of all meshes.
+ * @param meshes
+ * a list of meshes that will form the emitter's shape
+ */
+ public EmitterMeshConvexHullShape(List<Mesh> meshes) {
+ super(meshes);
+ }
+
+ /**
+ * This method fills the point with coordinates of randomly selected point inside a convex hull
+ * of randomly selected mesh.
+ * @param store
+ * the variable to store with coordinates of randomly selected selected point inside a convex hull
+ * of randomly selected mesh
+ */
+ @Override
+ public void getRandomPoint(Vector3f store) {
+ super.getRandomPoint(store);
+ // now move the point from the meshe's face towards the center of the mesh
+ // the center is in (0, 0, 0) in the local coordinates
+ store.multLocal(FastMath.nextRandomFloat());
+ }
+
+ /**
+ * This method fills the point with coordinates of randomly selected point inside a convex hull
+ * of randomly selected mesh.
+ * The normal param is not used.
+ * @param store
+ * the variable to store with coordinates of randomly selected selected point inside a convex hull
+ * of randomly selected mesh
+ * @param normal
+ * not used in this class
+ */
+ @Override
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
+ super.getRandomPointAndNormal(store, normal);
+ // now move the point from the meshe's face towards the center of the mesh
+ // the center is in (0, 0, 0) in the local coordinates
+ store.multLocal(FastMath.nextRandomFloat());
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterMeshFaceShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterMeshFaceShape.java
new file mode 100644
index 0000000..023ca5b
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterMeshFaceShape.java
@@ -0,0 +1,97 @@
+package com.jme3.effect.shapes;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This emiter shape emits the particles from the given shape's faces.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class EmitterMeshFaceShape extends EmitterMeshVertexShape {
+
+ /**
+ * Empty constructor. Sets nothing.
+ */
+ public EmitterMeshFaceShape() {
+ }
+
+ /**
+ * Constructor. It stores a copy of vertex list of all meshes.
+ * @param meshes
+ * a list of meshes that will form the emitter's shape
+ */
+ public EmitterMeshFaceShape(List<Mesh> meshes) {
+ super(meshes);
+ }
+
+ @Override
+ public void setMeshes(List<Mesh> meshes) {
+ this.vertices = new ArrayList<List<Vector3f>>(meshes.size());
+ this.normals = new ArrayList<List<Vector3f>>(meshes.size());
+ for (Mesh mesh : meshes) {
+ Vector3f[] vertexTable = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position));
+ int[] indices = new int[3];
+ List<Vector3f> vertices = new ArrayList<Vector3f>(mesh.getTriangleCount() * 3);
+ List<Vector3f> normals = new ArrayList<Vector3f>(mesh.getTriangleCount());
+ for (int i = 0; i < mesh.getTriangleCount(); ++i) {
+ mesh.getTriangle(i, indices);
+ vertices.add(vertexTable[indices[0]]);
+ vertices.add(vertexTable[indices[1]]);
+ vertices.add(vertexTable[indices[2]]);
+ normals.add(FastMath.computeNormal(vertexTable[indices[0]], vertexTable[indices[1]], vertexTable[indices[2]]));
+ }
+ this.vertices.add(vertices);
+ this.normals.add(normals);
+ }
+ }
+
+ /**
+ * This method fills the point with coordinates of randomly selected point on a random face.
+ * @param store
+ * the variable to store with coordinates of randomly selected selected point on a random face
+ */
+ @Override
+ public void getRandomPoint(Vector3f store) {
+ int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
+ // the index of the first vertex of a face (must be dividable by 3)
+ int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1) * 3;
+ // put the point somewhere between the first and the second vertex of a face
+ float moveFactor = FastMath.nextRandomFloat();
+ store.set(Vector3f.ZERO);
+ store.addLocal(vertices.get(meshIndex).get(vertIndex));
+ store.addLocal((vertices.get(meshIndex).get(vertIndex + 1).x - vertices.get(meshIndex).get(vertIndex).x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).y - vertices.get(meshIndex).get(vertIndex).y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).z - vertices.get(meshIndex).get(vertIndex).z) * moveFactor);
+ // move the result towards the last face vertex
+ moveFactor = FastMath.nextRandomFloat();
+ store.addLocal((vertices.get(meshIndex).get(vertIndex + 2).x - store.x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).y - store.y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).z - store.z) * moveFactor);
+ }
+
+ /**
+ * This method fills the point with coordinates of randomly selected point on a random face.
+ * The normal param is filled with selected face's normal.
+ * @param store
+ * the variable to store with coordinates of randomly selected selected point on a random face
+ * @param normal
+ * filled with selected face's normal
+ */
+ @Override
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
+ int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
+ // the index of the first vertex of a face (must be dividable by 3)
+ int faceIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1);
+ int vertIndex = faceIndex * 3;
+ // put the point somewhere between the first and the second vertex of a face
+ float moveFactor = FastMath.nextRandomFloat();
+ store.set(Vector3f.ZERO);
+ store.addLocal(vertices.get(meshIndex).get(vertIndex));
+ store.addLocal((vertices.get(meshIndex).get(vertIndex + 1).x - vertices.get(meshIndex).get(vertIndex).x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).y - vertices.get(meshIndex).get(vertIndex).y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).z - vertices.get(meshIndex).get(vertIndex).z) * moveFactor);
+ // move the result towards the last face vertex
+ moveFactor = FastMath.nextRandomFloat();
+ store.addLocal((vertices.get(meshIndex).get(vertIndex + 2).x - store.x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).y - store.y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).z - store.z) * moveFactor);
+ normal.set(normals.get(meshIndex).get(faceIndex));
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java
new file mode 100644
index 0000000..28ee8b4
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java
@@ -0,0 +1,158 @@
+package com.jme3.effect.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * This emiter shape emits the particles from the given shape's vertices
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class EmitterMeshVertexShape implements EmitterShape {
+
+ protected List<List<Vector3f>> vertices;
+ protected List<List<Vector3f>> normals;
+
+ /**
+ * Empty constructor. Sets nothing.
+ */
+ public EmitterMeshVertexShape() {
+ }
+
+ /**
+ * Constructor. It stores a copy of vertex list of all meshes.
+ * @param meshes
+ * a list of meshes that will form the emitter's shape
+ */
+ public EmitterMeshVertexShape(List<Mesh> meshes) {
+ this.setMeshes(meshes);
+ }
+
+ /**
+ * This method sets the meshes that will form the emiter's shape.
+ * @param meshes
+ * a list of meshes that will form the emitter's shape
+ */
+ public void setMeshes(List<Mesh> meshes) {
+ Map<Vector3f, Vector3f> vertToNormalMap = new HashMap<Vector3f, Vector3f>();
+
+ this.vertices = new ArrayList<List<Vector3f>>(meshes.size());
+ this.normals = new ArrayList<List<Vector3f>>(meshes.size());
+ for (Mesh mesh : meshes) {
+ // fetching the data
+ float[] vertexTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Position));
+ float[] normalTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Normal));
+
+ // unifying normals
+ for (int i = 0; i < vertexTable.length; i += 3) {// the tables should have the same size and be dividable by 3
+ Vector3f vert = new Vector3f(vertexTable[i], vertexTable[i + 1], vertexTable[i + 2]);
+ Vector3f norm = vertToNormalMap.get(vert);
+ if (norm == null) {
+ norm = new Vector3f(normalTable[i], normalTable[i + 1], normalTable[i + 2]);
+ vertToNormalMap.put(vert, norm);
+ } else {
+ norm.addLocal(normalTable[i], normalTable[i + 1], normalTable[i + 2]);
+ }
+ }
+
+ // adding data to vertices and normals
+ List<Vector3f> vertices = new ArrayList<Vector3f>(vertToNormalMap.size());
+ List<Vector3f> normals = new ArrayList<Vector3f>(vertToNormalMap.size());
+ for (Entry<Vector3f, Vector3f> entry : vertToNormalMap.entrySet()) {
+ vertices.add(entry.getKey());
+ normals.add(entry.getValue().normalizeLocal());
+ }
+ this.vertices.add(vertices);
+ this.normals.add(normals);
+ }
+ }
+
+ /**
+ * This method fills the point with coordinates of randomly selected mesh vertex.
+ * @param store
+ * the variable to store with coordinates of randomly selected mesh vertex
+ */
+ @Override
+ public void getRandomPoint(Vector3f store) {
+ int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
+ int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() - 1);
+ store.set(vertices.get(meshIndex).get(vertIndex));
+ }
+
+ /**
+ * This method fills the point with coordinates of randomly selected mesh vertex.
+ * The normal param is filled with selected vertex's normal.
+ * @param store
+ * the variable to store with coordinates of randomly selected mesh vertex
+ * @param normal
+ * filled with selected vertex's normal
+ */
+ @Override
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
+ int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
+ int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() - 1);
+ store.set(vertices.get(meshIndex).get(vertIndex));
+ normal.set(normals.get(meshIndex).get(vertIndex));
+ }
+
+ @Override
+ public EmitterShape deepClone() {
+ try {
+ EmitterMeshVertexShape clone = (EmitterMeshVertexShape) super.clone();
+ if (this.vertices != null) {
+ clone.vertices = new ArrayList<List<Vector3f>>(vertices.size());
+ for (List<Vector3f> list : vertices) {
+ List<Vector3f> vectorList = new ArrayList<Vector3f>(list.size());
+ for (Vector3f vector : list) {
+ vectorList.add(vector.clone());
+ }
+ clone.vertices.add(vectorList);
+ }
+ }
+ if (this.normals != null) {
+ clone.normals = new ArrayList<List<Vector3f>>(normals.size());
+ for (List<Vector3f> list : normals) {
+ List<Vector3f> vectorList = new ArrayList<Vector3f>(list.size());
+ for (Vector3f vector : list) {
+ vectorList.add(vector.clone());
+ }
+ clone.normals.add(vectorList);
+ }
+ }
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.writeSavableArrayList((ArrayList<List<Vector3f>>) vertices, "vertices", null);
+ oc.writeSavableArrayList((ArrayList<List<Vector3f>>) normals, "normals", null);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ this.vertices = ic.readSavableArrayList("vertices", null);
+
+ List<List<Vector3f>> tmpNormals = ic.readSavableArrayList("normals", null);
+ if (tmpNormals != null){
+ this.normals = tmpNormals;
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterPointShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterPointShape.java
new file mode 100644
index 0000000..f8ba70e
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterPointShape.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.effect.shapes;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+public class EmitterPointShape implements EmitterShape {
+
+ private Vector3f point;
+
+ public EmitterPointShape() {
+ }
+
+ public EmitterPointShape(Vector3f point) {
+ this.point = point;
+ }
+
+ @Override
+ public EmitterShape deepClone() {
+ try {
+ EmitterPointShape clone = (EmitterPointShape) super.clone();
+ clone.point = point.clone();
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void getRandomPoint(Vector3f store) {
+ store.set(point);
+ }
+
+ /**
+ * This method fills the point with data.
+ * It does not fill the normal.
+ * @param store the variable to store the point data
+ * @param normal not used in this class
+ */
+ @Override
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
+ store.set(point);
+ }
+
+ public Vector3f getPoint() {
+ return point;
+ }
+
+ public void setPoint(Vector3f point) {
+ this.point = point;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(point, "point", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ this.point = (Vector3f) im.getCapsule(this).readSavable("point", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterShape.java
new file mode 100644
index 0000000..c23c19d
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterShape.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.effect.shapes;
+
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+
+/**
+ * This interface declares methods used by all shapes that represent particle emitters.
+ * @author Kirill
+ */
+public interface EmitterShape extends Savable, Cloneable {
+
+ /**
+ * This method fills in the initial position of the particle.
+ * @param store
+ * store variable for initial position
+ */
+ public void getRandomPoint(Vector3f store);
+
+ /**
+ * This method fills in the initial position of the particle and its normal vector.
+ * @param store
+ * store variable for initial position
+ * @param normal
+ * store variable for initial normal
+ */
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal);
+
+ /**
+ * This method creates a deep clone of the current instance of the emitter shape.
+ * @return deep clone of the current instance of the emitter shape
+ */
+ public EmitterShape deepClone();
+}
diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterSphereShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterSphereShape.java
new file mode 100644
index 0000000..642b279
--- /dev/null
+++ b/engine/src/core/com/jme3/effect/shapes/EmitterSphereShape.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.effect.shapes;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+public class EmitterSphereShape implements EmitterShape {
+
+ private Vector3f center;
+ private float radius;
+
+ public EmitterSphereShape() {
+ }
+
+ public EmitterSphereShape(Vector3f center, float radius) {
+ if (center == null) {
+ throw new NullPointerException();
+ }
+
+ if (radius <= 0) {
+ throw new IllegalArgumentException("Radius must be greater than 0");
+ }
+
+ this.center = center;
+ this.radius = radius;
+ }
+
+ @Override
+ public EmitterShape deepClone() {
+ try {
+ EmitterSphereShape clone = (EmitterSphereShape) super.clone();
+ clone.center = center.clone();
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void getRandomPoint(Vector3f store) {
+ do {
+ store.x = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
+ store.y = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
+ store.z = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
+ } while (store.distance(center) > radius);
+ }
+
+ @Override
+ public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
+ this.getRandomPoint(store);
+ }
+
+ public Vector3f getCenter() {
+ return center;
+ }
+
+ public void setCenter(Vector3f center) {
+ this.center = center;
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void setRadius(float radius) {
+ this.radius = radius;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(center, "center", null);
+ oc.write(radius, "radius", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ center = (Vector3f) ic.readSavable("center", null);
+ radius = ic.readFloat("radius", 0);
+ }
+}
diff --git a/engine/src/core/com/jme3/export/FormatVersion.java b/engine/src/core/com/jme3/export/FormatVersion.java
new file mode 100644
index 0000000..843248d
--- /dev/null
+++ b/engine/src/core/com/jme3/export/FormatVersion.java
@@ -0,0 +1,22 @@
+package com.jme3.export;
+
+/**
+ * Specifies the version of the format for jME3 object (j3o) files.
+ *
+ * @author Kirill Vainer
+ */
+public final class FormatVersion {
+
+ /**
+ * Version number of the format
+ */
+ public static final int VERSION = 2;
+
+ /**
+ * Signature of the format. Currently "JME3" as ASCII
+ */
+ public static final int SIGNATURE = 0x4A4D4533;
+
+ private FormatVersion(){
+ }
+}
diff --git a/engine/src/core/com/jme3/export/InputCapsule.java b/engine/src/core/com/jme3/export/InputCapsule.java
new file mode 100644
index 0000000..c7c7e16
--- /dev/null
+++ b/engine/src/core/com/jme3/export/InputCapsule.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export;
+
+import com.jme3.util.IntMap;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * @author Joshua Slack
+ */
+public interface InputCapsule {
+
+ public int getSavableVersion(Class<? extends Savable> clazz);
+
+ // byte primitive
+
+ public byte readByte(String name, byte defVal) throws IOException;
+ public byte[] readByteArray(String name, byte[] defVal) throws IOException;
+ public byte[][] readByteArray2D(String name, byte[][] defVal) throws IOException;
+
+ // int primitive
+
+ public int readInt(String name, int defVal) throws IOException;
+ public int[] readIntArray(String name, int[] defVal) throws IOException;
+ public int[][] readIntArray2D(String name, int[][] defVal) throws IOException;
+
+
+ // float primitive
+
+ public float readFloat(String name, float defVal) throws IOException;
+ public float[] readFloatArray(String name, float[] defVal) throws IOException;
+ public float[][] readFloatArray2D(String name, float[][] defVal) throws IOException;
+
+
+ // double primitive
+
+ public double readDouble(String name, double defVal) throws IOException;
+ public double[] readDoubleArray(String name, double[] defVal) throws IOException;
+ public double[][] readDoubleArray2D(String name, double[][] defVal) throws IOException;
+
+
+ // long primitive
+
+ public long readLong(String name, long defVal) throws IOException;
+ public long[] readLongArray(String name, long[] defVal) throws IOException;
+ public long[][] readLongArray2D(String name, long[][] defVal) throws IOException;
+
+
+ // short primitive
+
+ public short readShort(String name, short defVal) throws IOException;
+ public short[] readShortArray(String name, short[] defVal) throws IOException;
+ public short[][] readShortArray2D(String name, short[][] defVal) throws IOException;
+
+
+ // boolean primitive
+
+ public boolean readBoolean(String name, boolean defVal) throws IOException;
+ public boolean[] readBooleanArray(String name, boolean[] defVal) throws IOException;
+ public boolean[][] readBooleanArray2D(String name, boolean[][] defVal) throws IOException;
+
+
+ // String
+
+ public String readString(String name, String defVal) throws IOException;
+ public String[] readStringArray(String name, String[] defVal) throws IOException;
+ public String[][] readStringArray2D(String name, String[][] defVal) throws IOException;
+
+
+ // BitSet
+
+ public BitSet readBitSet(String name, BitSet defVal) throws IOException;
+
+
+ // BinarySavable
+
+ public Savable readSavable(String name, Savable defVal) throws IOException;
+ public Savable[] readSavableArray(String name, Savable[] defVal) throws IOException;
+ public Savable[][] readSavableArray2D(String name, Savable[][] defVal) throws IOException;
+
+
+ // ArrayLists
+
+ public ArrayList readSavableArrayList(String name, ArrayList defVal) throws IOException;
+ public ArrayList[] readSavableArrayListArray(String name, ArrayList[] defVal) throws IOException;
+ public ArrayList[][] readSavableArrayListArray2D(String name, ArrayList[][] defVal) throws IOException;
+
+ public ArrayList<FloatBuffer> readFloatBufferArrayList(String name, ArrayList<FloatBuffer> defVal) throws IOException;
+ public ArrayList<ByteBuffer> readByteBufferArrayList(String name, ArrayList<ByteBuffer> defVal) throws IOException;
+
+
+ // Maps
+
+ public Map<? extends Savable, ? extends Savable> readSavableMap(String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException;
+ public Map<String, ? extends Savable> readStringSavableMap(String name, Map<String, ? extends Savable> defVal) throws IOException;
+ public IntMap<? extends Savable> readIntSavableMap(String name, IntMap<? extends Savable> defVal) throws IOException;
+
+ // NIO BUFFERS
+ // float buffer
+
+ public FloatBuffer readFloatBuffer(String name, FloatBuffer defVal) throws IOException;
+
+
+ // int buffer
+
+ public IntBuffer readIntBuffer(String name, IntBuffer defVal) throws IOException;
+
+
+ // byte buffer
+
+ public ByteBuffer readByteBuffer(String name, ByteBuffer defVal) throws IOException;
+
+
+ // short buffer
+
+ public ShortBuffer readShortBuffer(String name, ShortBuffer defVal) throws IOException;
+
+
+ // enums
+
+ public <T extends Enum<T>> T readEnum(String name, Class<T> enumType, T defVal) throws IOException;
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/export/JmeExporter.java b/engine/src/core/com/jme3/export/JmeExporter.java
new file mode 100644
index 0000000..956dfd8
--- /dev/null
+++ b/engine/src/core/com/jme3/export/JmeExporter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * <code>JmeExporter</code> specifies an export implementation for jME3
+ * data.
+ */
+public interface JmeExporter {
+
+ /**
+ * Export the {@link Savable} to an OutputStream.
+ *
+ * @param object The savable to export
+ * @param f The output stream
+ * @return Always returns true. If an error occurs during export,
+ * an exception is thrown
+ * @throws IOException If an io exception occurs during export
+ */
+ public boolean save(Savable object, OutputStream f) throws IOException;
+
+ /**
+ * Export the {@link Savable} to a file.
+ *
+ * @param object The savable to export
+ * @param f The file to export to
+ * @return Always returns true. If an error occurs during export,
+ * an exception is thrown
+ * @throws IOException If an io exception occurs during export
+ */
+ public boolean save(Savable object, File f) throws IOException;
+
+ /**
+ * Returns the {@link OutputCapsule} for the given savable object.
+ *
+ * @param object The object to retrieve an output capsule for.
+ * @return
+ */
+ public OutputCapsule getCapsule(Savable object);
+}
diff --git a/engine/src/core/com/jme3/export/JmeImporter.java b/engine/src/core/com/jme3/export/JmeImporter.java
new file mode 100644
index 0000000..4986c85
--- /dev/null
+++ b/engine/src/core/com/jme3/export/JmeImporter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export;
+
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+
+public interface JmeImporter extends AssetLoader {
+ public InputCapsule getCapsule(Savable id);
+ public AssetManager getAssetManager();
+
+ /**
+ * Returns the version number written in the header of the J3O/XML
+ * file.
+ *
+ * @return Global version number for the file
+ */
+ public int getFormatVersion();
+}
diff --git a/engine/src/core/com/jme3/export/OutputCapsule.java b/engine/src/core/com/jme3/export/OutputCapsule.java
new file mode 100644
index 0000000..fecda9b
--- /dev/null
+++ b/engine/src/core/com/jme3/export/OutputCapsule.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export;
+
+import com.jme3.util.IntMap;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * @author Joshua Slack
+ */
+public interface OutputCapsule {
+
+ // byte primitive
+
+ public void write(byte value, String name, byte defVal) throws IOException;
+ public void write(byte[] value, String name, byte[] defVal) throws IOException;
+ public void write(byte[][] value, String name, byte[][] defVal) throws IOException;
+
+
+ // int primitive
+
+ public void write(int value, String name, int defVal) throws IOException;
+ public void write(int[] value, String name, int[] defVal) throws IOException;
+ public void write(int[][] value, String name, int[][] defVal) throws IOException;
+
+
+ // float primitive
+
+ public void write(float value, String name, float defVal) throws IOException;
+ public void write(float[] value, String name, float[] defVal) throws IOException;
+ public void write(float[][] value, String name, float[][] defVal) throws IOException;
+
+
+ // double primitive
+
+ public void write(double value, String name, double defVal) throws IOException;
+ public void write(double[] value, String name, double[] defVal) throws IOException;
+ public void write(double[][] value, String name, double[][] defVal) throws IOException;
+
+
+ // long primitive
+
+ public void write(long value, String name, long defVal) throws IOException;
+ public void write(long[] value, String name, long[] defVal) throws IOException;
+ public void write(long[][] value, String name, long[][] defVal) throws IOException;
+
+
+ // short primitive
+
+ public void write(short value, String name, short defVal) throws IOException;
+ public void write(short[] value, String name, short[] defVal) throws IOException;
+ public void write(short[][] value, String name, short[][] defVal) throws IOException;
+
+
+ // boolean primitive
+
+ public void write(boolean value, String name, boolean defVal) throws IOException;
+ public void write(boolean[] value, String name, boolean[] defVal) throws IOException;
+ public void write(boolean[][] value, String name, boolean[][] defVal) throws IOException;
+
+
+ // String
+
+ public void write(String value, String name, String defVal) throws IOException;
+ public void write(String[] value, String name, String[] defVal) throws IOException;
+ public void write(String[][] value, String name, String[][] defVal) throws IOException;
+
+
+ // BitSet
+
+ public void write(BitSet value, String name, BitSet defVal) throws IOException;
+
+
+ // BinarySavable
+
+ public void write(Savable object, String name, Savable defVal) throws IOException;
+ public void write(Savable[] objects, String name, Savable[] defVal) throws IOException;
+ public void write(Savable[][] objects, String name, Savable[][] defVal) throws IOException;
+
+
+ // ArrayLists
+
+ public void writeSavableArrayList(ArrayList array, String name, ArrayList defVal) throws IOException;
+ public void writeSavableArrayListArray(ArrayList[] array, String name, ArrayList[] defVal) throws IOException;
+ public void writeSavableArrayListArray2D(ArrayList[][] array, String name, ArrayList[][] defVal) throws IOException;
+
+ public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array, String name, ArrayList<FloatBuffer> defVal) throws IOException;
+ public void writeByteBufferArrayList(ArrayList<ByteBuffer> array, String name, ArrayList<ByteBuffer> defVal) throws IOException;
+
+
+ // Maps
+
+ public void writeSavableMap(Map<? extends Savable, ? extends Savable> map, String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException;
+ public void writeStringSavableMap(Map<String, ? extends Savable> map, String name, Map<String, ? extends Savable> defVal) throws IOException;
+ public void writeIntSavableMap(IntMap<? extends Savable> map, String name, IntMap<? extends Savable> defVal) throws IOException;
+
+ // NIO BUFFERS
+ // float buffer
+
+ public void write(FloatBuffer value, String name, FloatBuffer defVal) throws IOException;
+
+
+ // int buffer
+
+ public void write(IntBuffer value, String name, IntBuffer defVal) throws IOException;
+
+
+ // byte buffer
+
+ public void write(ByteBuffer value, String name, ByteBuffer defVal) throws IOException;
+
+
+ // short buffer
+
+ public void write(ShortBuffer value, String name, ShortBuffer defVal) throws IOException;
+
+
+ // enums
+
+ public void write(Enum value, String name, Enum defVal) throws IOException;
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/export/ReadListener.java b/engine/src/core/com/jme3/export/ReadListener.java
new file mode 100644
index 0000000..bb87b3a
--- /dev/null
+++ b/engine/src/core/com/jme3/export/ReadListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export;
+
+public interface ReadListener {
+
+ public void readBytes(int bytes);
+
+}
diff --git a/engine/src/core/com/jme3/export/Savable.java b/engine/src/core/com/jme3/export/Savable.java
new file mode 100644
index 0000000..0e90e30
--- /dev/null
+++ b/engine/src/core/com/jme3/export/Savable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export;
+
+import java.io.IOException;
+
+/**
+ * <code>Savable</code> is an interface for objects that can be serialized
+ * using jME's serialization system.
+ *
+ * @author Kirill Vainer
+ */
+public interface Savable {
+ void write(JmeExporter ex) throws IOException;
+ void read(JmeImporter im) throws IOException;
+}
diff --git a/engine/src/core/com/jme3/export/SavableClassUtil.java b/engine/src/core/com/jme3/export/SavableClassUtil.java
new file mode 100644
index 0000000..cda328a
--- /dev/null
+++ b/engine/src/core/com/jme3/export/SavableClassUtil.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.export;
+
+import com.jme3.animation.Animation;
+import com.jme3.effect.shapes.*;
+import com.jme3.material.MatParamTexture;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>SavableClassUtil</code> contains various utilities to handle
+ * Savable classes. The methods are general enough to not be specific to any
+ * particular implementation.
+ * Currently it will remap any classes from old paths to new paths
+ * so that old J3O models can still be loaded.
+ *
+ * @author mpowell
+ * @author Kirill Vainer
+ */
+public class SavableClassUtil {
+
+ private final static HashMap<String, String> classRemappings = new HashMap<String, String>();
+
+ private static void addRemapping(String oldClass, Class<? extends Savable> newClass){
+ classRemappings.put(oldClass, newClass.getName());
+ }
+
+ static {
+ addRemapping("com.jme3.effect.EmitterSphereShape", EmitterSphereShape.class);
+ addRemapping("com.jme3.effect.EmitterBoxShape", EmitterBoxShape.class);
+ addRemapping("com.jme3.effect.EmitterMeshConvexHullShape", EmitterMeshConvexHullShape.class);
+ addRemapping("com.jme3.effect.EmitterMeshFaceShape", EmitterMeshFaceShape.class);
+ addRemapping("com.jme3.effect.EmitterMeshVertexShape", EmitterMeshVertexShape.class);
+ addRemapping("com.jme3.effect.EmitterPointShape", EmitterPointShape.class);
+ addRemapping("com.jme3.material.Material$MatParamTexture", MatParamTexture.class);
+ addRemapping("com.jme3.animation.BoneAnimation", Animation.class);
+ addRemapping("com.jme3.animation.SpatialAnimation", Animation.class);
+ }
+
+ private static String remapClass(String className) throws ClassNotFoundException {
+ String result = classRemappings.get(className);
+ if (result == null) {
+ return className;
+ } else {
+ return result;
+ }
+ }
+
+ public static boolean isImplementingSavable(Class clazz){
+ boolean result = Savable.class.isAssignableFrom(clazz);
+ return result;
+ }
+
+ public static int[] getSavableVersions(Class<? extends Savable> clazz) throws IOException{
+ ArrayList<Integer> versionList = new ArrayList<Integer>();
+ Class superclass = clazz;
+ do {
+ versionList.add(getSavableVersion(superclass));
+ superclass = superclass.getSuperclass();
+ } while (superclass != null && SavableClassUtil.isImplementingSavable(superclass));
+
+ int[] versions = new int[versionList.size()];
+ for (int i = 0; i < versionList.size(); i++){
+ versions[i] = versionList.get(i);
+ }
+ return versions;
+ }
+
+ public static int getSavableVersion(Class<? extends Savable> clazz) throws IOException{
+ try {
+ Field field = clazz.getField("SAVABLE_VERSION");
+ Class<? extends Savable> declaringClass = (Class<? extends Savable>) field.getDeclaringClass();
+ if (declaringClass == clazz){
+ return field.getInt(null);
+ }else{
+ return 0; // This class doesn't declare this field, e.g. version == 0
+ }
+ } catch (IllegalAccessException ex) {
+ IOException ioEx = new IOException();
+ ioEx.initCause(ex);
+ throw ioEx;
+ } catch (IllegalArgumentException ex) {
+ throw ex; // can happen if SAVABLE_VERSION is not static
+ } catch (NoSuchFieldException ex) {
+ return 0; // not using versions
+ }
+ }
+
+ public static int getSavedSavableVersion(Object savable, Class<? extends Savable> desiredClass, int[] versions, int formatVersion){
+ Class thisClass = savable.getClass();
+ int count = 0;
+
+ while (thisClass != desiredClass) {
+ thisClass = thisClass.getSuperclass();
+ if (thisClass != null && SavableClassUtil.isImplementingSavable(thisClass)){
+ count ++;
+ }else{
+ break;
+ }
+ }
+
+ if (thisClass == null){
+ throw new IllegalArgumentException(savable.getClass().getName() +
+ " does not extend " +
+ desiredClass.getName() + "!");
+ }else if (count >= versions.length){
+ if (formatVersion <= 1){
+ return 0; // for buggy versions of j3o
+ }else{
+ throw new IllegalArgumentException(savable.getClass().getName() +
+ " cannot access version of " +
+ desiredClass.getName() +
+ " because it doesn't implement Savable");
+ }
+ }
+ return versions[count];
+ }
+
+ /**
+ * fromName creates a new Savable from the provided class name. First registered modules
+ * are checked to handle special cases, if the modules do not handle the class name, the
+ * class is instantiated directly.
+ * @param className the class name to create.
+ * @param inputCapsule the InputCapsule that will be used for loading the Savable (to look up ctor parameters)
+ * @return the Savable instance of the class.
+ * @throws InstantiationException thrown if the class does not have an empty constructor.
+ * @throws IllegalAccessException thrown if the class is not accessable.
+ * @throws ClassNotFoundException thrown if the class name is not in the classpath.
+ * @throws IOException when loading ctor parameters fails
+ */
+ public static Savable fromName(String className) throws InstantiationException,
+ IllegalAccessException, ClassNotFoundException, IOException {
+
+ className = remapClass(className);
+ try {
+ return (Savable) Class.forName(className).newInstance();
+ } catch (InstantiationException e) {
+ Logger.getLogger(SavableClassUtil.class.getName()).log(
+ Level.SEVERE, "Could not access constructor of class ''{0}" + "''! \n"
+ + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", className);
+ throw e;
+ } catch (IllegalAccessException e) {
+ Logger.getLogger(SavableClassUtil.class.getName()).log(
+ Level.SEVERE, "{0} \n"
+ + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", e.getMessage());
+ throw e;
+ }
+ }
+
+ public static Savable fromName(String className, List<ClassLoader> loaders) throws InstantiationException,
+ IllegalAccessException, ClassNotFoundException, IOException {
+ if (loaders == null) {
+ return fromName(className);
+ }
+
+ String newClassName = remapClass(className);
+ synchronized(loaders) {
+ for (ClassLoader classLoader : loaders){
+ try {
+ return (Savable) classLoader.loadClass(newClassName).newInstance();
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ }
+
+ }
+ }
+
+ return fromName(className);
+ }
+}
diff --git a/engine/src/core/com/jme3/font/BitmapCharacter.java b/engine/src/core/com/jme3/font/BitmapCharacter.java
new file mode 100644
index 0000000..8e06b87
--- /dev/null
+++ b/engine/src/core/com/jme3/font/BitmapCharacter.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font;
+
+import com.jme3.export.*;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.io.IOException;
+
+/**
+ * Represents a single bitmap character.
+ */
+public class BitmapCharacter implements Savable, Cloneable {
+ private char c;
+ private int x;
+ private int y;
+ private int width;
+ private int height;
+ private int xOffset;
+ private int yOffset;
+ private int xAdvance;
+ private IntMap<Integer> kerning = new IntMap<Integer>();
+ private int page;
+
+ public BitmapCharacter() {}
+
+ public BitmapCharacter(char c) {
+ this.c = c;
+ }
+
+ @Override
+ public BitmapCharacter clone() {
+ try {
+ BitmapCharacter result = (BitmapCharacter) super.clone();
+ result.kerning = kerning.clone();
+ return result;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public int getXOffset() {
+ return xOffset;
+ }
+
+ public void setXOffset(int offset) {
+ xOffset = offset;
+ }
+
+ public int getYOffset() {
+ return yOffset;
+ }
+
+ public void setYOffset(int offset) {
+ yOffset = offset;
+ }
+
+ public int getXAdvance() {
+ return xAdvance;
+ }
+
+ public void setXAdvance(int advance) {
+ xAdvance = advance;
+ }
+
+ public void setPage(int page) {
+ this.page = page;
+ }
+
+ public int getPage() {
+ return page;
+ }
+
+ public char getChar() {
+ return c;
+ }
+
+ public void setChar(char c) {
+ this.c = c;
+ }
+
+ public void addKerning(int second, int amount){
+ kerning.put(second, amount);
+ }
+
+ public int getKerning(int second){
+ Integer i = kerning.get(second);
+ if (i == null)
+ return 0;
+ else
+ return i.intValue();
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(c, "c", 0);
+ oc.write(x, "x", 0);
+ oc.write(y, "y", 0);
+ oc.write(width, "width", 0);
+ oc.write(height, "height", 0);
+ oc.write(xOffset, "xOffset", 0);
+ oc.write(yOffset, "yOffset", 0);
+ oc.write(xAdvance, "xAdvance", 0);
+
+ int[] seconds = new int[kerning.size()];
+ int[] amounts = new int[seconds.length];
+
+ int i = 0;
+ for (Entry<Integer> entry : kerning){
+ seconds[i] = entry.getKey();
+ amounts[i] = entry.getValue();
+ i++;
+ }
+
+ oc.write(seconds, "seconds", null);
+ oc.write(amounts, "amounts", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ c = (char) ic.readInt("c", 0);
+ x = ic.readInt("x", 0);
+ y = ic.readInt("y", 0);
+ width = ic.readInt("width", 0);
+ height = ic.readInt("height", 0);
+ xOffset = ic.readInt("xOffset", 0);
+ yOffset = ic.readInt("yOffset", 0);
+ xAdvance = ic.readInt("xAdvance", 0);
+
+ int[] seconds = ic.readIntArray("seconds", null);
+ int[] amounts = ic.readIntArray("amounts", null);
+
+ for (int i = 0; i < seconds.length; i++){
+ kerning.put(seconds[i], amounts[i]);
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/font/BitmapCharacterSet.java b/engine/src/core/com/jme3/font/BitmapCharacterSet.java
new file mode 100644
index 0000000..be51554
--- /dev/null
+++ b/engine/src/core/com/jme3/font/BitmapCharacterSet.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font;
+
+import com.jme3.export.*;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.io.IOException;
+
+public class BitmapCharacterSet implements Savable {
+
+ private int lineHeight;
+ private int base;
+ private int renderedSize;
+ private int width;
+ private int height;
+ private IntMap<IntMap<BitmapCharacter>> characters;
+ private int pageSize;
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(lineHeight, "lineHeight", 0);
+ oc.write(base, "base", 0);
+ oc.write(renderedSize, "renderedSize", 0);
+ oc.write(width, "width", 0);
+ oc.write(height, "height", 0);
+ oc.write(pageSize, "pageSize", 0);
+
+ int[] styles = new int[characters.size()];
+ int index = 0;
+ for (Entry<IntMap<BitmapCharacter>> entry : characters) {
+ int style = entry.getKey();
+ styles[index] = style;
+ index++;
+ IntMap<BitmapCharacter> charset = entry.getValue();
+ writeCharset(oc, style, charset);
+ }
+ oc.write(styles, "styles", null);
+ }
+
+ protected void writeCharset(OutputCapsule oc, int style, IntMap<BitmapCharacter> charset) throws IOException {
+ int size = charset.size();
+ short[] indexes = new short[size];
+ BitmapCharacter[] chars = new BitmapCharacter[size];
+ int i = 0;
+ for (Entry<BitmapCharacter> chr : charset){
+ indexes[i] = (short) chr.getKey();
+ chars[i] = chr.getValue();
+ i++;
+ }
+
+ oc.write(indexes, "indexes"+style, null);
+ oc.write(chars, "chars"+style, null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ lineHeight = ic.readInt("lineHeight", 0);
+ base = ic.readInt("base", 0);
+ renderedSize = ic.readInt("renderedSize", 0);
+ width = ic.readInt("width", 0);
+ height = ic.readInt("height", 0);
+ pageSize = ic.readInt("pageSize", 0);
+ int[] styles = ic.readIntArray("styles", null);
+
+ for (int style : styles) {
+ characters.put(style, readCharset(ic, style));
+ }
+ }
+
+ private IntMap<BitmapCharacter> readCharset(InputCapsule ic, int style) throws IOException {
+ IntMap<BitmapCharacter> charset = new IntMap<BitmapCharacter>();
+ short[] indexes = ic.readShortArray("indexes"+style, null);
+ Savable[] chars = ic.readSavableArray("chars"+style, null);
+
+ for (int i = 0; i < indexes.length; i++){
+ int index = indexes[i] & 0xFFFF;
+ BitmapCharacter chr = (BitmapCharacter) chars[i];
+ charset.put(index, chr);
+ }
+ return charset;
+ }
+
+ public BitmapCharacterSet() {
+ characters = new IntMap<IntMap<BitmapCharacter>>();
+ }
+
+ public BitmapCharacter getCharacter(int index){
+ return getCharacter(index, 0);
+ }
+
+ public BitmapCharacter getCharacter(int index, int style){
+ IntMap<BitmapCharacter> map = getCharacterSet(style);
+ return map.get(index);
+ }
+
+ private IntMap<BitmapCharacter> getCharacterSet(int style) {
+ if (characters.size() == 0) {
+ characters.put(style, new IntMap<BitmapCharacter>());
+ }
+ return characters.get(style);
+ }
+
+ public void addCharacter(int index, BitmapCharacter ch){
+ getCharacterSet(0).put(index, ch);
+ }
+
+ public int getLineHeight() {
+ return lineHeight;
+ }
+
+ public void setLineHeight(int lineHeight) {
+ this.lineHeight = lineHeight;
+ }
+
+ public int getBase() {
+ return base;
+ }
+
+ public void setBase(int base) {
+ this.base = base;
+ }
+
+ public int getRenderedSize() {
+ return renderedSize;
+ }
+
+ public void setRenderedSize(int renderedSize) {
+ this.renderedSize = renderedSize;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ /**
+ * Merge two fonts.
+ * If two font have the same style, merge will fail.
+ * @param styleSet Style must be assigned to this.
+ * @author Yonghoon
+ */
+ public void merge(BitmapCharacterSet styleSet) {
+ if (this.renderedSize != styleSet.renderedSize) {
+ throw new RuntimeException("Only support same font size");
+ }
+ for (Entry<IntMap<BitmapCharacter>> entry : styleSet.characters) {
+ int style = entry.getKey();
+ if (style == 0) {
+ throw new RuntimeException("Style must be set first. use setStyle(int)");
+ }
+ IntMap<BitmapCharacter> charset = entry.getValue();
+ this.lineHeight = Math.max(this.lineHeight, styleSet.lineHeight);
+ IntMap<BitmapCharacter> old = this.characters.put(style, charset);
+ if (old != null) {
+ throw new RuntimeException("Can't override old style");
+ }
+
+ for (Entry<BitmapCharacter> charEntry : charset) {
+ BitmapCharacter ch = charEntry.getValue();
+ ch.setPage(ch.getPage() + this.pageSize);
+ }
+ }
+ this.pageSize += styleSet.pageSize;
+ }
+
+ public void setStyle(int style) {
+ if (characters.size() > 1) {
+ throw new RuntimeException("Applicable only for single style font");
+ }
+ Entry<IntMap<BitmapCharacter>> entry = characters.iterator().next();
+ IntMap<BitmapCharacter> charset = entry.getValue();
+ characters.remove(entry.getKey());
+ characters.put(style, charset);
+ }
+
+ void setPageSize(int pageSize) {
+ this.pageSize = pageSize;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/font/BitmapFont.java b/engine/src/core/com/jme3/font/BitmapFont.java
new file mode 100644
index 0000000..ce532e9
--- /dev/null
+++ b/engine/src/core/com/jme3/font/BitmapFont.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font;
+
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import java.io.IOException;
+
+/**
+ * Represents a font within jME that is generated with the AngelCode Bitmap Font Generator
+ * @author dhdd
+ */
+public class BitmapFont implements Savable {
+
+ /**
+ * Specifies horizontal alignment for text.
+ *
+ * @see BitmapText#setAlignment(com.jme3.font.BitmapFont.Align)
+ */
+ public enum Align {
+
+ /**
+ * Align text on the left of the text block
+ */
+ Left,
+
+ /**
+ * Align text in the center of the text block
+ */
+ Center,
+
+ /**
+ * Align text on the right of the text block
+ */
+ Right
+ }
+
+ /**
+ * Specifies vertical alignment for text.
+ *
+ * @see BitmapText#setVerticalAlignment(com.jme3.font.BitmapFont.VAlign)
+ */
+ public enum VAlign {
+ /**
+ * Align text on the top of the text block
+ */
+ Top,
+
+ /**
+ * Align text in the center of the text block
+ */
+ Center,
+
+ /**
+ * Align text at the bottom of the text block
+ */
+ Bottom
+ }
+
+ private BitmapCharacterSet charSet;
+ private Material[] pages;
+
+ public BitmapFont() {
+ }
+
+ public BitmapText createLabel(String content){
+ BitmapText label = new BitmapText(this);
+ label.setSize(getCharSet().getRenderedSize());
+ label.setText(content);
+ return label;
+ }
+
+ public float getPreferredSize(){
+ return getCharSet().getRenderedSize();
+ }
+
+ public void setCharSet(BitmapCharacterSet charSet) {
+ this.charSet = charSet;
+ }
+
+ public void setPages(Material[] pages) {
+ this.pages = pages;
+ charSet.setPageSize(pages.length);
+ }
+
+ public Material getPage(int index) {
+ return pages[index];
+ }
+
+ public int getPageSize() {
+ return pages.length;
+ }
+
+ public BitmapCharacterSet getCharSet() {
+ return charSet;
+ }
+
+ /**
+ * Gets the line height of a StringBlock.
+ * @param sb
+ * @return
+ */
+ public float getLineHeight(StringBlock sb) {
+ return charSet.getLineHeight() * (sb.getSize() / charSet.getRenderedSize());
+ }
+
+ public float getCharacterAdvance(char curChar, char nextChar, float size){
+ BitmapCharacter c = charSet.getCharacter(curChar);
+ if (c == null)
+ return 0f;
+
+ float advance = size * c.getXAdvance();
+ advance += c.getKerning(nextChar) * size;
+ return advance;
+ }
+
+ private int findKerningAmount(int newLineLastChar, int nextChar) {
+ BitmapCharacter c = charSet.getCharacter(newLineLastChar);
+ if (c == null)
+ return 0;
+ return c.getKerning(nextChar);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(charSet, "charSet", null);
+ oc.write(pages, "pages", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
+ Savable[] pagesSavable = ic.readSavableArray("pages", null);
+ pages = new Material[pagesSavable.length];
+ System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
+ }
+
+ public float getLineWidth(CharSequence text){
+
+ // This method will probably always be a bit of a maintenance
+ // nightmare since it basis its calculation on a different
+ // routine than the Letters class. The ideal situation would
+ // be to abstract out letter position and size into its own
+ // class that both BitmapFont and Letters could use for
+ // positioning.
+ // If getLineWidth() here ever again returns a different value
+ // than Letters does with the same text then it might be better
+ // just to create a Letters object for the sole purpose of
+ // getting a text size. It's less efficient but at least it
+ // would be accurate.
+
+ // And here I am mucking around in here again...
+ //
+ // A font character has a few values that are pertinent to the
+ // line width:
+ // xOffset
+ // xAdvance
+ // kerningAmount(nextChar)
+ //
+ // The way BitmapText ultimately works is that the first character
+ // starts with xOffset included (ie: it is rendered at -xOffset).
+ // Its xAdvance is wider to accomodate that initial offset.
+ // The cursor position is advanced by xAdvance each time.
+ //
+ // So, a width should be calculated in a similar way. Start with
+ // -xOffset + xAdvance for the first character and then each subsequent
+ // character is just xAdvance more 'width'.
+ //
+ // The kerning amount from one character to the next affects the
+ // cursor position of that next character and thus the ultimate width
+ // and so must be factored in also.
+
+ float lineWidth = 0f;
+ float maxLineWidth = 0f;
+ char lastChar = 0;
+ boolean firstCharOfLine = true;
+// float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
+ float sizeScale = 1f;
+ for (int i = 0; i < text.length(); i++){
+ char theChar = text.charAt(i);
+ if (theChar == '\n'){
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ lineWidth = 0f;
+ firstCharOfLine = true;
+ continue;
+ }
+ BitmapCharacter c = charSet.getCharacter((int) theChar);
+ if (c != null){
+ if (theChar == '\\' && i<text.length()-1 && text.charAt(i+1)=='#'){
+ if (i+5<text.length() && text.charAt(i+5)=='#'){
+ i+=5;
+ continue;
+ }else if (i+8<text.length() && text.charAt(i+8)=='#'){
+ i+=8;
+ continue;
+ }
+ }
+ if (!firstCharOfLine){
+ lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
+ } else {
+ // The first character needs to add in its xOffset but it
+ // is the only one... and negative offsets = postive width
+ // because we're trying to account for the part that hangs
+ // over the left. So we subtract.
+ lineWidth -= c.getXOffset() * sizeScale;
+ firstCharOfLine = false;
+ }
+ float xAdvance = c.getXAdvance() * sizeScale;
+
+ // If this is the last character, then we really should have
+ // only add its width. The advance may include extra spacing
+ // that we don't care about.
+ if (i == text.length() - 1) {
+ lineWidth += c.getWidth() * sizeScale;
+
+ // Since theh width includes the xOffset then we need
+ // to take it out again by adding it, ie: offset the width
+ // we just added by the appropriate amount.
+ lineWidth += c.getXOffset() * sizeScale;
+ } else {
+ lineWidth += xAdvance;
+ }
+ }
+ }
+ return Math.max(maxLineWidth, lineWidth);
+ }
+
+
+ /**
+ * Merge two fonts.
+ * If two font have the same style, merge will fail.
+ * @param styleSet Style must be assigned to this.
+ * @author Yonghoon
+ */
+ public void merge(BitmapFont newFont) {
+ charSet.merge(newFont.charSet);
+ final int size1 = this.pages.length;
+ final int size2 = newFont.pages.length;
+
+ Material[] tmp = new Material[size1+size2];
+ System.arraycopy(this.pages, 0, tmp, 0, size1);
+ System.arraycopy(newFont.pages, 0, tmp, size1, size2);
+
+ this.pages = tmp;
+
+// this.pages = Arrays.copyOf(this.pages, size1+size2);
+// System.arraycopy(newFont.pages, 0, this.pages, size1, size2);
+ }
+
+ public void setStyle(int style) {
+ charSet.setStyle(style);
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/font/BitmapText.java b/engine/src/core/com/jme3/font/BitmapText.java
new file mode 100644
index 0000000..7601324
--- /dev/null
+++ b/engine/src/core/com/jme3/font/BitmapText.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.font;
+
+import com.jme3.font.BitmapFont.Align;
+import com.jme3.font.BitmapFont.VAlign;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Node;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author YongHoon
+ */
+public class BitmapText extends Node {
+ private BitmapFont font;
+ private StringBlock block;
+ private boolean needRefresh = true;
+ private final BitmapTextPage[] textPages;
+ private Letters letters;
+
+ public BitmapText(BitmapFont font) {
+ this(font, false, false);
+ }
+
+ public BitmapText(BitmapFont font, boolean rightToLeft) {
+ this(font, rightToLeft, false);
+ }
+
+ public BitmapText(BitmapFont font, boolean rightToLeft, boolean arrayBased) {
+ textPages = new BitmapTextPage[font.getPageSize()];
+ for (int page = 0; page < textPages.length; page++) {
+ textPages[page] = new BitmapTextPage(font, arrayBased, page);
+ attachChild(textPages[page]);
+ }
+
+ this.font = font;
+ this.block = new StringBlock();
+ block.setSize(font.getPreferredSize());
+ letters = new Letters(font, block, rightToLeft);
+ }
+
+ @Override
+ public BitmapText clone() {
+ BitmapText clone = (BitmapText) super.clone();
+ for (int i = 0; i < textPages.length; i++) {
+ clone.textPages[i] = textPages[i].clone();
+ }
+ clone.block = block.clone();
+ clone.needRefresh = true;
+ return clone;
+ }
+
+ public BitmapFont getFont() {
+ return font;
+ }
+
+ /**
+ * Changes text size
+ * @param size text size
+ */
+ public void setSize(float size) {
+ block.setSize(size);
+ needRefresh = true;
+ letters.invalidate();
+ }
+
+ /**
+ *
+ * @param text charsequence to change text to
+ */
+ public void setText(CharSequence text) {
+ // note: text.toString() is free if text is already a java.lang.String.
+ setText( text != null ? text.toString() : null );
+ }
+
+ /**
+ *
+ * @param text String to change text to
+ */
+ public void setText(String text) {
+ text = text == null ? "" : text;
+ if (text == block.getText() || block.getText().equals(text)) {
+ return;
+ }
+
+ block.setText(text);
+ letters.setText(text);
+ needRefresh = true;
+ }
+
+ /**
+ * @return returns text
+ */
+ public String getText() {
+ return block.getText();
+ }
+
+ /**
+ * @return color of the text
+ */
+ public ColorRGBA getColor() {
+ return letters.getBaseColor();
+ }
+
+ /**
+ * changes text color. all substring colors are deleted.
+ * @param color new color of text
+ */
+ public void setColor(ColorRGBA color) {
+ letters.setColor(color);
+ letters.invalidate(); // TODO: Don't have to align.
+ needRefresh = true;
+ }
+
+ /**
+ * Define area where bitmaptext will be rendered
+ * @param rect position and size box where text is rendered
+ */
+ public void setBox(Rectangle rect) {
+ block.setTextBox(rect);
+ letters.invalidate();
+ needRefresh = true;
+ }
+
+ /**
+ * @return height of the line
+ */
+ public float getLineHeight() {
+ return font.getLineHeight(block);
+ }
+
+ /**
+ * @return height of whole textblock
+ */
+ public float getHeight() {
+ if (needRefresh) {
+ assemble();
+ }
+ float height = getLineHeight()*block.getLineCount();
+ Rectangle textBox = block.getTextBox();
+ if (textBox != null) {
+ return Math.max(height, textBox.height);
+ }
+ return height;
+ }
+
+ /**
+ * @return width of line
+ */
+ public float getLineWidth() {
+ if (needRefresh) {
+ assemble();
+ }
+ Rectangle textBox = block.getTextBox();
+ if (textBox != null) {
+ return Math.max(letters.getTotalWidth(), textBox.width);
+ }
+ return letters.getTotalWidth();
+ }
+
+ /**
+ * @return line count
+ */
+ public int getLineCount() {
+ if (needRefresh) {
+ assemble();
+ }
+ return block.getLineCount();
+ }
+
+ public LineWrapMode getLineWrapMode() {
+ return block.getLineWrapMode();
+ }
+
+ /**
+ * Set horizontal alignment. Applicable only when text bound is set.
+ * @param align
+ */
+ public void setAlignment(BitmapFont.Align align) {
+ if (block.getTextBox() == null && align != Align.Left) {
+ throw new RuntimeException("Bound is not set");
+ }
+ block.setAlignment(align);
+ letters.invalidate();
+ needRefresh = true;
+ }
+
+ /**
+ * Set vertical alignment. Applicable only when text bound is set.
+ * @param align
+ */
+ public void setVerticalAlignment(BitmapFont.VAlign align) {
+ if (block.getTextBox() == null && align != VAlign.Top) {
+ throw new RuntimeException("Bound is not set");
+ }
+ block.setVerticalAlignment(align);
+ letters.invalidate();
+ needRefresh = true;
+ }
+
+ public BitmapFont.Align getAlignment() {
+ return block.getAlignment();
+ }
+
+ public BitmapFont.VAlign getVerticalAlignment() {
+ return block.getVerticalAlignment();
+ }
+
+ /**
+ * Set the font style of substring. If font doesn't contain style, default style is used
+ * @param start start index to set style. inclusive.
+ * @param end end index to set style. EXCLUSIVE.
+ * @param style
+ */
+ public void setStyle(int start, int end, int style) {
+ letters.setStyle(start, end, style);
+ }
+
+ /**
+ * Set the font style of substring. If font doesn't contain style, default style is applied
+ * @param regexp regular expression
+ * @param style
+ */
+ public void setStyle(String regexp, int style) {
+ Pattern p = Pattern.compile(regexp);
+ Matcher m = p.matcher(block.getText());
+ while (m.find()) {
+ setStyle(m.start(), m.end(), style);
+ }
+ }
+
+ /**
+ * Set the color of substring.
+ * @param start start index to set style. inclusive.
+ * @param end end index to set style. EXCLUSIVE.
+ * @param color
+ */
+ public void setColor(int start, int end, ColorRGBA color) {
+ letters.setColor(start, end, color);
+ letters.invalidate();
+ needRefresh = true;
+ }
+
+ /**
+ * Set the color of substring.
+ * @param regexp regular expression
+ * @param color
+ */
+ public void setColor(String regexp, ColorRGBA color) {
+ Pattern p = Pattern.compile(regexp);
+ Matcher m = p.matcher(block.getText());
+ while (m.find()) {
+ letters.setColor(m.start(), m.end(), color);
+ }
+ letters.invalidate();
+ needRefresh = true;
+ }
+
+ /**
+ * @param tabs tab positions
+ */
+ public void setTabPosition(float... tabs) {
+ block.setTabPosition(tabs);
+ letters.invalidate();
+ needRefresh = false;
+ }
+
+ /**
+ * used for the tabs over the last tab position.
+ * @param width tab size
+ */
+ public void setTabWidth(float width) {
+ block.setTabWidth(width);
+ letters.invalidate();
+ needRefresh = false;
+ }
+
+ /**
+ * for setLineWrapType(LineWrapType.NoWrap),
+ * set the last character when the text exceeds the bound.
+ * @param c
+ */
+ public void setEllipsisChar(char c) {
+ block.setEllipsisChar(c);
+ letters.invalidate();
+ needRefresh = false;
+ }
+
+ /**
+ * Available only when bounding is set. <code>setBox()</code> method call is needed in advance.
+ * true when
+ * @param wrap NoWrap : Letters over the text bound is not shown. the last character is set to '...'(0x2026)
+ * Character: Character is split at the end of the line.
+ * Word : Word is split at the end of the line.
+ */
+ public void setLineWrapMode(LineWrapMode wrap) {
+ if (block.getLineWrapMode() != wrap) {
+ block.setLineWrapMode(wrap);
+ letters.invalidate();
+ needRefresh = true;
+ }
+ }
+
+ @Override
+ public void updateLogicalState(float tpf) {
+ super.updateLogicalState(tpf);
+ if (needRefresh) {
+ assemble();
+ }
+ }
+
+ private void assemble() {
+ // first generate quadlist
+ letters.update();
+
+ for (int i = 0; i < textPages.length; i++) {
+ textPages[i].assemble(letters);
+ }
+ needRefresh = false;
+ }
+
+ public void render(RenderManager rm) {
+ for (BitmapTextPage page : textPages) {
+ Material mat = page.getMaterial();
+ mat.setTexture("Texture", page.getTexture());
+ mat.render(page, rm);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/font/BitmapTextPage.java b/engine/src/core/com/jme3/font/BitmapTextPage.java
new file mode 100644
index 0000000..f49b5c9
--- /dev/null
+++ b/engine/src/core/com/jme3/font/BitmapTextPage.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.font;
+
+import com.jme3.material.Material;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.LinkedList;
+
+/**
+ * One page per BitmapText Font Texture.
+ * @author Lim, YongHoon
+ */
+class BitmapTextPage extends Geometry {
+
+ private final float[] pos;
+ private final float[] tc;
+ private final short[] idx;
+ private final byte[] color;
+ private final int page;
+ private final Texture2D texture;
+ private final LinkedList<LetterQuad> pageQuads = new LinkedList<LetterQuad>();
+
+ BitmapTextPage(BitmapFont font, boolean arrayBased, int page) {
+ super("BitmapFont", new Mesh());
+
+ if (font == null) {
+ throw new NullPointerException("'font' cannot be null.");
+ }
+
+ this.page = page;
+
+ Material mat = font.getPage(page);
+ if (mat == null) {
+ throw new IllegalStateException("The font's texture was not found!");
+ }
+
+ setMaterial(mat);
+ this.texture = (Texture2D) mat.getTextureParam("ColorMap").getTextureValue();
+
+ // initialize buffers
+ Mesh m = getMesh();
+ m.setBuffer(Type.Position, 3, new float[0]);
+ m.setBuffer(Type.TexCoord, 2, new float[0]);
+ m.setBuffer(Type.Color, 4, new byte[0]);
+ m.setBuffer(Type.Index, 3, new short[0]);
+
+ // scale colors from 0 - 255 range into 0 - 1
+ m.getBuffer(Type.Color).setNormalized(true);
+
+ arrayBased = true;
+
+ if (arrayBased) {
+ pos = new float[4 * 3]; // 4 verticies * 3 floats
+ tc = new float[4 * 2]; // 4 verticies * 2 floats
+ idx = new short[2 * 3]; // 2 triangles * 3 indices
+ color = new byte[4 * 4]; // 4 verticies * 4 bytes
+ } else {
+ pos = null;
+ tc = null;
+ idx = null;
+ color = null;
+ }
+ }
+
+ BitmapTextPage(BitmapFont font, boolean arrayBased) {
+ this(font, arrayBased, 0);
+ }
+
+ BitmapTextPage(BitmapFont font) {
+ this(font, false, 0);
+ }
+
+ Texture2D getTexture() {
+ return texture;
+ }
+
+ @Override
+ public BitmapTextPage clone() {
+ BitmapTextPage clone = (BitmapTextPage) super.clone();
+ clone.mesh = mesh.deepClone();
+ return clone;
+ }
+
+ void assemble(Letters quads) {
+ pageQuads.clear();
+ quads.rewind();
+
+ while (quads.nextCharacter()) {
+ if (quads.isPrintable()) {
+ if (quads.getCharacterSetPage() == page) {
+ pageQuads.add(quads.getQuad());
+ }
+ }
+ }
+
+ Mesh m = getMesh();
+ int vertCount = pageQuads.size() * 4;
+ int triCount = pageQuads.size() * 2;
+
+ VertexBuffer pb = m.getBuffer(Type.Position);
+ VertexBuffer tb = m.getBuffer(Type.TexCoord);
+ VertexBuffer ib = m.getBuffer(Type.Index);
+ VertexBuffer cb = m.getBuffer(Type.Color);
+
+ FloatBuffer fpb = (FloatBuffer) pb.getData();
+ FloatBuffer ftb = (FloatBuffer) tb.getData();
+ ShortBuffer sib = (ShortBuffer) ib.getData();
+ ByteBuffer bcb = (ByteBuffer) cb.getData();
+
+ // increase capacity of buffers as needed
+ fpb.rewind();
+ fpb = BufferUtils.ensureLargeEnough(fpb, vertCount * 3);
+ fpb.limit(vertCount * 3);
+ pb.updateData(fpb);
+
+ ftb.rewind();
+ ftb = BufferUtils.ensureLargeEnough(ftb, vertCount * 2);
+ ftb.limit(vertCount * 2);
+ tb.updateData(ftb);
+
+ bcb.rewind();
+ bcb = BufferUtils.ensureLargeEnough(bcb, vertCount * 4);
+ bcb.limit(vertCount * 4);
+ cb.updateData(bcb);
+
+ sib.rewind();
+ sib = BufferUtils.ensureLargeEnough(sib, triCount * 3);
+ sib.limit(triCount * 3);
+ ib.updateData(sib);
+
+ m.updateCounts();
+
+ // go for each quad and append it to the buffers
+ if (pos != null) {
+ for (int i = 0; i < pageQuads.size(); i++) {
+ LetterQuad fq = pageQuads.get(i);
+ fq.storeToArrays(pos, tc, idx, color, i);
+ fpb.put(pos);
+ ftb.put(tc);
+ sib.put(idx);
+ bcb.put(color);
+ }
+ } else {
+ for (int i = 0; i < pageQuads.size(); i++) {
+ LetterQuad fq = pageQuads.get(i);
+ fq.appendPositions(fpb);
+ fq.appendTexCoords(ftb);
+ fq.appendIndices(sib, i);
+ fq.appendColors(bcb);
+ }
+ }
+
+ fpb.rewind();
+ ftb.rewind();
+ sib.rewind();
+ bcb.rewind();
+
+ updateModelBound();
+ }
+}
diff --git a/engine/src/core/com/jme3/font/ColorTags.java b/engine/src/core/com/jme3/font/ColorTags.java
new file mode 100644
index 0000000..01f15c3
--- /dev/null
+++ b/engine/src/core/com/jme3/font/ColorTags.java
@@ -0,0 +1,91 @@
+package com.jme3.font;
+
+import com.jme3.math.ColorRGBA;
+import java.util.LinkedList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Contains the color information tagged in a text string
+ * Format: \#rgb#
+ * \#rgba#
+ * \#rrggbb#
+ * \#rrggbbaa#
+ * @author YongHoon
+ */
+class ColorTags {
+ private static final Pattern colorPattern = Pattern.compile("\\\\#([0-9a-fA-F]{8})#|\\\\#([0-9a-fA-F]{6})#|" +
+ "\\\\#([0-9a-fA-F]{4})#|\\\\#([0-9a-fA-F]{3})#");
+ private LinkedList<Range> colors = new LinkedList<Range>();
+ private String text;
+
+ ColorTags() { }
+
+ ColorTags(String seq) {
+ setText(seq);
+ }
+
+ /**
+ * @return text without color tags
+ */
+ String getPlainText() {
+ return text;
+ }
+
+ LinkedList<Range> getTags() {
+ return colors;
+ }
+
+ void setText(final String charSeq) {
+ colors.clear();
+ if (charSeq == null) {
+ return;
+ }
+ Matcher m = colorPattern.matcher(charSeq);
+ if (m.find()) {
+ StringBuilder builder = new StringBuilder(charSeq.length()-7);
+ int startIndex = 0;
+ do {
+ String colorStr = null;
+ for (int i = 1; i <= 4 && colorStr==null; i++) {
+ colorStr = m.group(i);
+ }
+ builder.append(charSeq.subSequence(startIndex, m.start()));
+ Range range = new Range(builder.length(), colorStr);
+ startIndex = m.end();
+ colors.add(range);
+ } while (m.find());
+ builder.append(charSeq.subSequence(startIndex, charSeq.length()));
+ text = builder.toString();
+ } else {
+ text = charSeq;
+ }
+ }
+
+ class Range {
+ int start;
+ ColorRGBA color;
+ Range(int start, String colorStr) {
+ this.start = start;
+ this.color = new ColorRGBA();
+ if (colorStr.length() >= 6) {
+ color.set(Integer.parseInt(colorStr.subSequence(0,2).toString(), 16) / 255f,
+ Integer.parseInt(colorStr.subSequence(2,4).toString(), 16) / 255f,
+ Integer.parseInt(colorStr.subSequence(4,6).toString(), 16) / 255f,
+ 1);
+ if (colorStr.length() == 8) {
+ color.a = Integer.parseInt(colorStr.subSequence(6,8).toString(), 16) / 255f;
+ }
+ } else {
+ color.set(Integer.parseInt(Character.toString(colorStr.charAt(0)), 16) / 15f,
+ Integer.parseInt(Character.toString(colorStr.charAt(1)), 16) / 15f,
+ Integer.parseInt(Character.toString(colorStr.charAt(2)), 16) / 15f,
+ 1);
+ if (colorStr.length() == 4) {
+ color.a = Integer.parseInt(Character.toString(colorStr.charAt(3)), 16) / 15f;
+ }
+ }
+
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/font/Kerning.java b/engine/src/core/com/jme3/font/Kerning.java
new file mode 100644
index 0000000..e4be815
--- /dev/null
+++ b/engine/src/core/com/jme3/font/Kerning.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+
+/**
+ * Represents kerning information for a character.
+ */
+public class Kerning implements Savable {
+
+ private int second;
+ private int amount;
+
+ public int getSecond() {
+ return second;
+ }
+
+ public void setSecond(int second) {
+ this.second = second;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+
+ public void setAmount(int amount) {
+ this.amount = amount;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(second, "second", 0);
+ oc.write(amount, "amount", 0);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ second = ic.readInt("second", 0);
+ amount = ic.readInt("amount", 0);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/font/LetterQuad.java b/engine/src/core/com/jme3/font/LetterQuad.java
new file mode 100644
index 0000000..09f0e24
--- /dev/null
+++ b/engine/src/core/com/jme3/font/LetterQuad.java
@@ -0,0 +1,496 @@
+package com.jme3.font;
+
+import com.jme3.math.ColorRGBA;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * LetterQuad contains the position, color, uv texture information for a character in text.
+ * @author YongHoon
+ */
+class LetterQuad {
+ private static final Rectangle UNBOUNDED = new Rectangle(0, 0, Float.MAX_VALUE, Float.MAX_VALUE);
+ private static final float LINE_DIR = -1;
+
+ private final BitmapFont font;
+ private final char c;
+ private final int index;
+ private int style;
+
+ private BitmapCharacter bitmapChar = null;
+ private float x0 = Integer.MIN_VALUE;
+ private float y0 = Integer.MIN_VALUE;
+ private float width = Integer.MIN_VALUE;
+ private float height = Integer.MIN_VALUE;
+ private float xAdvance = 0;
+ private float u0;
+ private float v0;
+ private float u1;
+ private float v1;
+ private float lineY;
+ private boolean eol;
+
+ private LetterQuad previous;
+ private LetterQuad next;
+ private int colorInt = 0xFFFFFFFF;
+
+ private boolean rightToLeft;
+ private float alignX;
+ private float alignY;
+ private float sizeScale = 1;
+
+ /**
+ * create head / tail
+ * @param font
+ * @param rightToLeft
+ */
+ protected LetterQuad(BitmapFont font, boolean rightToLeft) {
+ this.font = font;
+ this.c = Character.MIN_VALUE;
+ this.rightToLeft = rightToLeft;
+ this.index = -1;
+ setBitmapChar(null);
+ }
+
+ /**
+ * create letter and append to previous LetterQuad
+ *
+ * @param c
+ * @param prev previous character
+ */
+ protected LetterQuad(char c, LetterQuad prev) {
+ this.font = prev.font;
+ this.rightToLeft = prev.rightToLeft;
+ this.c = c;
+ this.index = prev.index+1;
+ this.eol = isLineFeed();
+ setBitmapChar(c);
+ prev.insert(this);
+ }
+
+ LetterQuad addNextCharacter(char c) {
+ LetterQuad n = new LetterQuad(c, this);
+ return n;
+ }
+
+ BitmapCharacter getBitmapChar() {
+ return bitmapChar;
+ }
+
+ char getChar() {
+ return c;
+ }
+
+ int getIndex() {
+ return index;
+ }
+
+ private Rectangle getBound(StringBlock block) {
+ if (block.getTextBox() != null) {
+ return block.getTextBox();
+ }
+ return UNBOUNDED;
+ }
+
+ LetterQuad getPrevious() {
+ return previous;
+ }
+
+ LetterQuad getNext() {
+ return next;
+ }
+
+ public float getU0() {
+ return u0;
+ }
+
+ float getU1() {
+ return u1;
+ }
+
+ float getV0() {
+ return v0;
+ }
+
+ float getV1() {
+ return v1;
+ }
+
+ boolean isInvalid() {
+ return x0 == Integer.MIN_VALUE;
+ }
+
+ boolean isInvalid(StringBlock block) {
+ return isInvalid(block, 0);
+ }
+
+ boolean isInvalid(StringBlock block, float gap) {
+ if (isHead() || isTail())
+ return false;
+ if (x0 == Integer.MIN_VALUE || y0 == Integer.MIN_VALUE) {
+ return true;
+ }
+ Rectangle bound = block.getTextBox();
+ if (bound == null) {
+ return false;
+ }
+ return x0 > 0 && bound.x+bound.width-gap < getX1();
+ }
+
+ float getX0() {
+ return x0;
+ }
+
+ float getX1() {
+ return x0+width;
+ }
+
+ float getNextX() {
+ return x0+xAdvance;
+ }
+
+ float getNextLine() {
+ return lineY+LINE_DIR*font.getCharSet().getLineHeight() * sizeScale;
+ }
+
+ float getY0() {
+ return y0;
+ }
+
+ float getY1() {
+ return y0-height;
+ }
+
+ float getWidth() {
+ return width;
+ }
+
+ float getHeight() {
+ return height;
+ }
+
+ void insert(LetterQuad ins) {
+ LetterQuad n = next;
+ next = ins;
+ ins.next = n;
+ ins.previous = this;
+ n.previous = ins;
+ }
+
+ void invalidate() {
+ eol = isLineFeed();
+ setBitmapChar(font.getCharSet().getCharacter(c, style));
+ }
+
+ boolean isTail() {
+ return next == null;
+ }
+
+ boolean isHead() {
+ return previous == null;
+ }
+
+ /**
+ * @return next letter
+ */
+ LetterQuad remove() {
+ this.previous.next = next;
+ this.next.previous = previous;
+ return next;
+ }
+
+ void setPrevious(LetterQuad before) {
+ this.previous = before;
+ }
+
+ void setStyle(int style) {
+ this.style = style;
+ invalidate();
+ }
+
+ void setColor(ColorRGBA color) {
+ this.colorInt = color.asIntRGBA();
+ invalidate();
+ }
+
+ void setBitmapChar(char c) {
+ BitmapCharacterSet charSet = font.getCharSet();
+ BitmapCharacter bm = charSet.getCharacter(c, style);
+ setBitmapChar(bm);
+ }
+
+ void setBitmapChar(BitmapCharacter bitmapChar) {
+ x0 = Integer.MIN_VALUE;
+ y0 = Integer.MIN_VALUE;
+ width = Integer.MIN_VALUE;
+ height = Integer.MIN_VALUE;
+ alignX = 0;
+ alignY = 0;
+
+ BitmapCharacterSet charSet = font.getCharSet();
+ this.bitmapChar = bitmapChar;
+ if (bitmapChar != null) {
+ u0 = (float) bitmapChar.getX() / charSet.getWidth();
+ v0 = (float) bitmapChar.getY() / charSet.getHeight();
+ u1 = u0 + (float) bitmapChar.getWidth() / charSet.getWidth();
+ v1 = v0 + (float) bitmapChar.getHeight() / charSet.getHeight();
+ } else {
+ u0 = 0;
+ v0 = 0;
+ u1 = 0;
+ v1 = 0;
+ }
+ }
+
+ void setNext(LetterQuad next) {
+ this.next = next;
+ }
+
+ void update(StringBlock block) {
+ final float[] tabs = block.getTabPosition();
+ final float tabWidth = block.getTabWidth();
+ final Rectangle bound = getBound(block);
+ sizeScale = block.getSize() / font.getCharSet().getRenderedSize();
+ lineY = computeLineY(block);
+
+ if (isHead()) {
+ x0 = getBound(block).x;
+ y0 = lineY;
+ width = 0;
+ height = 0;
+ xAdvance = 0;
+ } else if (isTab()) {
+ x0 = previous.getNextX();
+ width = tabWidth;
+ y0 = lineY;
+ height = 0;
+ if (tabs != null && x0 < tabs[tabs.length-1]) {
+ for (int i = 0; i < tabs.length-1; i++) {
+ if (x0 > tabs[i] && x0 < tabs[i+1]) {
+ width = tabs[i+1] - x0;
+ }
+ }
+ }
+ xAdvance = width;
+ } else if (bitmapChar == null) {
+ x0 = getPrevious().getX1();
+ y0 = lineY;
+ width = 0;
+ height = 0;
+ xAdvance = 0;
+ } else {
+ float xOffset = bitmapChar.getXOffset() * sizeScale;
+ float yOffset = bitmapChar.getYOffset() * sizeScale;
+ xAdvance = bitmapChar.getXAdvance() * sizeScale;
+ width = bitmapChar.getWidth() * sizeScale;
+ height = bitmapChar.getHeight() * sizeScale;
+ float incrScale = rightToLeft ? -1f : 1f;
+ float kernAmount = 0f;
+
+ if (previous.isHead() || previous.eol) {
+ x0 = bound.x;
+
+ // The first letter quad will be drawn right at the first
+ // position... but it does not offset by the characters offset
+ // amount. This means that we've potentially accumulated extra
+ // pixels and the next letter won't get drawn far enough unless
+ // we add this offset back into xAdvance.. by subtracting it.
+ // This is the same thing that's done below because we've
+ // technically baked the offset in just like below. It doesn't
+ // look like it at first glance so I'm keeping it separate with
+ // this comment.
+ xAdvance -= xOffset * incrScale;
+
+ } else {
+ x0 = previous.getNextX() + xOffset * incrScale;
+
+ // Since x0 will have offset baked into it then we
+ // need to counteract that in xAdvance. This is better
+ // than removing it in getNextX() because we also need
+ // to take kerning into account below... which will also
+ // get baked in.
+ // Without this, getNextX() will return values too far to
+ // the left, for example.
+ xAdvance -= xOffset * incrScale;
+ }
+ y0 = lineY + LINE_DIR*yOffset;
+
+ // Adjust for kerning
+ BitmapCharacter lastChar = previous.getBitmapChar();
+ if (lastChar != null && block.isKerning()) {
+ kernAmount = lastChar.getKerning(c) * sizeScale;
+ x0 += kernAmount * incrScale;
+
+ // Need to unbake the kerning from xAdvance since it
+ // is baked into x0... see above.
+ //xAdvance -= kernAmount * incrScale;
+ // No, kerning is an inter-character spacing and _does_ affect
+ // all subsequent cursor positions.
+ }
+ }
+ if (isEndOfLine()) {
+ xAdvance = bound.x-x0;
+ }
+ }
+
+ /**
+ * add temporary linewrap indicator
+ */
+ void setEndOfLine() {
+ this.eol = true;
+ }
+
+ boolean isEndOfLine() {
+ return eol;
+ }
+
+ boolean isLineWrap() {
+ return !isHead() && !isTail() && bitmapChar == null && c == Character.MIN_VALUE;
+ }
+
+ private float computeLineY(StringBlock block) {
+ if (isHead()) {
+ return getBound(block).y;
+ } else if (previous.eol) {
+ return previous.getNextLine();
+ } else {
+ return previous.lineY;
+ }
+ }
+
+
+ boolean isLineStart() {
+ return x0 == 0 || (previous != null && previous.eol);
+ }
+
+ boolean isBlank() {
+ return c == ' ' || isTab();
+ }
+
+ public void storeToArrays(float[] pos, float[] tc, short[] idx, byte[] colors, int quadIdx){
+ float x = x0+alignX;
+ float y = y0-alignY;
+ float xpw = x+width;
+ float ymh = y-height;
+
+ pos[0] = x; pos[1] = y; pos[2] = 0;
+ pos[3] = x; pos[4] = ymh; pos[5] = 0;
+ pos[6] = xpw; pos[7] = ymh; pos[8] = 0;
+ pos[9] = xpw; pos[10] = y; pos[11] = 0;
+
+ float v0 = 1f - this.v0;
+ float v1 = 1f - this.v1;
+
+ tc[0] = u0; tc[1] = v0;
+ tc[2] = u0; tc[3] = v1;
+ tc[4] = u1; tc[5] = v1;
+ tc[6] = u1; tc[7] = v0;
+
+ colors[3] = (byte) (colorInt & 0xff);
+ colors[2] = (byte) ((colorInt >> 8) & 0xff);
+ colors[1] = (byte) ((colorInt >> 16) & 0xff);
+ colors[0] = (byte) ((colorInt >> 24) & 0xff);
+ System.arraycopy(colors, 0, colors, 4, 4);
+ System.arraycopy(colors, 0, colors, 8, 4);
+ System.arraycopy(colors, 0, colors, 12, 4);
+
+ short i0 = (short) (quadIdx * 4);
+ short i1 = (short) (i0 + 1);
+ short i2 = (short) (i0 + 2);
+ short i3 = (short) (i0 + 3);
+
+ idx[0] = i0; idx[1] = i1; idx[2] = i2;
+ idx[3] = i0; idx[4] = i2; idx[5] = i3;
+ }
+
+ public void appendPositions(FloatBuffer fb){
+ float sx = x0+alignX;
+ float sy = y0-alignY;
+ float ex = sx+width;
+ float ey = sy-height;
+ // NOTE: subtracting the height here
+ // because OGL's Ortho origin is at lower-left
+ fb.put(sx).put(sy).put(0f);
+ fb.put(sx).put(ey).put(0f);
+ fb.put(ex).put(ey).put(0f);
+ fb.put(ex).put(sy).put(0f);
+ }
+
+ public void appendPositions(ShortBuffer sb){
+ final float x1 = getX1();
+ final float y1 = getY1();
+ short x = (short) x0;
+ short y = (short) y0;
+ short xpw = (short) (x1);
+ short ymh = (short) (y1);
+
+ sb.put(x).put(y).put((short)0);
+ sb.put(x).put(ymh).put((short)0);
+ sb.put(xpw).put(ymh).put((short)0);
+ sb.put(xpw).put(y).put((short)0);
+ }
+
+ public void appendTexCoords(FloatBuffer fb){
+ // flip coords to be compatible with OGL
+ float v0 = 1 - this.v0;
+ float v1 = 1 - this.v1;
+
+ // upper left
+ fb.put(u0).put(v0);
+ // lower left
+ fb.put(u0).put(v1);
+ // lower right
+ fb.put(u1).put(v1);
+ // upper right
+ fb.put(u1).put(v0);
+ }
+
+ public void appendColors(ByteBuffer bb){
+ bb.putInt(colorInt);
+ bb.putInt(colorInt);
+ bb.putInt(colorInt);
+ bb.putInt(colorInt);
+ }
+
+ public void appendIndices(ShortBuffer sb, int quadIndex){
+ // each quad has 4 indices
+ short v0 = (short) (quadIndex * 4);
+ short v1 = (short) (v0 + 1);
+ short v2 = (short) (v0 + 2);
+ short v3 = (short) (v0 + 3);
+
+ sb.put(v0).put(v1).put(v2);
+ sb.put(v0).put(v2).put(v3);
+// sb.put(new short[]{ v0, v1, v2,
+// v0, v2, v3 });
+ }
+
+
+ @Override
+ public String toString() {
+ return String.valueOf(c);
+ }
+
+ void setAlignment(float alignX, float alignY) {
+ this.alignX = alignX;
+ this.alignY = alignY;
+ }
+
+ float getAlignX() {
+ return alignX;
+ }
+
+ float getAlignY() {
+ return alignY;
+ }
+
+ boolean isLineFeed() {
+ return c == '\n';
+ }
+
+ boolean isTab() {
+ return c == '\t';
+ }
+
+}
diff --git a/engine/src/core/com/jme3/font/Letters.java b/engine/src/core/com/jme3/font/Letters.java
new file mode 100644
index 0000000..dbb9ef9
--- /dev/null
+++ b/engine/src/core/com/jme3/font/Letters.java
@@ -0,0 +1,331 @@
+package com.jme3.font;
+
+import com.jme3.font.BitmapFont.Align;
+import com.jme3.font.BitmapFont.VAlign;
+import com.jme3.font.ColorTags.Range;
+import com.jme3.math.ColorRGBA;
+import java.util.LinkedList;
+
+/**
+ * Manage and align LetterQuads
+ * @author YongHoon
+ */
+class Letters {
+ private final LetterQuad head;
+ private final LetterQuad tail;
+ private final BitmapFont font;
+ private LetterQuad current;
+ private StringBlock block;
+ private float totalWidth;
+ private float totalHeight;
+ private ColorTags colorTags = new ColorTags();
+ private ColorRGBA baseColor = null;
+
+ Letters(BitmapFont font, StringBlock bound, boolean rightToLeft) {
+ final String text = bound.getText();
+ this.block = bound;
+ this.font = font;
+ head = new LetterQuad(font, rightToLeft);
+ tail = new LetterQuad(font, rightToLeft);
+ setText(text);
+ }
+
+ void setText(final String text) {
+ colorTags.setText(text);
+ String plainText = colorTags.getPlainText();
+
+ head.setNext(tail);
+ tail.setPrevious(head);
+ current = head;
+ if (text != null && plainText.length() > 0) {
+ LetterQuad l = head;
+ for (int i = 0; i < plainText.length(); i++) {
+ l = l.addNextCharacter(plainText.charAt(i));
+ if (baseColor != null) {
+ // Give the letter a default color if
+ // one has been provided.
+ l.setColor( baseColor );
+ }
+ }
+ }
+
+ LinkedList<Range> ranges = colorTags.getTags();
+ if (!ranges.isEmpty()) {
+ for (int i = 0; i < ranges.size()-1; i++) {
+ Range start = ranges.get(i);
+ Range end = ranges.get(i+1);
+ setColor(start.start, end.start, start.color);
+ }
+ Range end = ranges.getLast();
+ setColor(end.start, plainText.length(), end.color);
+ }
+
+ invalidate();
+ }
+
+ LetterQuad getHead() {
+ return head;
+ }
+
+ LetterQuad getTail() {
+ return tail;
+ }
+
+ void update() {
+ LetterQuad l = head;
+ int lineCount = 1;
+ BitmapCharacter ellipsis = font.getCharSet().getCharacter(block.getEllipsisChar());
+ float ellipsisWidth = ellipsis!=null? ellipsis.getWidth()*getScale(): 0;
+
+ while (!l.isTail()) {
+ if (l.isInvalid()) {
+ l.update(block);
+
+ if (l.isInvalid(block)) {
+ switch (block.getLineWrapMode()) {
+ case Character:
+ lineWrap(l);
+ lineCount++;
+ break;
+ case Word:
+ if (!l.isBlank()) {
+ // search last blank character before this word
+ LetterQuad blank = l;
+ while (!blank.isBlank()) {
+ if (blank.isLineStart() || blank.isHead()) {
+ lineWrap(l);
+ lineCount++;
+ blank = null;
+ break;
+ }
+ blank = blank.getPrevious();
+ }
+ if (blank != null) {
+ blank.setEndOfLine();
+ lineCount++;
+ while (blank != l) {
+ blank = blank.getNext();
+ blank.invalidate();
+ blank.update(block);
+ }
+ }
+ }
+ break;
+ case NoWrap:
+ // search last blank character before this word
+ LetterQuad cursor = l.getPrevious();
+ while (cursor.isInvalid(block, ellipsisWidth) && !cursor.isLineStart()) {
+ cursor = cursor.getPrevious();
+ }
+ cursor.setBitmapChar(ellipsis);
+ cursor.update(block);
+ cursor = cursor.getNext();
+ while (!cursor.isTail() && !cursor.isLineFeed()) {
+ cursor.setBitmapChar(null);
+ cursor.update(block);
+ cursor = cursor.getNext();
+ }
+ break;
+ }
+ }
+ } else if (current.isInvalid(block)) {
+ invalidate(current);
+ }
+ if (l.isEndOfLine()) {
+ lineCount++;
+ }
+ l = l.getNext();
+ }
+
+ align();
+ block.setLineCount(lineCount);
+ rewind();
+ }
+
+ private void align() {
+ final Align alignment = block.getAlignment();
+ final VAlign valignment = block.getVerticalAlignment();
+ if (block.getTextBox() == null || (alignment == Align.Left && valignment == VAlign.Top))
+ return;
+ LetterQuad cursor = tail.getPrevious();
+ cursor.setEndOfLine();
+ final float width = block.getTextBox().width;
+ final float height = block.getTextBox().height;
+ float lineWidth = 0;
+ float gapX = 0;
+ float gapY = 0;
+ validateSize();
+ if (totalHeight < height) { // align vertically only for no overflow
+ switch (valignment) {
+ case Top:
+ gapY = 0;
+ break;
+ case Center:
+ gapY = (height-totalHeight)*0.5f;
+ break;
+ case Bottom:
+ gapY = height-totalHeight;
+ break;
+ }
+ }
+ while (!cursor.isHead()) {
+ if (cursor.isEndOfLine()) {
+ lineWidth = cursor.getX1()-block.getTextBox().x;
+ if (alignment == Align.Center) {
+ gapX = (width-lineWidth)/2;
+ } else if (alignment == Align.Right) {
+ gapX = width-lineWidth;
+ } else {
+ gapX = 0;
+ }
+ }
+ cursor.setAlignment(gapX, gapY);
+ cursor = cursor.getPrevious();
+ }
+ }
+
+ private void lineWrap(LetterQuad l) {
+ if (l.isHead() || l.isBlank())
+ return;
+ l.getPrevious().setEndOfLine();
+ l.invalidate();
+ l.update(block); // TODO: update from l
+ }
+
+ float getCharacterX0() {
+ return current.getX0();
+ }
+
+ float getCharacterY0() {
+ return current.getY0();
+ }
+
+ float getCharacterX1() {
+ return current.getX1();
+ }
+
+ float getCharacterY1() {
+ return current.getY1();
+ }
+
+ float getCharacterAlignX() {
+ return current.getAlignX();
+ }
+
+ float getCharacterAlignY() {
+ return current.getAlignY();
+ }
+
+ float getCharacterWidth() {
+ return current.getWidth();
+ }
+
+ float getCharacterHeight() {
+ return current.getHeight();
+ }
+
+ public boolean nextCharacter() {
+ if (current.isTail())
+ return false;
+ current = current.getNext();
+ return true;
+ }
+
+ public int getCharacterSetPage() {
+ return current.getBitmapChar().getPage();
+ }
+
+ public LetterQuad getQuad() {
+ return current;
+ }
+
+ public void rewind() {
+ current = head;
+ }
+
+ public void invalidate() {
+ invalidate(head);
+ }
+
+ public void invalidate(LetterQuad cursor) {
+ totalWidth = -1;
+ totalHeight = -1;
+
+ while (!cursor.isTail() && !cursor.isInvalid()) {
+ cursor.invalidate();
+ cursor = cursor.getNext();
+ }
+ }
+
+ float getScale() {
+ return block.getSize() / font.getCharSet().getRenderedSize();
+ }
+
+ public boolean isPrintable() {
+ return current.getBitmapChar() != null;
+ }
+
+ float getTotalWidth() {
+ validateSize();
+ return totalWidth;
+ }
+
+ float getTotalHeight() {
+ validateSize();
+ return totalHeight;
+ }
+
+ void validateSize() {
+ if (totalWidth < 0) {
+ LetterQuad l = head;
+ while (!l.isTail()) {
+ totalWidth = Math.max(totalWidth, l.getX1());
+ l = l.getNext();
+ totalHeight = Math.max(totalHeight, -l.getY1());
+ }
+ }
+ }
+
+ /**
+ * @param start start index to set style. inclusive.
+ * @param end end index to set style. EXCLUSIVE.
+ * @param style
+ */
+ void setStyle(int start, int end, int style) {
+ LetterQuad cursor = head.getNext();
+ while (!cursor.isTail()) {
+ if (cursor.getIndex() >= start && cursor.getIndex() < end) {
+ cursor.setStyle(style);
+ }
+ cursor = cursor.getNext();
+ }
+ }
+
+ /**
+ * Sets the base color for all new letter quads and resets
+ * the color of existing letter quads.
+ */
+ void setColor( ColorRGBA color ) {
+ baseColor = color;
+ setColor( 0, block.getText().length(), color );
+ }
+
+ ColorRGBA getBaseColor() {
+ return baseColor;
+ }
+
+ /**
+ * @param start start index to set style. inclusive.
+ * @param end end index to set style. EXCLUSIVE.
+ * @param color
+ */
+ void setColor(int start, int end, ColorRGBA color) {
+ LetterQuad cursor = head.getNext();
+ while (!cursor.isTail()) {
+ if (cursor.getIndex() >= start && cursor.getIndex() < end) {
+ cursor.setColor(color);
+ }
+ cursor = cursor.getNext();
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/font/LineWrapMode.java b/engine/src/core/com/jme3/font/LineWrapMode.java
new file mode 100644
index 0000000..3c77d6e
--- /dev/null
+++ b/engine/src/core/com/jme3/font/LineWrapMode.java
@@ -0,0 +1,11 @@
+package com.jme3.font;
+
+/**
+ * Line-wrap type for BitmapText
+ * @author YongHoon
+ */
+public enum LineWrapMode {
+ NoWrap,
+ Character,
+ Word
+}
diff --git a/engine/src/core/com/jme3/font/Rectangle.java b/engine/src/core/com/jme3/font/Rectangle.java
new file mode 100644
index 0000000..33b47c5
--- /dev/null
+++ b/engine/src/core/com/jme3/font/Rectangle.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font;
+
+/**
+ * Defines a rectangle that can constrict a text paragraph.
+ * @author dhdd
+ */
+public class Rectangle implements Cloneable {
+
+ public final float x, y, width, height;
+
+ /**
+ *
+ * @param x the X value of the upper left corner of the rectangle
+ * @param y the Y value of the upper left corner of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public Rectangle(float x, float y, float width, float height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public Rectangle clone(){
+ try {
+ return (Rectangle) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/font/StringBlock.java b/engine/src/core/com/jme3/font/StringBlock.java
new file mode 100644
index 0000000..9f6f055
--- /dev/null
+++ b/engine/src/core/com/jme3/font/StringBlock.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font;
+
+import com.jme3.font.BitmapFont.Align;
+import com.jme3.font.BitmapFont.VAlign;
+import com.jme3.math.ColorRGBA;
+
+/**
+ * Defines a String that is to be drawn in one block that can be constrained by a {@link Rectangle}. Also holds
+ * formatting information for the StringBlock
+ *
+ * @author dhdd
+ */
+class StringBlock implements Cloneable {
+
+ private String text;
+ private Rectangle textBox;
+ private Align alignment = Align.Left;
+ private VAlign valignment = VAlign.Top;
+ private float size;
+ private ColorRGBA color = new ColorRGBA(ColorRGBA.White);
+ private boolean kerning;
+ private int lineCount;
+ private LineWrapMode wrapType = LineWrapMode.Word;
+ private float[] tabPos;
+ private float tabWidth = 50;
+ private char ellipsisChar = 0x2026;
+
+ /**
+ *
+ * @param text the text that the StringBlock will hold
+ * @param textBox the rectangle that constrains the text
+ * @param alignment the initial alignment of the text
+ * @param size the size in pixels (vertical size of a single line)
+ * @param color the initial color of the text
+ * @param kerning
+ */
+ StringBlock(String text, Rectangle textBox, BitmapFont.Align alignment, float size, ColorRGBA color,
+ boolean kerning) {
+ this.text = text;
+ this.textBox = textBox;
+ this.alignment = alignment;
+ this.size = size;
+ this.color.set(color);
+ this.kerning = kerning;
+ }
+
+ StringBlock(){
+ this.text = "";
+ this.textBox = null;
+ this.alignment = Align.Left;
+ this.size = 100;
+ this.color.set(ColorRGBA.White);
+ this.kerning = true;
+ }
+
+ @Override
+ public StringBlock clone(){
+ try {
+ StringBlock clone = (StringBlock) super.clone();
+ clone.color = color.clone();
+ if (textBox != null)
+ clone.textBox = textBox.clone();
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ String getText() {
+ return text;
+ }
+
+ void setText(String text){
+ this.text = text == null ? "" : text;
+ }
+
+ Rectangle getTextBox() {
+ return textBox;
+ }
+
+ void setTextBox(Rectangle textBox) {
+ this.textBox = textBox;
+ }
+
+ BitmapFont.Align getAlignment() {
+ return alignment;
+ }
+
+ BitmapFont.VAlign getVerticalAlignment() {
+ return valignment;
+ }
+
+ void setAlignment(BitmapFont.Align alignment) {
+ this.alignment = alignment;
+ }
+
+ void setVerticalAlignment(BitmapFont.VAlign alignment) {
+ this.valignment = alignment;
+ }
+
+ float getSize() {
+ return size;
+ }
+
+ void setSize(float size) {
+ this.size = size;
+ }
+
+ ColorRGBA getColor() {
+ return color;
+ }
+
+ void setColor(ColorRGBA color) {
+ this.color.set(color);
+ }
+
+ boolean isKerning() {
+ return kerning;
+ }
+
+ void setKerning(boolean kerning) {
+ this.kerning = kerning;
+ }
+
+ int getLineCount() {
+ return lineCount;
+ }
+
+ void setLineCount(int lineCount) {
+ this.lineCount = lineCount;
+ }
+
+ LineWrapMode getLineWrapMode() {
+ return wrapType;
+ }
+
+ /**
+ * available only when bounding is set. <code>setBox()</code> method call is needed in advance.
+ * @param wrap true when word need not be split at the end of the line.
+ */
+ void setLineWrapMode(LineWrapMode wrap) {
+ this.wrapType = wrap;
+ }
+
+ void setTabWidth(float tabWidth) {
+ this.tabWidth = tabWidth;
+ }
+
+ void setTabPosition(float[] tabs) {
+ this.tabPos = tabs;
+ }
+
+ float getTabWidth() {
+ return tabWidth;
+ }
+
+ float[] getTabPosition() {
+ return tabPos;
+ }
+
+ void setEllipsisChar(char c) {
+ this.ellipsisChar = c;
+ }
+
+ int getEllipsisChar() {
+ return ellipsisChar;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/input/ChaseCamera.java b/engine/src/core/com/jme3/input/ChaseCamera.java
new file mode 100644
index 0000000..b3b649d
--- /dev/null
+++ b/engine/src/core/com/jme3/input/ChaseCamera.java
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.input;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.input.controls.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import java.io.IOException;
+
+/**
+ * A camera that follows a spatial and can turn around it by dragging the mouse
+ * @author nehon
+ */
+public class ChaseCamera implements ActionListener, AnalogListener, Control {
+
+ protected Spatial target = null;
+ protected float minVerticalRotation = 0.00f;
+ protected float maxVerticalRotation = FastMath.PI / 2;
+ protected float minDistance = 1.0f;
+ protected float maxDistance = 40.0f;
+ protected float distance = 20;
+ protected float zoomSpeed = 2f;
+ protected float rotationSpeed = 1.0f;
+ protected float rotation = 0;
+ protected float trailingRotationInertia = 0.05f;
+ protected float zoomSensitivity = 5f;
+ protected float rotationSensitivity = 5f;
+ protected float chasingSensitivity = 5f;
+ protected float trailingSensitivity = 0.5f;
+ protected float vRotation = FastMath.PI / 6;
+ protected boolean smoothMotion = false;
+ protected boolean trailingEnabled = true;
+ protected float rotationLerpFactor = 0;
+ protected float trailingLerpFactor = 0;
+ protected boolean rotating = false;
+ protected boolean vRotating = false;
+ protected float targetRotation = rotation;
+ protected InputManager inputManager;
+ protected Vector3f initialUpVec;
+ protected float targetVRotation = vRotation;
+ protected float vRotationLerpFactor = 0;
+ protected float targetDistance = distance;
+ protected float distanceLerpFactor = 0;
+ protected boolean zooming = false;
+ protected boolean trailing = false;
+ protected boolean chasing = false;
+ protected boolean canRotate;
+ protected float offsetDistance = 0.002f;
+ protected Vector3f prevPos;
+ protected boolean targetMoves = false;
+ protected boolean enabled = true;
+ protected Camera cam = null;
+ protected final Vector3f targetDir = new Vector3f();
+ protected float previousTargetRotation;
+ protected final Vector3f pos = new Vector3f();
+ protected Vector3f targetLocation = new Vector3f(0, 0, 0);
+ protected boolean dragToRotate = true;
+ protected Vector3f lookAtOffset = new Vector3f(0, 0, 0);
+ protected boolean leftClickRotate = true;
+ protected boolean rightClickRotate = true;
+ protected Vector3f temp = new Vector3f(0, 0, 0);
+ protected boolean invertYaxis = false;
+ protected boolean invertXaxis = false;
+ protected final static String ChaseCamDown = "ChaseCamDown";
+ protected final static String ChaseCamUp = "ChaseCamUp";
+ protected final static String ChaseCamZoomIn = "ChaseCamZoomIn";
+ protected final static String ChaseCamZoomOut = "ChaseCamZoomOut";
+ protected final static String ChaseCamMoveLeft = "ChaseCamMoveLeft";
+ protected final static String ChaseCamMoveRight = "ChaseCamMoveRight";
+ protected final static String ChaseCamToggleRotate = "ChaseCamToggleRotate";
+
+ /**
+ * Constructs the chase camera
+ * @param cam the application camera
+ * @param target the spatial to follow
+ */
+ public ChaseCamera(Camera cam, final Spatial target) {
+ this(cam);
+ target.addControl(this);
+ }
+
+ /**
+ * Constructs the chase camera
+ * if you use this constructor you have to attach the cam later to a spatial
+ * doing spatial.addControl(chaseCamera);
+ * @param cam the application camera
+ */
+ public ChaseCamera(Camera cam) {
+ this.cam = cam;
+ initialUpVec = cam.getUp().clone();
+ }
+
+ /**
+ * Constructs the chase camera, and registers inputs
+ * if you use this constructor you have to attach the cam later to a spatial
+ * doing spatial.addControl(chaseCamera);
+ * @param cam the application camera
+ * @param inputManager the inputManager of the application to register inputs
+ */
+ public ChaseCamera(Camera cam, InputManager inputManager) {
+ this(cam);
+ registerWithInput(inputManager);
+ }
+
+ /**
+ * Constructs the chase camera, and registers inputs
+ * @param cam the application camera
+ * @param target the spatial to follow
+ * @param inputManager the inputManager of the application to register inputs
+ */
+ public ChaseCamera(Camera cam, final Spatial target, InputManager inputManager) {
+ this(cam, target);
+ registerWithInput(inputManager);
+ }
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (dragToRotate) {
+ if (name.equals(ChaseCamToggleRotate) && enabled) {
+ if (keyPressed) {
+ canRotate = true;
+ inputManager.setCursorVisible(false);
+ } else {
+ canRotate = false;
+ inputManager.setCursorVisible(true);
+ }
+ }
+ }
+
+ }
+ private boolean zoomin;
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals(ChaseCamMoveLeft)) {
+ rotateCamera(-value);
+ } else if (name.equals(ChaseCamMoveRight)) {
+ rotateCamera(value);
+ } else if (name.equals(ChaseCamUp)) {
+ vRotateCamera(value);
+ } else if (name.equals(ChaseCamDown)) {
+ vRotateCamera(-value);
+ } else if (name.equals(ChaseCamZoomIn)) {
+ zoomCamera(-value);
+ if (zoomin == false) {
+ distanceLerpFactor = 0;
+ }
+ zoomin = true;
+ } else if (name.equals(ChaseCamZoomOut)) {
+ zoomCamera(+value);
+ if (zoomin == true) {
+ distanceLerpFactor = 0;
+ }
+ zoomin = false;
+ }
+ }
+
+ /**
+ * Registers inputs with the input manager
+ * @param inputManager
+ */
+ public final void registerWithInput(InputManager inputManager) {
+
+ String[] inputs = {ChaseCamToggleRotate,
+ ChaseCamDown,
+ ChaseCamUp,
+ ChaseCamMoveLeft,
+ ChaseCamMoveRight,
+ ChaseCamZoomIn,
+ ChaseCamZoomOut};
+
+ this.inputManager = inputManager;
+ if (!invertYaxis) {
+ inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
+ inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
+ } else {
+ inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
+ inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
+ }
+ inputManager.addMapping(ChaseCamZoomIn, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
+ inputManager.addMapping(ChaseCamZoomOut, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
+ if(!invertXaxis){
+ inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));
+ inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));
+ }else{
+ inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));
+ inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));
+ }
+ inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
+
+ inputManager.addListener(this, inputs);
+ }
+
+ /**
+ * Sets custom triggers for toggleing the rotation of the cam
+ * deafult are
+ * new MouseButtonTrigger(MouseInput.BUTTON_LEFT) left mouse button
+ * new MouseButtonTrigger(MouseInput.BUTTON_RIGHT) right mouse button
+ * @param triggers
+ */
+ public void setToggleRotationTrigger(Trigger... triggers) {
+ inputManager.deleteMapping(ChaseCamToggleRotate);
+ inputManager.addMapping(ChaseCamToggleRotate, triggers);
+ inputManager.addListener(this, ChaseCamToggleRotate);
+ }
+
+ /**
+ * Sets custom triggers for zomming in the cam
+ * default is
+ * new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true) mouse wheel up
+ * @param triggers
+ */
+ public void setZoomInTrigger(Trigger... triggers) {
+ inputManager.deleteMapping(ChaseCamZoomIn);
+ inputManager.addMapping(ChaseCamZoomIn, triggers);
+ inputManager.addListener(this, ChaseCamZoomIn);
+ }
+
+ /**
+ * Sets custom triggers for zomming out the cam
+ * default is
+ * new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false) mouse wheel down
+ * @param triggers
+ */
+ public void setZoomOutTrigger(Trigger... triggers) {
+ inputManager.deleteMapping(ChaseCamZoomOut);
+ inputManager.addMapping(ChaseCamZoomOut, triggers);
+ inputManager.addListener(this, ChaseCamZoomOut);
+ }
+
+ private void computePosition() {
+
+ float hDistance = (distance) * FastMath.sin((FastMath.PI / 2) - vRotation);
+ pos.set(hDistance * FastMath.cos(rotation), (distance) * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));
+ pos.addLocal(target.getWorldTranslation());
+ }
+
+ //rotate the camera around the target on the horizontal plane
+ private void rotateCamera(float value) {
+ if (!canRotate || !enabled) {
+ return;
+ }
+ rotating = true;
+ targetRotation += value * rotationSpeed;
+
+
+ }
+
+ //move the camera toward or away the target
+ private void zoomCamera(float value) {
+ if (!enabled) {
+ return;
+ }
+
+ zooming = true;
+ targetDistance += value * zoomSpeed;
+ if (targetDistance > maxDistance) {
+ targetDistance = maxDistance;
+ }
+ if (targetDistance < minDistance) {
+ targetDistance = minDistance;
+ }
+ if ((targetVRotation < minVerticalRotation) && (targetDistance > (minDistance + 1.0f))) {
+ targetVRotation = minVerticalRotation;
+ }
+ }
+
+ //rotate the camera around the target on the vertical plane
+ private void vRotateCamera(float value) {
+ if (!canRotate || !enabled) {
+ return;
+ }
+ vRotating = true;
+ targetVRotation += value * rotationSpeed;
+ if (targetVRotation > maxVerticalRotation) {
+ targetVRotation = maxVerticalRotation;
+ }
+ if ((targetVRotation < minVerticalRotation) && (targetDistance > (minDistance + 1.0f))) {
+ targetVRotation = minVerticalRotation;
+ }
+ }
+
+ /**
+ * Updates the camera, should only be called internally
+ */
+ protected void updateCamera(float tpf) {
+ if (enabled) {
+ targetLocation.set(target.getWorldTranslation()).addLocal(lookAtOffset);
+ if (smoothMotion) {
+
+ //computation of target direction
+ targetDir.set(targetLocation).subtractLocal(prevPos);
+ float dist = targetDir.length();
+
+ //Low pass filtering on the target postition to avoid shaking when physics are enabled.
+ if (offsetDistance < dist) {
+ //target moves, start chasing.
+ chasing = true;
+ //target moves, start trailing if it has to.
+ if (trailingEnabled) {
+ trailing = true;
+ }
+ //target moves...
+ targetMoves = true;
+ } else {
+ //if target was moving, we compute a slight offset in rotation to avoid a rought stop of the cam
+ //We do not if the player is rotationg the cam
+ if (targetMoves && !canRotate) {
+ if (targetRotation - rotation > trailingRotationInertia) {
+ targetRotation = rotation + trailingRotationInertia;
+ } else if (targetRotation - rotation < -trailingRotationInertia) {
+ targetRotation = rotation - trailingRotationInertia;
+ }
+ }
+ //Target stops
+ targetMoves = false;
+ }
+
+ //the user is rotating the cam by dragging the mouse
+ if (canRotate) {
+ //reseting the trailing lerp factor
+ trailingLerpFactor = 0;
+ //stop trailing user has the control
+ trailing = false;
+ }
+
+
+ if (trailingEnabled && trailing) {
+ if (targetMoves) {
+ //computation if the inverted direction of the target
+ Vector3f a = targetDir.negate().normalizeLocal();
+ //the x unit vector
+ Vector3f b = Vector3f.UNIT_X;
+ //2d is good enough
+ a.y = 0;
+ //computation of the rotation angle between the x axis and the trail
+ if (targetDir.z > 0) {
+ targetRotation = FastMath.TWO_PI - FastMath.acos(a.dot(b));
+ } else {
+ targetRotation = FastMath.acos(a.dot(b));
+ }
+ if (targetRotation - rotation > FastMath.PI || targetRotation - rotation < -FastMath.PI) {
+ targetRotation -= FastMath.TWO_PI;
+ }
+
+ //if there is an important change in the direction while trailing reset of the lerp factor to avoid jumpy movements
+ if (targetRotation != previousTargetRotation && FastMath.abs(targetRotation - previousTargetRotation) > FastMath.PI / 8) {
+ trailingLerpFactor = 0;
+ }
+ previousTargetRotation = targetRotation;
+ }
+ //computing lerp factor
+ trailingLerpFactor = Math.min(trailingLerpFactor + tpf * tpf * trailingSensitivity, 1);
+ //computing rotation by linear interpolation
+ rotation = FastMath.interpolateLinear(trailingLerpFactor, rotation, targetRotation);
+
+ //if the rotation is near the target rotation we're good, that's over
+ if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {
+ trailing = false;
+ trailingLerpFactor = 0;
+ }
+ }
+
+ //linear interpolation of the distance while chasing
+ if (chasing) {
+ distance = temp.set(targetLocation).subtractLocal(cam.getLocation()).length();
+ distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * chasingSensitivity * 0.05f), 1);
+ distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);
+ if (targetDistance + 0.01f >= distance && targetDistance - 0.01f <= distance) {
+ distanceLerpFactor = 0;
+ chasing = false;
+ }
+ }
+
+ //linear interpolation of the distance while zooming
+ if (zooming) {
+ distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * zoomSensitivity), 1);
+ distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);
+ if (targetDistance + 0.1f >= distance && targetDistance - 0.1f <= distance) {
+ zooming = false;
+ distanceLerpFactor = 0;
+ }
+ }
+
+ //linear interpolation of the rotation while rotating horizontally
+ if (rotating) {
+ rotationLerpFactor = Math.min(rotationLerpFactor + tpf * tpf * rotationSensitivity, 1);
+ rotation = FastMath.interpolateLinear(rotationLerpFactor, rotation, targetRotation);
+ if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {
+ rotating = false;
+ rotationLerpFactor = 0;
+ }
+ }
+
+ //linear interpolation of the rotation while rotating vertically
+ if (vRotating) {
+ vRotationLerpFactor = Math.min(vRotationLerpFactor + tpf * tpf * rotationSensitivity, 1);
+ vRotation = FastMath.interpolateLinear(vRotationLerpFactor, vRotation, targetVRotation);
+ if (targetVRotation + 0.01f >= vRotation && targetVRotation - 0.01f <= vRotation) {
+ vRotating = false;
+ vRotationLerpFactor = 0;
+ }
+ }
+ //computing the position
+ computePosition();
+ //setting the position at last
+ cam.setLocation(pos.addLocal(lookAtOffset));
+ } else {
+ //easy no smooth motion
+ vRotation = targetVRotation;
+ rotation = targetRotation;
+ distance = targetDistance;
+ computePosition();
+ cam.setLocation(pos.addLocal(lookAtOffset));
+ }
+ //keeping track on the previous position of the target
+ prevPos.set(targetLocation);
+
+ //the cam looks at the target
+ cam.lookAt(targetLocation, initialUpVec);
+
+ }
+ }
+
+ /**
+ * Return the enabled/disabled state of the camera
+ * @return true if the camera is enabled
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Enable or disable the camera
+ * @param enabled true to enable
+ */
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (!enabled) {
+ canRotate = false; // reset this flag in-case it was on before
+ }
+ }
+
+ /**
+ * Returns the max zoom distance of the camera (default is 40)
+ * @return maxDistance
+ */
+ public float getMaxDistance() {
+ return maxDistance;
+ }
+
+ /**
+ * Sets the max zoom distance of the camera (default is 40)
+ * @param maxDistance
+ */
+ public void setMaxDistance(float maxDistance) {
+ this.maxDistance = maxDistance;
+ }
+
+ /**
+ * Returns the min zoom distance of the camera (default is 1)
+ * @return minDistance
+ */
+ public float getMinDistance() {
+ return minDistance;
+ }
+
+ /**
+ * Sets the min zoom distance of the camera (default is 1)
+ * @return minDistance
+ */
+ public void setMinDistance(float minDistance) {
+ this.minDistance = minDistance;
+ }
+
+ /**
+ * clone this camera for a spatial
+ * @param spatial
+ * @return
+ */
+ public Control cloneForSpatial(Spatial spatial) {
+ ChaseCamera cc = new ChaseCamera(cam, spatial, inputManager);
+ cc.setMaxDistance(getMaxDistance());
+ cc.setMinDistance(getMinDistance());
+ return cc;
+ }
+
+ /**
+ * Sets the spacial for the camera control, should only be used internally
+ * @param spatial
+ */
+ public void setSpatial(Spatial spatial) {
+ target = spatial;
+ if (spatial == null) {
+ return;
+ }
+ computePosition();
+ prevPos = new Vector3f(target.getWorldTranslation());
+ cam.setLocation(pos);
+ }
+
+ /**
+ * update the camera control, should only be used internally
+ * @param tpf
+ */
+ public void update(float tpf) {
+ updateCamera(tpf);
+ }
+
+ /**
+ * renders the camera control, should only be used internally
+ * @param rm
+ * @param vp
+ */
+ public void render(RenderManager rm, ViewPort vp) {
+ //nothing to render
+ }
+
+ /**
+ * Write the camera
+ * @param ex the exporter
+ * @throws IOException
+ */
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(maxDistance, "maxDistance", 40);
+ capsule.write(minDistance, "minDistance", 1);
+ }
+
+ /**
+ * Read the camera
+ * @param im
+ * @throws IOException
+ */
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ maxDistance = ic.readFloat("maxDistance", 40);
+ minDistance = ic.readFloat("minDistance", 1);
+ }
+
+ /**
+ * returns the maximal vertical rotation angle of the camera around the target
+ * @return
+ */
+ public float getMaxVerticalRotation() {
+ return maxVerticalRotation;
+ }
+
+ /**
+ * sets the maximal vertical rotation angle of the camera around the target default is Pi/2;
+ * @param maxVerticalRotation
+ */
+ public void setMaxVerticalRotation(float maxVerticalRotation) {
+ this.maxVerticalRotation = maxVerticalRotation;
+ }
+
+ /**
+ * returns the minimal vertical rotation angle of the camera around the target
+ * @return
+ */
+ public float getMinVerticalRotation() {
+ return minVerticalRotation;
+ }
+
+ /**
+ * sets the minimal vertical rotation angle of the camera around the target default is 0;
+ * @param minHeight
+ */
+ public void setMinVerticalRotation(float minHeight) {
+ this.minVerticalRotation = minHeight;
+ }
+
+ /**
+ * returns true is smmoth motion is enabled for this chase camera
+ * @return
+ */
+ public boolean isSmoothMotion() {
+ return smoothMotion;
+ }
+
+ /**
+ * Enables smooth motion for this chase camera
+ * @param smoothMotion
+ */
+ public void setSmoothMotion(boolean smoothMotion) {
+ this.smoothMotion = smoothMotion;
+ }
+
+ /**
+ * returns the chasing sensitivity
+ * @return
+ */
+ public float getChasingSensitivity() {
+ return chasingSensitivity;
+ }
+
+ /**
+ *
+ * Sets the chasing sensitivity, the lower the value the slower the camera will follow the target when it moves
+ * default is 5
+ * Only has an effect if smoothMotion is set to true and trailing is enabled
+ * @param chasingSensitivity
+ */
+ public void setChasingSensitivity(float chasingSensitivity) {
+ this.chasingSensitivity = chasingSensitivity;
+ }
+
+ /**
+ * Returns the rotation sensitivity
+ * @return
+ */
+ public float getRotationSensitivity() {
+ return rotationSensitivity;
+ }
+
+ /**
+ * Sets the rotation sensitivity, the lower the value the slower the camera will rotates around the target when draging with the mouse
+ * default is 5, values over 5 should have no effect.
+ * If you want a significant slow down try values below 1.
+ * Only has an effect if smoothMotion is set to true
+ * @param rotationSensitivity
+ */
+ public void setRotationSensitivity(float rotationSensitivity) {
+ this.rotationSensitivity = rotationSensitivity;
+ }
+
+ /**
+ * returns true if the trailing is enabled
+ * @return
+ */
+ public boolean isTrailingEnabled() {
+ return trailingEnabled;
+ }
+
+ /**
+ * Enable the camera trailing : The camera smoothly go in the targets trail when it moves.
+ * Only has an effect if smoothMotion is set to true
+ * @param trailingEnabled
+ */
+ public void setTrailingEnabled(boolean trailingEnabled) {
+ this.trailingEnabled = trailingEnabled;
+ }
+
+ /**
+ *
+ * returns the trailing rotation inertia
+ * @return
+ */
+ public float getTrailingRotationInertia() {
+ return trailingRotationInertia;
+ }
+
+ /**
+ * Sets the trailing rotation inertia : default is 0.1. This prevent the camera to roughtly stop when the target stops moving
+ * before the camera reached the trail position.
+ * Only has an effect if smoothMotion is set to true and trailing is enabled
+ * @param trailingRotationInertia
+ */
+ public void setTrailingRotationInertia(float trailingRotationInertia) {
+ this.trailingRotationInertia = trailingRotationInertia;
+ }
+
+ /**
+ * returns the trailing sensitivity
+ * @return
+ */
+ public float getTrailingSensitivity() {
+ return trailingSensitivity;
+ }
+
+ /**
+ * Only has an effect if smoothMotion is set to true and trailing is enabled
+ * Sets the trailing sensitivity, the lower the value, the slower the camera will go in the target trail when it moves.
+ * default is 0.5;
+ * @param trailingSensitivity
+ */
+ public void setTrailingSensitivity(float trailingSensitivity) {
+ this.trailingSensitivity = trailingSensitivity;
+ }
+
+ /**
+ * returns the zoom sensitivity
+ * @return
+ */
+ public float getZoomSensitivity() {
+ return zoomSensitivity;
+ }
+
+ /**
+ * Sets the zoom sensitivity, the lower the value, the slower the camera will zoom in and out.
+ * default is 5.
+ * @param zoomSensitivity
+ */
+ public void setZoomSensitivity(float zoomSensitivity) {
+ this.zoomSensitivity = zoomSensitivity;
+ }
+
+ /**
+ * Sets the default distance at start of applicaiton
+ * @param defaultDistance
+ */
+ public void setDefaultDistance(float defaultDistance) {
+ distance = defaultDistance;
+ targetDistance = distance;
+ }
+
+ /**
+ * sets the default horizontal rotation of the camera at start of the application
+ * @param angle
+ */
+ public void setDefaultHorizontalRotation(float angle) {
+ rotation = angle;
+ targetRotation = angle;
+ }
+
+ /**
+ * sets the default vertical rotation of the camera at start of the application
+ * @param angle
+ */
+ public void setDefaultVerticalRotation(float angle) {
+ vRotation = angle;
+ targetVRotation = angle;
+ }
+
+ /**
+ * @return If drag to rotate feature is enabled.
+ *
+ * @see FlyByCamera#setDragToRotate(boolean)
+ */
+ public boolean isDragToRotate() {
+ return dragToRotate;
+ }
+
+ /**
+ * @param dragToRotate When true, the user must hold the mouse button
+ * and drag over the screen to rotate the camera, and the cursor is
+ * visible until dragged. Otherwise, the cursor is invisible at all times
+ * and holding the mouse button is not needed to rotate the camera.
+ * This feature is disabled by default.
+ */
+ public void setDragToRotate(boolean dragToRotate) {
+ this.dragToRotate = dragToRotate;
+ this.canRotate = !dragToRotate;
+ inputManager.setCursorVisible(dragToRotate);
+ }
+
+ /**
+ * return the current distance from the camera to the target
+ * @return
+ */
+ public float getDistanceToTarget() {
+ return distance;
+ }
+
+ /**
+ * returns the current horizontal rotation around the target in radians
+ * @return
+ */
+ public float getHorizontalRotation() {
+ return rotation;
+ }
+
+ /**
+ * returns the current vertical rotation around the target in radians.
+ * @return
+ */
+ public float getVerticalRotation() {
+ return vRotation;
+ }
+
+ /**
+ * returns the offset from the target's position where the camera looks at
+ * @return
+ */
+ public Vector3f getLookAtOffset() {
+ return lookAtOffset;
+ }
+
+ /**
+ * Sets the offset from the target's position where the camera looks at
+ * @param lookAtOffset
+ */
+ public void setLookAtOffset(Vector3f lookAtOffset) {
+ this.lookAtOffset = lookAtOffset;
+ }
+
+ /**
+ * Sets the up vector of the camera used for the lookAt on the target
+ * @param up
+ */
+ public void setUpVector(Vector3f up){
+ initialUpVec=up;
+ }
+
+ /**
+ * Returns the up vector of the camera used for the lookAt on the target
+ * @return
+ */
+ public Vector3f getUpVector(){
+ return initialUpVec;
+ }
+
+ /**
+ * invert the vertical axis movement of the mouse
+ * @param invertYaxis
+ */
+ public void setInvertVerticalAxis(boolean invertYaxis) {
+ this.invertYaxis = invertYaxis;
+ inputManager.deleteMapping(ChaseCamDown);
+ inputManager.deleteMapping(ChaseCamUp);
+ if (!invertYaxis) {
+ inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
+ inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
+ } else {
+ inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
+ inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
+ }
+ inputManager.addListener(this, ChaseCamDown, ChaseCamUp);
+ }
+
+ /**
+ * invert the Horizontal axis movement of the mouse
+ * @param invertXaxis
+ */
+ public void setInvertHorizontalAxis(boolean invertXaxis) {
+ this.invertXaxis = invertXaxis;
+ inputManager.deleteMapping(ChaseCamMoveLeft);
+ inputManager.deleteMapping(ChaseCamMoveRight);
+ if(!invertXaxis){
+ inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));
+ inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));
+ }else{
+ inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));
+ inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));
+ }
+ inputManager.addListener(this, ChaseCamMoveLeft, ChaseCamMoveRight);
+ }
+}
diff --git a/engine/src/core/com/jme3/input/FlyByCamera.java b/engine/src/core/com/jme3/input/FlyByCamera.java
new file mode 100644
index 0000000..7b439ef
--- /dev/null
+++ b/engine/src/core/com/jme3/input/FlyByCamera.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+import com.jme3.collision.MotionAllowedListener;
+import com.jme3.input.controls.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+
+/**
+ * A first person view camera controller.
+ * After creation, you must register the camera controller with the
+ * dispatcher using #registerWithDispatcher().
+ *
+ * Controls:
+ * - Move the mouse to rotate the camera
+ * - Mouse wheel for zooming in or out
+ * - WASD keys for moving forward/backward and strafing
+ * - QZ keys raise or lower the camera
+ */
+public class FlyByCamera implements AnalogListener, ActionListener {
+
+ private static String[] mappings = new String[]{
+ "FLYCAM_Left",
+ "FLYCAM_Right",
+ "FLYCAM_Up",
+ "FLYCAM_Down",
+
+ "FLYCAM_StrafeLeft",
+ "FLYCAM_StrafeRight",
+ "FLYCAM_Forward",
+ "FLYCAM_Backward",
+
+ "FLYCAM_ZoomIn",
+ "FLYCAM_ZoomOut",
+ "FLYCAM_RotateDrag",
+
+ "FLYCAM_Rise",
+ "FLYCAM_Lower"
+ };
+
+ protected Camera cam;
+ protected Vector3f initialUpVec;
+ protected float rotationSpeed = 1f;
+ protected float moveSpeed = 3f;
+ protected MotionAllowedListener motionAllowed = null;
+ protected boolean enabled = true;
+ protected boolean dragToRotate = false;
+ protected boolean canRotate = false;
+ protected InputManager inputManager;
+
+ /**
+ * Creates a new FlyByCamera to control the given Camera object.
+ * @param cam
+ */
+ public FlyByCamera(Camera cam){
+ this.cam = cam;
+ initialUpVec = cam.getUp().clone();
+ }
+
+ /**
+ * Sets the up vector that should be used for the camera.
+ * @param upVec
+ */
+ public void setUpVector(Vector3f upVec) {
+ initialUpVec.set(upVec);
+ }
+
+ public void setMotionAllowedListener(MotionAllowedListener listener){
+ this.motionAllowed = listener;
+ }
+
+ /**
+ * Sets the move speed. The speed is given in world units per second.
+ * @param moveSpeed
+ */
+ public void setMoveSpeed(float moveSpeed){
+ this.moveSpeed = moveSpeed;
+ }
+
+ /**
+ * Sets the rotation speed.
+ * @param rotationSpeed
+ */
+ public void setRotationSpeed(float rotationSpeed){
+ this.rotationSpeed = rotationSpeed;
+ }
+
+ /**
+ * @param enable If false, the camera will ignore input.
+ */
+ public void setEnabled(boolean enable){
+ if (enabled && !enable){
+ if (inputManager!= null && (!dragToRotate || (dragToRotate && canRotate))){
+ inputManager.setCursorVisible(true);
+ }
+ }
+ enabled = enable;
+ }
+
+ /**
+ * @return If enabled
+ * @see FlyByCamera#setEnabled(boolean)
+ */
+ public boolean isEnabled(){
+ return enabled;
+ }
+
+ /**
+ * @return If drag to rotate feature is enabled.
+ *
+ * @see FlyByCamera#setDragToRotate(boolean)
+ */
+ public boolean isDragToRotate() {
+ return dragToRotate;
+ }
+
+ /**
+ * Set if drag to rotate mode is enabled.
+ *
+ * When true, the user must hold the mouse button
+ * and drag over the screen to rotate the camera, and the cursor is
+ * visible until dragged. Otherwise, the cursor is invisible at all times
+ * and holding the mouse button is not needed to rotate the camera.
+ * This feature is disabled by default.
+ *
+ * @param dragToRotate True if drag to rotate mode is enabled.
+ */
+ public void setDragToRotate(boolean dragToRotate) {
+ this.dragToRotate = dragToRotate;
+ if (inputManager != null) {
+ inputManager.setCursorVisible(dragToRotate);
+ }
+ }
+
+ /**
+ * Registers the FlyByCamera to receive input events from the provided
+ * Dispatcher.
+ * @param inputManager
+ */
+ public void registerWithInput(InputManager inputManager){
+ this.inputManager = inputManager;
+
+ // both mouse and button - rotation of cam
+ inputManager.addMapping("FLYCAM_Left", new MouseAxisTrigger(MouseInput.AXIS_X, true),
+ new KeyTrigger(KeyInput.KEY_LEFT));
+
+ inputManager.addMapping("FLYCAM_Right", new MouseAxisTrigger(MouseInput.AXIS_X, false),
+ new KeyTrigger(KeyInput.KEY_RIGHT));
+
+ inputManager.addMapping("FLYCAM_Up", new MouseAxisTrigger(MouseInput.AXIS_Y, false),
+ new KeyTrigger(KeyInput.KEY_UP));
+
+ inputManager.addMapping("FLYCAM_Down", new MouseAxisTrigger(MouseInput.AXIS_Y, true),
+ new KeyTrigger(KeyInput.KEY_DOWN));
+
+ // mouse only - zoom in/out with wheel, and rotate drag
+ inputManager.addMapping("FLYCAM_ZoomIn", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
+ inputManager.addMapping("FLYCAM_ZoomOut", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
+ inputManager.addMapping("FLYCAM_RotateDrag", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+
+ // keyboard only WASD for movement and WZ for rise/lower height
+ inputManager.addMapping("FLYCAM_StrafeLeft", new KeyTrigger(KeyInput.KEY_A));
+ inputManager.addMapping("FLYCAM_StrafeRight", new KeyTrigger(KeyInput.KEY_D));
+ inputManager.addMapping("FLYCAM_Forward", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("FLYCAM_Backward", new KeyTrigger(KeyInput.KEY_S));
+ inputManager.addMapping("FLYCAM_Rise", new KeyTrigger(KeyInput.KEY_Q));
+ inputManager.addMapping("FLYCAM_Lower", new KeyTrigger(KeyInput.KEY_Z));
+
+ inputManager.addListener(this, mappings);
+ inputManager.setCursorVisible(dragToRotate || !isEnabled());
+
+ Joystick[] joysticks = inputManager.getJoysticks();
+ if (joysticks != null && joysticks.length > 0){
+ Joystick joystick = joysticks[0];
+ joystick.assignAxis("FLYCAM_StrafeRight", "FLYCAM_StrafeLeft", JoyInput.AXIS_POV_X);
+ joystick.assignAxis("FLYCAM_Forward", "FLYCAM_Backward", JoyInput.AXIS_POV_Y);
+ joystick.assignAxis("FLYCAM_Right", "FLYCAM_Left", joystick.getXAxisIndex());
+ joystick.assignAxis("FLYCAM_Down", "FLYCAM_Up", joystick.getYAxisIndex());
+ }
+ }
+
+ /**
+ * Registers the FlyByCamera to receive input events from the provided
+ * Dispatcher.
+ * @param inputManager
+ */
+ public void unregisterInput(){
+
+ if (inputManager == null) {
+ return;
+ }
+
+ for (String s : mappings) {
+ if (inputManager.hasMapping(s)) {
+ inputManager.deleteMapping( s );
+ }
+ }
+
+ inputManager.removeListener(this);
+ inputManager.setCursorVisible(!dragToRotate);
+
+ Joystick[] joysticks = inputManager.getJoysticks();
+ if (joysticks != null && joysticks.length > 0){
+ Joystick joystick = joysticks[0];
+
+ // No way to unassing axis
+ }
+ }
+
+ protected void rotateCamera(float value, Vector3f axis){
+ if (dragToRotate){
+ if (canRotate){
+// value = -value;
+ }else{
+ return;
+ }
+ }
+
+ Matrix3f mat = new Matrix3f();
+ mat.fromAngleNormalAxis(rotationSpeed * value, axis);
+
+ Vector3f up = cam.getUp();
+ Vector3f left = cam.getLeft();
+ Vector3f dir = cam.getDirection();
+
+ mat.mult(up, up);
+ mat.mult(left, left);
+ mat.mult(dir, dir);
+
+ Quaternion q = new Quaternion();
+ q.fromAxes(left, up, dir);
+ q.normalize();
+
+ cam.setAxes(q);
+ }
+
+ protected void zoomCamera(float value){
+ // derive fovY value
+ float h = cam.getFrustumTop();
+ float w = cam.getFrustumRight();
+ float aspect = w / h;
+
+ float near = cam.getFrustumNear();
+
+ float fovY = FastMath.atan(h / near)
+ / (FastMath.DEG_TO_RAD * .5f);
+ fovY += value * 0.1f;
+
+ h = FastMath.tan( fovY * FastMath.DEG_TO_RAD * .5f) * near;
+ w = h * aspect;
+
+ cam.setFrustumTop(h);
+ cam.setFrustumBottom(-h);
+ cam.setFrustumLeft(-w);
+ cam.setFrustumRight(w);
+ }
+
+ protected void riseCamera(float value){
+ Vector3f vel = new Vector3f(0, value * moveSpeed, 0);
+ Vector3f pos = cam.getLocation().clone();
+
+ if (motionAllowed != null)
+ motionAllowed.checkMotionAllowed(pos, vel);
+ else
+ pos.addLocal(vel);
+
+ cam.setLocation(pos);
+ }
+
+ protected void moveCamera(float value, boolean sideways){
+ Vector3f vel = new Vector3f();
+ Vector3f pos = cam.getLocation().clone();
+
+ if (sideways){
+ cam.getLeft(vel);
+ }else{
+ cam.getDirection(vel);
+ }
+ vel.multLocal(value * moveSpeed);
+
+ if (motionAllowed != null)
+ motionAllowed.checkMotionAllowed(pos, vel);
+ else
+ pos.addLocal(vel);
+
+ cam.setLocation(pos);
+ }
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (!enabled)
+ return;
+
+ if (name.equals("FLYCAM_Left")){
+ rotateCamera(value, initialUpVec);
+ }else if (name.equals("FLYCAM_Right")){
+ rotateCamera(-value, initialUpVec);
+ }else if (name.equals("FLYCAM_Up")){
+ rotateCamera(-value, cam.getLeft());
+ }else if (name.equals("FLYCAM_Down")){
+ rotateCamera(value, cam.getLeft());
+ }else if (name.equals("FLYCAM_Forward")){
+ moveCamera(value, false);
+ }else if (name.equals("FLYCAM_Backward")){
+ moveCamera(-value, false);
+ }else if (name.equals("FLYCAM_StrafeLeft")){
+ moveCamera(value, true);
+ }else if (name.equals("FLYCAM_StrafeRight")){
+ moveCamera(-value, true);
+ }else if (name.equals("FLYCAM_Rise")){
+ riseCamera(value);
+ }else if (name.equals("FLYCAM_Lower")){
+ riseCamera(-value);
+ }else if (name.equals("FLYCAM_ZoomIn")){
+ zoomCamera(value);
+ }else if (name.equals("FLYCAM_ZoomOut")){
+ zoomCamera(-value);
+ }
+ }
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (!enabled)
+ return;
+
+ if (name.equals("FLYCAM_RotateDrag") && dragToRotate){
+ canRotate = value;
+ inputManager.setCursorVisible(!value);
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/Input.java b/engine/src/core/com/jme3/input/Input.java
new file mode 100644
index 0000000..1cece68
--- /dev/null
+++ b/engine/src/core/com/jme3/input/Input.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+/**
+ * Abstract interface for an input device.
+ *
+ * @see MouseInput
+ * @see KeyInput
+ * @see JoyInput
+ */
+public interface Input {
+
+ /**
+ * Initializes the native side to listen into events from the device.
+ */
+ public void initialize();
+
+ /**
+ * Queries the device for input. All events should be sent to the
+ * RawInputListener set with setInputListener.
+ *
+ * @see #setInputListener(com.jme3.input.RawInputListener)
+ */
+ public void update();
+
+ /**
+ * Ceases listening to events from the device.
+ */
+ public void destroy();
+
+ /**
+ * @return True if the device has been initialized and not destroyed.
+ * @see #initialize()
+ * @see #destroy()
+ */
+ public boolean isInitialized();
+
+ /**
+ * Sets the input listener to receive events from this device. The
+ * appropriate events should be dispatched through the callbacks
+ * in RawInputListener.
+ * @param listener
+ */
+ public void setInputListener(RawInputListener listener);
+
+ /**
+ * @return The current absolute time as nanoseconds. This time is expected
+ * to be relative to the time given in InputEvents time property.
+ */
+ public long getInputTimeNanos();
+}
diff --git a/engine/src/core/com/jme3/input/InputManager.java b/engine/src/core/com/jme3/input/InputManager.java
new file mode 100644
index 0000000..23f2988
--- /dev/null
+++ b/engine/src/core/com/jme3/input/InputManager.java
@@ -0,0 +1,881 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.input;
+
+import com.jme3.app.Application;
+import com.jme3.input.controls.*;
+import com.jme3.input.event.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The <code>InputManager</code> is responsible for converting input events
+ * received from the Key, Mouse and Joy Input implementations into an
+ * abstract, input device independent representation that user code can use.
+ * <p>
+ * By default an <code>InputManager</code> is included with every Application instance for use
+ * in user code to query input, unless the Application is created as headless
+ * or with input explicitly disabled.
+ * <p>
+ * The input manager has two concepts, a {@link Trigger} and a mapping.
+ * A trigger represents a specific input trigger, such as a key button,
+ * or a mouse axis. A mapping represents a link onto one or several triggers,
+ * when the appropriate trigger is activated (e.g. a key is pressed), the
+ * mapping will be invoked. Any listeners registered to receive an event
+ * from the mapping will have an event raised.
+ * <p>
+ * There are two types of events that {@link InputListener input listeners}
+ * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}
+ * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}
+ * events.
+ * <p>
+ * <code>onAction</code> events are raised when the specific input
+ * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>
+ * event will be raised with the <code>isPressed</code> argument equal to true,
+ * when the key is released, <code>onAction</code> is called again but this time
+ * with the <code>isPressed</code> argument set to false.
+ * For analog inputs, the <code>onAction</code> method will be called any time
+ * the input is non-zero, however an exception to this is for joystick axis inputs,
+ * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.
+ * <p>
+ * <code>onAnalog</code> events are raised every frame while the input is activated.
+ * For digital inputs, every frame that the input is active will cause the
+ * <code>onAnalog</code> method to be called, the argument <code>value</code>
+ * argument will equal to the frame's time per frame (TPF) value but only
+ * for digital inputs. For analog inputs however, the <code>value</code> argument
+ * will equal the actual analog value.
+ */
+public class InputManager implements RawInputListener {
+
+ private static final Logger logger = Logger.getLogger(InputManager.class.getName());
+ private final KeyInput keys;
+ private final MouseInput mouse;
+ private final JoyInput joystick;
+ private final TouchInput touch;
+ private float frameTPF;
+ private long lastLastUpdateTime = 0;
+ private long lastUpdateTime = 0;
+ private long frameDelta = 0;
+ private long firstTime = 0;
+ private boolean eventsPermitted = false;
+ private boolean mouseVisible = true;
+ private boolean safeMode = false;
+ private float axisDeadZone = 0.05f;
+ private Vector2f cursorPos = new Vector2f();
+ private Joystick[] joysticks;
+ private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
+ private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
+ private final IntMap<Long> pressedButtons = new IntMap<Long>();
+ private final IntMap<Float> axisValues = new IntMap<Float>();
+ private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();
+ private RawInputListener[] rawListenerArray = null;
+ private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
+
+ private static class Mapping {
+
+ private final String name;
+ private final ArrayList<Integer> triggers = new ArrayList<Integer>();
+ private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();
+
+ public Mapping(String name) {
+ this.name = name;
+ }
+ }
+
+ /**
+ * Initializes the InputManager.
+ *
+ * <p>This should only be called internally in {@link Application}.
+ *
+ * @param mouse
+ * @param keys
+ * @param joystick
+ * @param touch
+ * @throws IllegalArgumentException If either mouseInput or keyInput are null.
+ */
+ public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {
+ if (keys == null || mouse == null) {
+ throw new NullPointerException("Mouse or keyboard cannot be null");
+ }
+
+ this.keys = keys;
+ this.mouse = mouse;
+ this.joystick = joystick;
+ this.touch = touch;
+
+ keys.setInputListener(this);
+ mouse.setInputListener(this);
+ if (joystick != null) {
+ joystick.setInputListener(this);
+ joysticks = joystick.loadJoysticks(this);
+ }
+ if (touch != null) {
+ touch.setInputListener(this);
+ }
+
+ firstTime = keys.getInputTimeNanos();
+ }
+
+ private void invokeActions(int hash, boolean pressed) {
+ ArrayList<Mapping> maps = bindings.get(hash);
+ if (maps == null) {
+ return;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+ if (listener instanceof ActionListener) {
+ ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);
+ }
+ }
+ }
+ }
+
+ private float computeAnalogValue(long timeDelta) {
+ if (safeMode || frameDelta == 0) {
+ return 1f;
+ } else {
+ return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);
+ }
+ }
+
+ private void invokeTimedActions(int hash, long time, boolean pressed) {
+ if (!bindings.containsKey(hash)) {
+ return;
+ }
+
+ if (pressed) {
+ pressedButtons.put(hash, time);
+ } else {
+ Long pressTimeObj = pressedButtons.remove(hash);
+ if (pressTimeObj == null) {
+ return; // under certain circumstances it can be null, ignore
+ } // the event then.
+
+ long pressTime = pressTimeObj;
+ long lastUpdate = lastLastUpdateTime;
+ long releaseTime = time;
+ long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);
+
+ if (timeDelta > 0) {
+ invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
+ }
+ }
+ }
+
+ private void invokeUpdateActions() {
+ for (Entry<Long> pressedButton : pressedButtons) {
+ int hash = pressedButton.getKey();
+
+ long pressTime = pressedButton.getValue();
+ long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);
+
+ if (timeDelta > 0) {
+ invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
+ }
+ }
+
+ for (Entry<Float> axisValue : axisValues) {
+ int hash = axisValue.getKey();
+ float value = axisValue.getValue();
+ invokeAnalogs(hash, value * frameTPF, true);
+ }
+ }
+
+ private void invokeAnalogs(int hash, float value, boolean isAxis) {
+ ArrayList<Mapping> maps = bindings.get(hash);
+ if (maps == null) {
+ return;
+ }
+
+ if (!isAxis) {
+ value *= frameTPF;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+ if (listener instanceof AnalogListener) {
+ // NOTE: multiply by TPF for any button bindings
+ ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
+ }
+ }
+ }
+ }
+
+ private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {
+ if (value < axisDeadZone) {
+ invokeAnalogs(hash, value, !applyTpf);
+ return;
+ }
+
+ ArrayList<Mapping> maps = bindings.get(hash);
+ if (maps == null) {
+ return;
+ }
+
+ boolean valueChanged = !axisValues.containsKey(hash);
+ if (applyTpf) {
+ value *= frameTPF;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+
+ if (listener instanceof ActionListener && valueChanged) {
+ ((ActionListener) listener).onAction(mapping.name, true, frameTPF);
+ }
+
+ if (listener instanceof AnalogListener) {
+ ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void beginInput() {
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void endInput() {
+ }
+
+ private void onJoyAxisEventQueued(JoyAxisEvent evt) {
+// for (int i = 0; i < rawListeners.size(); i++){
+// rawListeners.get(i).onJoyAxisEvent(evt);
+// }
+
+ int joyId = evt.getJoyIndex();
+ int axis = evt.getAxisIndex();
+ float value = evt.getValue();
+ if (value < axisDeadZone && value > -axisDeadZone) {
+ int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+ int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+
+ Float val1 = axisValues.get(hash1);
+ Float val2 = axisValues.get(hash2);
+
+ if (val1 != null && val1.floatValue() > axisDeadZone) {
+ invokeActions(hash1, false);
+ }
+ if (val2 != null && val2.floatValue() > axisDeadZone) {
+ invokeActions(hash2, false);
+ }
+
+ axisValues.remove(hash1);
+ axisValues.remove(hash2);
+
+ } else if (value < 0) {
+ int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+ int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+ invokeAnalogsAndActions(hash, -value, true);
+ axisValues.put(hash, -value);
+ axisValues.remove(otherHash);
+ } else {
+ int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+ int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+ invokeAnalogsAndActions(hash, value, true);
+ axisValues.put(hash, value);
+ axisValues.remove(otherHash);
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onJoyAxisEvent(JoyAxisEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
+ }
+
+ inputQueue.add(evt);
+ }
+
+ private void onJoyButtonEventQueued(JoyButtonEvent evt) {
+// for (int i = 0; i < rawListeners.size(); i++){
+// rawListeners.get(i).onJoyButtonEvent(evt);
+// }
+
+ int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());
+ invokeActions(hash, evt.isPressed());
+ invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onJoyButtonEvent(JoyButtonEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
+ }
+
+ inputQueue.add(evt);
+ }
+
+ private void onMouseMotionEventQueued(MouseMotionEvent evt) {
+// for (int i = 0; i < rawListeners.size(); i++){
+// rawListeners.get(i).onMouseMotionEvent(evt);
+// }
+
+ if (evt.getDX() != 0) {
+ float val = Math.abs(evt.getDX()) / 1024f;
+ invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);
+ }
+ if (evt.getDY() != 0) {
+ float val = Math.abs(evt.getDY()) / 1024f;
+ invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);
+ }
+ if (evt.getDeltaWheel() != 0) {
+ float val = Math.abs(evt.getDeltaWheel()) / 100f;
+ invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onMouseMotionEvent(MouseMotionEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
+ }
+
+ cursorPos.set(evt.getX(), evt.getY());
+ inputQueue.add(evt);
+ }
+
+ private void onMouseButtonEventQueued(MouseButtonEvent evt) {
+ int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());
+ invokeActions(hash, evt.isPressed());
+ invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onMouseButtonEvent(MouseButtonEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
+ }
+ //updating cursor pos on click, so that non android touch events can properly update cursor position.
+ cursorPos.set(evt.getX(), evt.getY());
+ inputQueue.add(evt);
+ }
+
+ private void onKeyEventQueued(KeyInputEvent evt) {
+ if (evt.isRepeating()) {
+ return; // repeat events not used for bindings
+ }
+
+ int hash = KeyTrigger.keyHash(evt.getKeyCode());
+ invokeActions(hash, evt.isPressed());
+ invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onKeyEvent(KeyInputEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");
+ }
+
+ inputQueue.add(evt);
+ }
+
+ /**
+ * Set the deadzone for joystick axes.
+ *
+ * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }
+ * events will only be raised if the joystick axis value is greater than
+ * the <code>deadZone</code>.
+ *
+ * @param deadZone the deadzone for joystick axes.
+ */
+ public void setAxisDeadZone(float deadZone) {
+ this.axisDeadZone = deadZone;
+ }
+
+ /**
+ * Returns the deadzone for joystick axes.
+ *
+ * @return the deadzone for joystick axes.
+ */
+ public float getAxisDeadZone() {
+ return axisDeadZone;
+ }
+
+ /**
+ * Adds a new listener to receive events on the given mappings.
+ *
+ * <p>The given InputListener will be registered to receive events
+ * on the specified mapping names. When a mapping raises an event, the
+ * listener will have its appropriate method invoked, either
+ * {@link ActionListener#onAction(java.lang.String, boolean, float) }
+ * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }
+ * depending on which interface the <code>listener</code> implements.
+ * If the listener implements both interfaces, then it will receive the
+ * appropriate event for each method.
+ *
+ * @param listener The listener to register to receive input events.
+ * @param mappingNames The mapping names which the listener will receive
+ * events from.
+ *
+ * @see InputManager#removeListener(com.jme3.input.controls.InputListener)
+ */
+ public void addListener(InputListener listener, String... mappingNames) {
+ for (String mappingName : mappingNames) {
+ Mapping mapping = mappings.get(mappingName);
+ if (mapping == null) {
+ mapping = new Mapping(mappingName);
+ mappings.put(mappingName, mapping);
+ }
+ if (!mapping.listeners.contains(listener)) {
+ mapping.listeners.add(listener);
+ }
+ }
+ }
+
+ /**
+ * Removes a listener from receiving events.
+ *
+ * <p>This will unregister the listener from any mappings that it
+ * was previously registered with via
+ * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.
+ *
+ * @param listener The listener to unregister.
+ *
+ * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[])
+ */
+ public void removeListener(InputListener listener) {
+ for (Mapping mapping : mappings.values()) {
+ mapping.listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Create a new mapping to the given triggers.
+ *
+ * <p>
+ * The given mapping will be assigned to the given triggers, when
+ * any of the triggers given raise an event, the listeners
+ * registered to the mappings will receive appropriate events.
+ *
+ * @param mappingName The mapping name to assign.
+ * @param triggers The triggers to which the mapping is to be registered.
+ *
+ * @see InputManager#deleteMapping(java.lang.String)
+ */
+ public void addMapping(String mappingName, Trigger... triggers) {
+ Mapping mapping = mappings.get(mappingName);
+ if (mapping == null) {
+ mapping = new Mapping(mappingName);
+ mappings.put(mappingName, mapping);
+ }
+
+ for (Trigger trigger : triggers) {
+ int hash = trigger.triggerHashCode();
+ ArrayList<Mapping> names = bindings.get(hash);
+ if (names == null) {
+ names = new ArrayList<Mapping>();
+ bindings.put(hash, names);
+ }
+ if (!names.contains(mapping)) {
+ names.add(mapping);
+ mapping.triggers.add(hash);
+ } else {
+ logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this InputManager has a mapping registered
+ * for the given mappingName.
+ *
+ * @param mappingName The mapping name to check.
+ *
+ * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])
+ * @see InputManager#deleteMapping(java.lang.String)
+ */
+ public boolean hasMapping(String mappingName) {
+ return mappings.containsKey(mappingName);
+ }
+
+ /**
+ * Deletes a mapping from receiving trigger events.
+ *
+ * <p>
+ * The given mapping will no longer be assigned to receive trigger
+ * events.
+ *
+ * @param mappingName The mapping name to unregister.
+ *
+ * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])
+ */
+ public void deleteMapping(String mappingName) {
+ Mapping mapping = mappings.remove(mappingName);
+ if (mapping == null) {
+ throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
+ }
+
+ ArrayList<Integer> triggers = mapping.triggers;
+ for (int i = triggers.size() - 1; i >= 0; i--) {
+ int hash = triggers.get(i);
+ ArrayList<Mapping> maps = bindings.get(hash);
+ maps.remove(mapping);
+ }
+ }
+
+ /**
+ * Deletes a specific trigger registered to a mapping.
+ *
+ * <p>
+ * The given mapping will no longer receive events raised by the
+ * trigger.
+ *
+ * @param mappingName The mapping name to cease receiving events from the
+ * trigger.
+ * @param trigger The trigger to no longer invoke events on the mapping.
+ */
+ public void deleteTrigger(String mappingName, Trigger trigger) {
+ Mapping mapping = mappings.get(mappingName);
+ if (mapping == null) {
+ throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
+ }
+
+ ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());
+ maps.remove(mapping);
+
+ }
+
+ /**
+ * Clears all the input mappings from this InputManager.
+ * Consequently, also clears all of the
+ * InputListeners as well.
+ */
+ public void clearMappings() {
+ mappings.clear();
+ bindings.clear();
+ reset();
+ }
+
+ /**
+ * Do not use.
+ * Called to reset pressed keys or buttons when focus is restored.
+ */
+ public void reset() {
+ pressedButtons.clear();
+ axisValues.clear();
+ }
+
+ /**
+ * Returns whether the mouse cursor is visible or not.
+ *
+ * <p>By default the cursor is visible.
+ *
+ * @return whether the mouse cursor is visible or not.
+ *
+ * @see InputManager#setCursorVisible(boolean)
+ */
+ public boolean isCursorVisible() {
+ return mouseVisible;
+ }
+
+ /**
+ * Set whether the mouse cursor should be visible or not.
+ *
+ * @param visible whether the mouse cursor should be visible or not.
+ */
+ public void setCursorVisible(boolean visible) {
+ if (mouseVisible != visible) {
+ mouseVisible = visible;
+ mouse.setCursorVisible(mouseVisible);
+ }
+ }
+
+ /**
+ * Returns the current cursor position. The position is relative to the
+ * bottom-left of the screen and is in pixels.
+ *
+ * @return the current cursor position
+ */
+ public Vector2f getCursorPosition() {
+ return cursorPos;
+ }
+
+ /**
+ * Returns an array of all joysticks installed on the system.
+ *
+ * @return an array of all joysticks installed on the system.
+ */
+ public Joystick[] getJoysticks() {
+ return joysticks;
+ }
+
+ /**
+ * Adds a {@link RawInputListener} to receive raw input events.
+ *
+ * <p>
+ * Any raw input listeners registered to this <code>InputManager</code>
+ * will receive raw input events first, before they get handled
+ * by the <code>InputManager</code> itself. The listeners are
+ * each processed in the order they were added, e.g. FIFO.
+ * <p>
+ * If a raw input listener has handled the event and does not wish
+ * other listeners down the list to process the event, it may set the
+ * {@link InputEvent#setConsumed() consumed flag} to indicate the
+ * event was consumed and shouldn't be processed any further.
+ * The listener may do this either at each of the event callbacks
+ * or at the {@link RawInputListener#endInput() } method.
+ *
+ * @param listener A listener to receive raw input events.
+ *
+ * @see RawInputListener
+ */
+ public void addRawInputListener(RawInputListener listener) {
+ rawListeners.add(listener);
+ rawListenerArray = null;
+ }
+
+ /**
+ * Removes a {@link RawInputListener} so that it no longer
+ * receives raw input events.
+ *
+ * @param listener The listener to cease receiving raw input events.
+ *
+ * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)
+ */
+ public void removeRawInputListener(RawInputListener listener) {
+ rawListeners.remove(listener);
+ rawListenerArray = null;
+ }
+
+ /**
+ * Clears all {@link RawInputListener}s.
+ *
+ * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)
+ */
+ public void clearRawInputListeners() {
+ rawListeners.clear();
+ rawListenerArray = null;
+ }
+
+ private RawInputListener[] getRawListenerArray() {
+ if (rawListenerArray == null)
+ rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);
+ return rawListenerArray;
+ }
+
+ /**
+ * Enable simulation of mouse events. Used for touchscreen input only.
+ *
+ * @param value True to enable simulation of mouse events
+ */
+ public void setSimulateMouse(boolean value) {
+ if (touch != null) {
+ touch.setSimulateMouse(value);
+ }
+ }
+
+ /**
+ * Enable simulation of keyboard events. Used for touchscreen input only.
+ *
+ * @param value True to enable simulation of keyboard events
+ */
+ public void setSimulateKeyboard(boolean value) {
+ if (touch != null) {
+ touch.setSimulateKeyboard(value);
+ }
+ }
+
+ private void processQueue() {
+ int queueSize = inputQueue.size();
+ RawInputListener[] array = getRawListenerArray();
+
+ for (RawInputListener listener : array) {
+ listener.beginInput();
+
+ for (int j = 0; j < queueSize; j++) {
+ InputEvent event = inputQueue.get(j);
+ if (event.isConsumed()) {
+ continue;
+ }
+
+ if (event instanceof MouseMotionEvent) {
+ listener.onMouseMotionEvent((MouseMotionEvent) event);
+ } else if (event instanceof KeyInputEvent) {
+ listener.onKeyEvent((KeyInputEvent) event);
+ } else if (event instanceof MouseButtonEvent) {
+ listener.onMouseButtonEvent((MouseButtonEvent) event);
+ } else if (event instanceof JoyAxisEvent) {
+ listener.onJoyAxisEvent((JoyAxisEvent) event);
+ } else if (event instanceof JoyButtonEvent) {
+ listener.onJoyButtonEvent((JoyButtonEvent) event);
+ } else if (event instanceof TouchEvent) {
+ listener.onTouchEvent((TouchEvent) event);
+ } else {
+ assert false;
+ }
+ }
+
+ listener.endInput();
+ }
+
+ for (int i = 0; i < queueSize; i++) {
+ InputEvent event = inputQueue.get(i);
+ if (event.isConsumed()) {
+ continue;
+ }
+
+ if (event instanceof MouseMotionEvent) {
+ onMouseMotionEventQueued((MouseMotionEvent) event);
+ } else if (event instanceof KeyInputEvent) {
+ onKeyEventQueued((KeyInputEvent) event);
+ } else if (event instanceof MouseButtonEvent) {
+ onMouseButtonEventQueued((MouseButtonEvent) event);
+ } else if (event instanceof JoyAxisEvent) {
+ onJoyAxisEventQueued((JoyAxisEvent) event);
+ } else if (event instanceof JoyButtonEvent) {
+ onJoyButtonEventQueued((JoyButtonEvent) event);
+ } else if (event instanceof TouchEvent) {
+ onTouchEventQueued((TouchEvent) event);
+ } else {
+ assert false;
+ }
+ // larynx, 2011.06.10 - flag event as reusable because
+ // the android input uses a non-allocating ringbuffer which
+ // needs to know when the event is not anymore in inputQueue
+ // and therefor can be reused.
+ event.setConsumed();
+ }
+
+ inputQueue.clear();
+ }
+
+ /**
+ * Updates the <code>InputManager</code>.
+ * This will query current input devices and send
+ * appropriate events to registered listeners.
+ *
+ * @param tpf Time per frame value.
+ */
+ public void update(float tpf) {
+ frameTPF = tpf;
+
+ // Activate safemode if the TPF value is so small
+ // that rounding errors are inevitable
+ safeMode = tpf < 0.015f;
+
+ long currentTime = keys.getInputTimeNanos();
+ frameDelta = currentTime - lastUpdateTime;
+
+ eventsPermitted = true;
+
+ keys.update();
+ mouse.update();
+ if (joystick != null) {
+ joystick.update();
+ }
+ if (touch != null) {
+ touch.update();
+ }
+
+ eventsPermitted = false;
+
+ processQueue();
+ invokeUpdateActions();
+
+ lastLastUpdateTime = lastUpdateTime;
+ lastUpdateTime = currentTime;
+ }
+
+ /**
+ * Dispatches touch events to touch listeners
+ * @param evt The touch event to be dispatched to all onTouch listeners
+ */
+ public void onTouchEventQueued(TouchEvent evt) {
+ ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));
+ if (maps == null) {
+ return;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+ if (listener instanceof TouchListener) {
+ ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF);
+ }
+ }
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ @Override
+ public void onTouchEvent(TouchEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");
+ }
+ inputQueue.add(evt);
+ }
+}
diff --git a/engine/src/core/com/jme3/input/JoyInput.java b/engine/src/core/com/jme3/input/JoyInput.java
new file mode 100644
index 0000000..77c5539
--- /dev/null
+++ b/engine/src/core/com/jme3/input/JoyInput.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+/**
+ * A specific API for interfacing with joysticks or gaming controllers.
+ */
+public interface JoyInput extends Input {
+
+ /**
+ * The X axis for POV (point of view hat).
+ */
+ public static final int AXIS_POV_X = 254;
+
+ /**
+ * The Y axis for POV (point of view hat).
+ */
+ public static final int AXIS_POV_Y = 255;
+
+ /**
+ * Causes the joystick at <code>joyId</code> index to rumble with
+ * the given amount.
+ *
+ * @param joyId The joystick index
+ * @param amount Rumble amount. Should be between 0 and 1.
+ */
+ public void setJoyRumble(int joyId, float amount);
+
+ /**
+ * Loads a list of joysticks from the system.
+ *
+ * @param inputManager The input manager requesting to load joysticks
+ * @return A list of joysticks that are installed.
+ */
+ public Joystick[] loadJoysticks(InputManager inputManager);
+}
diff --git a/engine/src/core/com/jme3/input/Joystick.java b/engine/src/core/com/jme3/input/Joystick.java
new file mode 100644
index 0000000..658781f
--- /dev/null
+++ b/engine/src/core/com/jme3/input/Joystick.java
@@ -0,0 +1,136 @@
+package com.jme3.input;
+
+import com.jme3.input.controls.JoyAxisTrigger;
+import com.jme3.input.controls.JoyButtonTrigger;
+
+/**
+ * A joystick represents a single joystick that is installed in the system.
+ *
+ * @author Kirill Vainer
+ */
+public final class Joystick {
+
+ private InputManager inputManager;
+ private JoyInput joyInput;
+ private int joyId;
+ private int buttonCount;
+ private int axisCount;
+ private int axisXIndex, axisYIndex;
+ private String name;
+
+ /**
+ * Creates a new joystick instance. Only used internally.
+ */
+ public Joystick(InputManager inputManager, JoyInput joyInput,
+ int joyId, String name, int buttonCount, int axisCount,
+ int xAxis, int yAxis){
+ this.inputManager = inputManager;
+ this.joyInput = joyInput;
+ this.joyId = joyId;
+ this.name = name;
+ this.buttonCount = buttonCount;
+ this.axisCount = axisCount;
+
+ this.axisXIndex = xAxis;
+ this.axisYIndex = yAxis;
+ }
+
+ /**
+ * Rumbles the joystick for the given amount/magnitude.
+ *
+ * @param amount The amount to rumble. Should be between 0 and 1.
+ */
+ public void rumble(float amount){
+ joyInput.setJoyRumble(joyId, amount);
+ }
+
+ /**
+ * Assign the mapping name to receive events from the given button index
+ * on the joystick.
+ *
+ * @param mappingName The mapping to receive joystick button events.
+ * @param buttonId The button index.
+ *
+ * @see Joystick#getButtonCount()
+ */
+ public void assignButton(String mappingName, int buttonId){
+ if (buttonId < 0 || buttonId >= buttonCount)
+ throw new IllegalArgumentException();
+
+ inputManager.addMapping(mappingName, new JoyButtonTrigger(joyId, buttonId));
+ }
+
+ /**
+ * Assign the mappings to receive events from the given joystick axis.
+ *
+ * @param positiveMapping The mapping to receive events when the axis is negative
+ * @param negativeMapping The mapping to receive events when the axis is positive
+ * @param axisId The axis index.
+ *
+ * @see Joystick#getAxisCount()
+ */
+ public void assignAxis(String positiveMapping, String negativeMapping, int axisId){
+ inputManager.addMapping(positiveMapping, new JoyAxisTrigger(joyId, axisId, false));
+ inputManager.addMapping(negativeMapping, new JoyAxisTrigger(joyId, axisId, true));
+ }
+
+ /**
+ * Gets the index number for the X axis on the joystick.
+ *
+ * <p>E.g. for most gamepads, the left control stick X axis will be returned.
+ *
+ * @return The axis index for the X axis for this joystick.
+ *
+ * @see Joystick#assignAxis(java.lang.String, java.lang.String, int)
+ */
+ public int getXAxisIndex(){
+ return axisXIndex;
+ }
+
+ /**
+ * Gets the index number for the Y axis on the joystick.
+ *
+ * <p>E.g. for most gamepads, the left control stick Y axis will be returned.
+ *
+ * @return The axis index for the Y axis for this joystick.
+ *
+ * @see Joystick#assignAxis(java.lang.String, java.lang.String, int)
+ */
+ public int getYAxisIndex(){
+ return axisYIndex;
+ }
+
+ /**
+ * Returns the number of axes on this joystick.
+ *
+ * @return the number of axes on this joystick.
+ */
+ public int getAxisCount() {
+ return axisCount;
+ }
+
+ /**
+ * Returns the number of buttons on this joystick.
+ *
+ * @return the number of buttons on this joystick.
+ */
+ public int getButtonCount() {
+ return buttonCount;
+ }
+
+ /**
+ * Returns the name of this joystick.
+ *
+ * @return the name of this joystick.
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString(){
+ return "Joystick[name=" + name + ", id=" + joyId + ", buttons=" + buttonCount
+ + ", axes=" + axisCount + "]";
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/KeyInput.java b/engine/src/core/com/jme3/input/KeyInput.java
new file mode 100644
index 0000000..a26d23a
--- /dev/null
+++ b/engine/src/core/com/jme3/input/KeyInput.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+/**
+ * A specific API for interfacing with the keyboard.
+ */
+public interface KeyInput extends Input {
+
+ /**
+ * escape key.
+ */
+ public static final int KEY_ESCAPE = 0x01;
+ /**
+ * 1 key.
+ */
+ public static final int KEY_1 = 0x02;
+ /**
+ * 2 key.
+ */
+ public static final int KEY_2 = 0x03;
+ /**
+ * 3 key.
+ */
+ public static final int KEY_3 = 0x04;
+ /**
+ * 4 key.
+ */
+ public static final int KEY_4 = 0x05;
+ /**
+ * 5 key.
+ */
+ public static final int KEY_5 = 0x06;
+ /**
+ * 6 key.
+ */
+ public static final int KEY_6 = 0x07;
+ /**
+ * 7 key.
+ */
+ public static final int KEY_7 = 0x08;
+ /**
+ * 8 key.
+ */
+ public static final int KEY_8 = 0x09;
+ /**
+ * 9 key.
+ */
+ public static final int KEY_9 = 0x0A;
+ /**
+ * 0 key.
+ */
+ public static final int KEY_0 = 0x0B;
+ /**
+ * - key.
+ */
+ public static final int KEY_MINUS = 0x0C;
+ /**
+ * = key.
+ */
+ public static final int KEY_EQUALS = 0x0D;
+ /**
+ * back key.
+ */
+ public static final int KEY_BACK = 0x0E;
+ /**
+ * tab key.
+ */
+ public static final int KEY_TAB = 0x0F;
+ /**
+ * q key.
+ */
+ public static final int KEY_Q = 0x10;
+ /**
+ * w key.
+ */
+ public static final int KEY_W = 0x11;
+ /**
+ * e key.
+ */
+ public static final int KEY_E = 0x12;
+ /**
+ * r key.
+ */
+ public static final int KEY_R = 0x13;
+ /**
+ * t key.
+ */
+ public static final int KEY_T = 0x14;
+ /**
+ * y key.
+ */
+ public static final int KEY_Y = 0x15;
+ /**
+ * u key.
+ */
+ public static final int KEY_U = 0x16;
+ /**
+ * i key.
+ */
+ public static final int KEY_I = 0x17;
+ /**
+ * o key.
+ */
+ public static final int KEY_O = 0x18;
+ /**
+ * p key.
+ */
+ public static final int KEY_P = 0x19;
+ /**
+ * [ key.
+ */
+ public static final int KEY_LBRACKET = 0x1A;
+ /**
+ * ] key.
+ */
+ public static final int KEY_RBRACKET = 0x1B;
+ /**
+ * enter (main keyboard) key.
+ */
+ public static final int KEY_RETURN = 0x1C;
+ /**
+ * left control key.
+ */
+ public static final int KEY_LCONTROL = 0x1D;
+ /**
+ * a key.
+ */
+ public static final int KEY_A = 0x1E;
+ /**
+ * s key.
+ */
+ public static final int KEY_S = 0x1F;
+ /**
+ * d key.
+ */
+ public static final int KEY_D = 0x20;
+ /**
+ * f key.
+ */
+ public static final int KEY_F = 0x21;
+ /**
+ * g key.
+ */
+ public static final int KEY_G = 0x22;
+ /**
+ * h key.
+ */
+ public static final int KEY_H = 0x23;
+ /**
+ * j key.
+ */
+ public static final int KEY_J = 0x24;
+ /**
+ * k key.
+ */
+ public static final int KEY_K = 0x25;
+ /**
+ * l key.
+ */
+ public static final int KEY_L = 0x26;
+ /**
+ * ; key.
+ */
+ public static final int KEY_SEMICOLON = 0x27;
+ /**
+ * ' key.
+ */
+ public static final int KEY_APOSTROPHE = 0x28;
+ /**
+ * ` key.
+ */
+ public static final int KEY_GRAVE = 0x29;
+ /**
+ * left shift key.
+ */
+ public static final int KEY_LSHIFT = 0x2A;
+ /**
+ * \ key.
+ */
+ public static final int KEY_BACKSLASH = 0x2B;
+ /**
+ * z key.
+ */
+ public static final int KEY_Z = 0x2C;
+ /**
+ * x key.
+ */
+ public static final int KEY_X = 0x2D;
+ /**
+ * c key.
+ */
+ public static final int KEY_C = 0x2E;
+ /**
+ * v key.
+ */
+ public static final int KEY_V = 0x2F;
+ /**
+ * b key.
+ */
+ public static final int KEY_B = 0x30;
+ /**
+ * n key.
+ */
+ public static final int KEY_N = 0x31;
+ /**
+ * m key.
+ */
+ public static final int KEY_M = 0x32;
+ /**
+ * , key.
+ */
+ public static final int KEY_COMMA = 0x33;
+ /**
+ * . key (main keyboard).
+ */
+ public static final int KEY_PERIOD = 0x34;
+ /**
+ * / key (main keyboard).
+ */
+ public static final int KEY_SLASH = 0x35;
+ /**
+ * right shift key.
+ */
+ public static final int KEY_RSHIFT = 0x36;
+ /**
+ * * key (on keypad).
+ */
+ public static final int KEY_MULTIPLY = 0x37;
+ /**
+ * left alt key.
+ */
+ public static final int KEY_LMENU = 0x38;
+ /**
+ * space key.
+ */
+ public static final int KEY_SPACE = 0x39;
+ /**
+ * caps lock key.
+ */
+ public static final int KEY_CAPITAL = 0x3A;
+ /**
+ * F1 key.
+ */
+ public static final int KEY_F1 = 0x3B;
+ /**
+ * F2 key.
+ */
+ public static final int KEY_F2 = 0x3C;
+ /**
+ * F3 key.
+ */
+ public static final int KEY_F3 = 0x3D;
+ /**
+ * F4 key.
+ */
+ public static final int KEY_F4 = 0x3E;
+ /**
+ * F5 key.
+ */
+ public static final int KEY_F5 = 0x3F;
+ /**
+ * F6 key.
+ */
+ public static final int KEY_F6 = 0x40;
+ /**
+ * F7 key.
+ */
+ public static final int KEY_F7 = 0x41;
+ /**
+ * F8 key.
+ */
+ public static final int KEY_F8 = 0x42;
+ /**
+ * F9 key.
+ */
+ public static final int KEY_F9 = 0x43;
+ /**
+ * F10 key.
+ */
+ public static final int KEY_F10 = 0x44;
+ /**
+ * NumLK key.
+ */
+ public static final int KEY_NUMLOCK = 0x45;
+ /**
+ * Scroll lock key.
+ */
+ public static final int KEY_SCROLL = 0x46;
+ /**
+ * 7 key (num pad).
+ */
+ public static final int KEY_NUMPAD7 = 0x47;
+ /**
+ * 8 key (num pad).
+ */
+ public static final int KEY_NUMPAD8 = 0x48;
+ /**
+ * 9 key (num pad).
+ */
+ public static final int KEY_NUMPAD9 = 0x49;
+ /**
+ * - key (num pad).
+ */
+ public static final int KEY_SUBTRACT = 0x4A;
+ /**
+ * 4 key (num pad).
+ */
+ public static final int KEY_NUMPAD4 = 0x4B;
+ /**
+ * 5 key (num pad).
+ */
+ public static final int KEY_NUMPAD5 = 0x4C;
+ /**
+ * 6 key (num pad).
+ */
+ public static final int KEY_NUMPAD6 = 0x4D;
+ /**
+ * + key (num pad).
+ */
+ public static final int KEY_ADD = 0x4E;
+ /**
+ * 1 key (num pad).
+ */
+ public static final int KEY_NUMPAD1 = 0x4F;
+ /**
+ * 2 key (num pad).
+ */
+ public static final int KEY_NUMPAD2 = 0x50;
+ /**
+ * 3 key (num pad).
+ */
+ public static final int KEY_NUMPAD3 = 0x51;
+ /**
+ * 0 key (num pad).
+ */
+ public static final int KEY_NUMPAD0 = 0x52;
+ /**
+ * . key (num pad).
+ */
+ public static final int KEY_DECIMAL = 0x53;
+ /**
+ * F11 key.
+ */
+ public static final int KEY_F11 = 0x57;
+ /**
+ * F12 key.
+ */
+ public static final int KEY_F12 = 0x58;
+ /**
+ * F13 key.
+ */
+ public static final int KEY_F13 = 0x64;
+ /**
+ * F14 key.
+ */
+ public static final int KEY_F14 = 0x65;
+ /**
+ * F15 key.
+ */
+ public static final int KEY_F15 = 0x66;
+ /**
+ * kana key (Japanese).
+ */
+ public static final int KEY_KANA = 0x70;
+ /**
+ * convert key (Japanese).
+ */
+ public static final int KEY_CONVERT = 0x79;
+ /**
+ * noconvert key (Japanese).
+ */
+ public static final int KEY_NOCONVERT = 0x7B;
+ /**
+ * yen key (Japanese).
+ */
+ public static final int KEY_YEN = 0x7D;
+ /**
+ * = on num pad (NEC PC98).
+ */
+ public static final int KEY_NUMPADEQUALS = 0x8D;
+ /**
+ * circum flex key (Japanese).
+ */
+ public static final int KEY_CIRCUMFLEX = 0x90;
+ /**
+ * &#064; key (NEC PC98).
+ */
+ public static final int KEY_AT = 0x91;
+ /**
+ * : key (NEC PC98)
+ */
+ public static final int KEY_COLON = 0x92;
+ /**
+ * _ key (NEC PC98).
+ */
+ public static final int KEY_UNDERLINE = 0x93;
+ /**
+ * kanji key (Japanese).
+ */
+ public static final int KEY_KANJI = 0x94;
+ /**
+ * stop key (NEC PC98).
+ */
+ public static final int KEY_STOP = 0x95;
+ /**
+ * ax key (Japanese).
+ */
+ public static final int KEY_AX = 0x96;
+ /**
+ * (J3100).
+ */
+ public static final int KEY_UNLABELED = 0x97;
+ /**
+ * Enter key (num pad).
+ */
+ public static final int KEY_NUMPADENTER = 0x9C;
+ /**
+ * right control key.
+ */
+ public static final int KEY_RCONTROL = 0x9D;
+ /**
+ * , key on num pad (NEC PC98).
+ */
+ public static final int KEY_NUMPADCOMMA = 0xB3;
+ /**
+ * / key (num pad).
+ */
+ public static final int KEY_DIVIDE = 0xB5;
+ /**
+ * SysRq key.
+ */
+ public static final int KEY_SYSRQ = 0xB7;
+ /**
+ * right alt key.
+ */
+ public static final int KEY_RMENU = 0xB8;
+ /**
+ * pause key.
+ */
+ public static final int KEY_PAUSE = 0xC5;
+ /**
+ * home key.
+ */
+ public static final int KEY_HOME = 0xC7;
+ /**
+ * up arrow key.
+ */
+ public static final int KEY_UP = 0xC8;
+ /**
+ * PgUp key.
+ */
+ public static final int KEY_PRIOR = 0xC9;
+ /**
+ * PgUp key.
+ */
+ public static final int KEY_PGUP = KEY_PRIOR;
+
+ /**
+ * left arrow key.
+ */
+ public static final int KEY_LEFT = 0xCB;
+ /**
+ * right arrow key.
+ */
+ public static final int KEY_RIGHT = 0xCD;
+ /**
+ * end key.
+ */
+ public static final int KEY_END = 0xCF;
+ /**
+ * down arrow key.
+ */
+ public static final int KEY_DOWN = 0xD0;
+ /**
+ * PgDn key.
+ */
+ public static final int KEY_NEXT = 0xD1;
+ /**
+ * PgDn key.
+ */
+ public static final int KEY_PGDN = KEY_NEXT;
+
+ /**
+ * insert key.
+ */
+ public static final int KEY_INSERT = 0xD2;
+ /**
+ * delete key.
+ */
+ public static final int KEY_DELETE = 0xD3;
+
+ /**
+ * Left "Windows" key on PC keyboards, left "Option" key on Mac keyboards.
+ */
+ public static final int KEY_LMETA = 0xDB;
+
+ /**
+ * Right "Windows" key on PC keyboards, right "Option" key on Mac keyboards.
+ */
+ public static final int KEY_RMETA = 0xDC;
+
+ public static final int KEY_APPS = 0xDD;
+ /**
+ * power key.
+ */
+ public static final int KEY_POWER = 0xDE;
+ /**
+ * sleep key.
+ */
+ public static final int KEY_SLEEP = 0xDF;
+
+}
diff --git a/engine/src/core/com/jme3/input/KeyNames.java b/engine/src/core/com/jme3/input/KeyNames.java
new file mode 100644
index 0000000..89490c0
--- /dev/null
+++ b/engine/src/core/com/jme3/input/KeyNames.java
@@ -0,0 +1,153 @@
+package com.jme3.input;
+
+import static com.jme3.input.KeyInput.*;
+
+public class KeyNames {
+
+ private static final String[] KEY_NAMES = new String[0xFF];
+
+ static {
+ KEY_NAMES[KEY_0] = "0";
+ KEY_NAMES[KEY_1] = "1";
+ KEY_NAMES[KEY_2] = "2";
+ KEY_NAMES[KEY_3] = "3";
+ KEY_NAMES[KEY_4] = "4";
+ KEY_NAMES[KEY_5] = "5";
+ KEY_NAMES[KEY_6] = "6";
+ KEY_NAMES[KEY_7] = "7";
+ KEY_NAMES[KEY_8] = "8";
+ KEY_NAMES[KEY_9] = "9";
+
+ KEY_NAMES[KEY_Q] = "Q";
+ KEY_NAMES[KEY_W] = "W";
+ KEY_NAMES[KEY_E] = "E";
+ KEY_NAMES[KEY_R] = "R";
+ KEY_NAMES[KEY_T] = "T";
+ KEY_NAMES[KEY_Y] = "Y";
+ KEY_NAMES[KEY_U] = "U";
+ KEY_NAMES[KEY_I] = "I";
+ KEY_NAMES[KEY_O] = "O";
+ KEY_NAMES[KEY_P] = "P";
+ KEY_NAMES[KEY_A] = "A";
+ KEY_NAMES[KEY_S] = "S";
+ KEY_NAMES[KEY_D] = "D";
+ KEY_NAMES[KEY_F] = "F";
+ KEY_NAMES[KEY_G] = "G";
+ KEY_NAMES[KEY_H] = "H";
+ KEY_NAMES[KEY_J] = "J";
+ KEY_NAMES[KEY_K] = "K";
+ KEY_NAMES[KEY_L] = "L";
+ KEY_NAMES[KEY_Z] = "Z";
+ KEY_NAMES[KEY_X] = "X";
+ KEY_NAMES[KEY_C] = "C";
+ KEY_NAMES[KEY_V] = "V";
+ KEY_NAMES[KEY_B] = "B";
+ KEY_NAMES[KEY_N] = "N";
+ KEY_NAMES[KEY_M] = "M";
+
+ KEY_NAMES[KEY_F1] = "F1";
+ KEY_NAMES[KEY_F2] = "F2";
+ KEY_NAMES[KEY_F3] = "F3";
+ KEY_NAMES[KEY_F4] = "F4";
+ KEY_NAMES[KEY_F5] = "F5";
+ KEY_NAMES[KEY_F6] = "F6";
+ KEY_NAMES[KEY_F7] = "F7";
+ KEY_NAMES[KEY_F8] = "F8";
+ KEY_NAMES[KEY_F9] = "F9";
+ KEY_NAMES[KEY_F10] = "F10";
+ KEY_NAMES[KEY_F11] = "F11";
+ KEY_NAMES[KEY_F12] = "F12";
+ KEY_NAMES[KEY_F13] = "F13";
+ KEY_NAMES[KEY_F14] = "F14";
+ KEY_NAMES[KEY_F15] = "F15";
+
+ KEY_NAMES[KEY_NUMPAD0] = "Numpad 0";
+ KEY_NAMES[KEY_NUMPAD1] = "Numpad 1";
+ KEY_NAMES[KEY_NUMPAD2] = "Numpad 2";
+ KEY_NAMES[KEY_NUMPAD3] = "Numpad 3";
+ KEY_NAMES[KEY_NUMPAD4] = "Numpad 4";
+ KEY_NAMES[KEY_NUMPAD5] = "Numpad 5";
+ KEY_NAMES[KEY_NUMPAD6] = "Numpad 6";
+ KEY_NAMES[KEY_NUMPAD7] = "Numpad 7";
+ KEY_NAMES[KEY_NUMPAD8] = "Numpad 8";
+ KEY_NAMES[KEY_NUMPAD9] = "Numpad 9";
+
+ KEY_NAMES[KEY_NUMPADEQUALS] = "Numpad =";
+ KEY_NAMES[KEY_NUMPADENTER] = "Numpad Enter";
+ KEY_NAMES[KEY_NUMPADCOMMA] = "Numpad .";
+ KEY_NAMES[KEY_DIVIDE] = "Numpad /";
+
+
+ KEY_NAMES[KEY_LMENU] = "Left Alt";
+ KEY_NAMES[KEY_RMENU] = "Right Alt";
+
+ KEY_NAMES[KEY_LCONTROL] = "Left Ctrl";
+ KEY_NAMES[KEY_RCONTROL] = "Right Ctrl";
+
+ KEY_NAMES[KEY_LSHIFT] = "Left Shift";
+ KEY_NAMES[KEY_RSHIFT] = "Right Shift";
+
+ KEY_NAMES[KEY_LMETA] = "Left Option";
+ KEY_NAMES[KEY_RMETA] = "Right Option";
+
+ KEY_NAMES[KEY_MINUS] = "-";
+ KEY_NAMES[KEY_EQUALS] = "=";
+ KEY_NAMES[KEY_LBRACKET] = "[";
+ KEY_NAMES[KEY_RBRACKET] = "]";
+ KEY_NAMES[KEY_SEMICOLON] = ";";
+ KEY_NAMES[KEY_APOSTROPHE] = "'";
+ KEY_NAMES[KEY_GRAVE] = "`";
+ KEY_NAMES[KEY_BACKSLASH] = "\\";
+ KEY_NAMES[KEY_COMMA] = ",";
+ KEY_NAMES[KEY_PERIOD] = ".";
+ KEY_NAMES[KEY_SLASH] = "/";
+ KEY_NAMES[KEY_MULTIPLY] = "*";
+ KEY_NAMES[KEY_ADD] = "+";
+ KEY_NAMES[KEY_COLON] = ":";
+ KEY_NAMES[KEY_UNDERLINE] = "_";
+ KEY_NAMES[KEY_AT] = "@";
+
+ KEY_NAMES[KEY_APPS] = "Apps";
+ KEY_NAMES[KEY_POWER] = "Power";
+ KEY_NAMES[KEY_SLEEP] = "Sleep";
+
+ KEY_NAMES[KEY_STOP] = "Stop";
+ KEY_NAMES[KEY_ESCAPE] = "Esc";
+ KEY_NAMES[KEY_RETURN] = "Enter";
+ KEY_NAMES[KEY_SPACE] = "Space";
+ KEY_NAMES[KEY_BACK] = "Backspace";
+ KEY_NAMES[KEY_TAB] = "Tab";
+
+ KEY_NAMES[KEY_SYSRQ] = "SysEq";
+ KEY_NAMES[KEY_PAUSE] = "Pause";
+
+ KEY_NAMES[KEY_HOME] = "Home";
+ KEY_NAMES[KEY_PGUP] = "Page Up";
+ KEY_NAMES[KEY_PGDN] = "Page Down";
+ KEY_NAMES[KEY_END] = "End";
+ KEY_NAMES[KEY_INSERT] = "Insert";
+ KEY_NAMES[KEY_DELETE] = "Delete";
+
+ KEY_NAMES[KEY_UP] = "Up";
+ KEY_NAMES[KEY_LEFT] = "Left";
+ KEY_NAMES[KEY_RIGHT] = "Right";
+ KEY_NAMES[KEY_DOWN] = "Down";
+
+ KEY_NAMES[KEY_NUMLOCK] = "Num Lock";
+ KEY_NAMES[KEY_CAPITAL] = "Caps Lock";
+ KEY_NAMES[KEY_SCROLL] = "Scroll Lock";
+
+ KEY_NAMES[KEY_KANA] = "Kana";
+ KEY_NAMES[KEY_CONVERT] = "Convert";
+ KEY_NAMES[KEY_NOCONVERT] = "No Convert";
+ KEY_NAMES[KEY_YEN] = "Yen";
+ KEY_NAMES[KEY_CIRCUMFLEX] = "Circumflex";
+ KEY_NAMES[KEY_KANJI] = "Kanji";
+ KEY_NAMES[KEY_AX] = "Ax";
+ KEY_NAMES[KEY_UNLABELED] = "Unlabeled";
+ }
+
+ public String getName(int keyId){
+ return KEY_NAMES[keyId];
+ }
+}
diff --git a/engine/src/core/com/jme3/input/MouseInput.java b/engine/src/core/com/jme3/input/MouseInput.java
new file mode 100644
index 0000000..f4a1f36
--- /dev/null
+++ b/engine/src/core/com/jme3/input/MouseInput.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+/**
+ * A specific API for interfacing with the mouse.
+ */
+public interface MouseInput extends Input {
+
+ /**
+ * Mouse X axis.
+ */
+ public static final int AXIS_X = 0;
+
+ /**
+ * Mouse Y axis.
+ */
+ public static final int AXIS_Y = 1;
+
+ /**
+ * Mouse wheel axis.
+ */
+ public static final int AXIS_WHEEL = 2;
+
+ /**
+ * Left mouse button.
+ */
+ public static final int BUTTON_LEFT = 0;
+
+ /**
+ * Right mouse button.
+ */
+ public static final int BUTTON_RIGHT = 1;
+
+ /**
+ * Middle mouse button.
+ */
+ public static final int BUTTON_MIDDLE = 2;
+
+ /**
+ * Set whether the mouse cursor should be visible or not.
+ *
+ * @param visible Whether the mouse cursor should be visible or not.
+ */
+ public void setCursorVisible(boolean visible);
+
+ /**
+ * Returns the number of buttons the mouse has. Typically 3 for most mice.
+ *
+ * @return the number of buttons the mouse has.
+ */
+ public int getButtonCount();
+}
diff --git a/engine/src/core/com/jme3/input/RawInputListener.java b/engine/src/core/com/jme3/input/RawInputListener.java
new file mode 100644
index 0000000..dfdd69e
--- /dev/null
+++ b/engine/src/core/com/jme3/input/RawInputListener.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+import com.jme3.input.event.*;
+
+/**
+ * An interface used for receiving raw input from devices.
+ */
+public interface RawInputListener {
+
+ /**
+ * Called before a batch of input will be sent to this
+ * <code>RawInputListener</code>.
+ */
+ public void beginInput();
+
+ /**
+ * Called after a batch of input was sent to this
+ * <code>RawInputListener</code>.
+ *
+ * The listener should set the {@link InputEvent#setConsumed() consumed flag}
+ * on any events that have been consumed either at this call or previous calls.
+ */
+ public void endInput();
+
+ /**
+ * Invoked on joystick axis events.
+ *
+ * @param evt
+ */
+ public void onJoyAxisEvent(JoyAxisEvent evt);
+
+ /**
+ * Invoked on joystick button presses.
+ *
+ * @param evt
+ */
+ public void onJoyButtonEvent(JoyButtonEvent evt);
+
+ /**
+ * Invoked on mouse movement/motion events.
+ *
+ * @param evt
+ */
+ public void onMouseMotionEvent(MouseMotionEvent evt);
+
+ /**
+ * Invoked on mouse button events.
+ *
+ * @param evt
+ */
+ public void onMouseButtonEvent(MouseButtonEvent evt);
+
+ /**
+ * Invoked on keyboard key press or release events.
+ *
+ * @param evt
+ */
+ public void onKeyEvent(KeyInputEvent evt);
+
+
+ /**
+ * Invoked on touchscreen touch events.
+ *
+ * @param evt
+ */
+ public void onTouchEvent(TouchEvent evt);
+}
diff --git a/engine/src/core/com/jme3/input/TouchInput.java b/engine/src/core/com/jme3/input/TouchInput.java
new file mode 100644
index 0000000..2f45b44
--- /dev/null
+++ b/engine/src/core/com/jme3/input/TouchInput.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input;
+
+/**
+ * A specific API for interfacing with smartphone touch devices
+ */
+public interface TouchInput extends Input {
+
+ /**
+ * No filter, get all events
+ */
+ public static final int ALL = 0x00;
+ /**
+ * Home key
+ */
+ public static final int KEYCODE_HOME = 0x03;
+ /**
+ * Escape key.
+ */
+ public static final int KEYCODE_BACK = 0x04;
+ /**
+ * Context Menu key.
+ */
+ public static final int KEYCODE_MENU = 0x52;
+ /**
+ * Search key.
+ */
+ public static final int KEYCODE_SEARCH = 0x54;
+ /**
+ * Volume up key.
+ */
+ public static final int KEYCODE_VOLUME_UP = 0x18;
+ /**
+ * Volume down key.
+ */
+ public static final int KEYCODE_VOLUME_DOWN = 0x19;
+
+
+ /**
+ * Set if mouse events should be generated
+ *
+ * @param simulate if mouse events should be generated
+ */
+ public void setSimulateMouse(boolean simulate);
+
+ /**
+ * Set if keyboard events should be generated
+ *
+ * @param simulate if keyboard events should be generated
+ */
+ public void setSimulateKeyboard(boolean simulate);
+
+ /**
+ * Set if historic android events should be transmitted, can be used to get better performance and less mem
+ * @see <a href="http://developer.android.com/reference/android/view/MotionEvent.html#getHistoricalX%28int,%20int%29">
+ * http://developer.android.com/reference/android/view/MotionEvent.html#getHistoricalX%28int,%20int%29</a>
+ * @param dontSendHistory turn of historic events if true, false else and default
+ */
+ public void setOmitHistoricEvents(boolean dontSendHistory);
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/input/controls/ActionListener.java b/engine/src/core/com/jme3/input/controls/ActionListener.java
new file mode 100644
index 0000000..8deed3c
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/ActionListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+/**
+ * <code>ActionListener</code> is used to receive input events in "digital" style.
+ * <p>
+ * Generally all button inputs, such as keyboard, mouse button, and joystick button,
+ * will be represented exactly. Analog inputs will be converted into digital.
+ * <p>
+ * When an action listener is registered to a natively digital input, such as a button,
+ * the event will be invoked when the button is pressed, with <code>value</code>
+ * set to <code>true</code>, and will be invoked again when the button is released,
+ * with <code>value</code> set to <code>false</code>.
+ *
+ * @author Kirill Vainer
+ */
+public interface ActionListener extends InputListener {
+
+ /**
+ * Called when an input to which this listener is registered to is invoked.
+ *
+ * @param name The name of the mapping that was invoked
+ * @param isPressed True if the action is "pressed", false otherwise
+ * @param tpf The time per frame value.
+ */
+ public void onAction(String name, boolean isPressed, float tpf);
+}
diff --git a/engine/src/core/com/jme3/input/controls/AnalogListener.java b/engine/src/core/com/jme3/input/controls/AnalogListener.java
new file mode 100644
index 0000000..b28796a
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/AnalogListener.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+/**
+ * <code>AnalogListener</code> is used to receive events of inputs
+ * in analog format.
+ *
+ * @author Kirill Vainer
+ */
+public interface AnalogListener extends InputListener {
+ /**
+ * Called to notify the implementation that an analog event has occurred.
+ *
+ * The results of KeyTrigger and MouseButtonTrigger events will have tpf
+ * == value.
+ *
+ * @param name The name of the mapping that was invoked
+ * @param value Value of the axis, from 0 to 1.
+ * @param tpf The time per frame value.
+ */
+ public void onAnalog(String name, float value, float tpf);
+}
diff --git a/engine/src/core/com/jme3/input/controls/InputListener.java b/engine/src/core/com/jme3/input/controls/InputListener.java
new file mode 100644
index 0000000..ce140a9
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/InputListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+/**
+ * A generic interface for input listeners, the {@link AnalogListener} and
+ * {@link ActionListener} interfaces extend this interface.
+ *
+ * @author Kirill Vainer
+ */
+public interface InputListener {
+}
diff --git a/engine/src/core/com/jme3/input/controls/JoyAxisTrigger.java b/engine/src/core/com/jme3/input/controls/JoyAxisTrigger.java
new file mode 100644
index 0000000..539d83e
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/JoyAxisTrigger.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+import com.jme3.input.Joystick;
+
+public class JoyAxisTrigger implements Trigger {
+
+ private final int joyId, axisId;
+ private final boolean negative;
+
+ /**
+ * Use {@link Joystick#assignAxis(java.lang.String, java.lang.String, int) }
+ * instead.
+ */
+ public JoyAxisTrigger(int joyId, int axisId, boolean negative) {
+ this.joyId = joyId;
+ this.axisId = axisId;
+ this.negative = negative;
+ }
+
+ public static int joyAxisHash(int joyId, int joyAxis, boolean negative){
+ assert joyAxis >= 0 && joyAxis <= 255;
+ return (2048 * joyId) | (negative ? 1280 : 1024) | (joyAxis & 0xff);
+ }
+
+ public int getAxisId() {
+ return axisId;
+ }
+
+ public int getJoyId() {
+ return joyId;
+ }
+
+ public boolean isNegative() {
+ return negative;
+ }
+
+ public String getName() {
+ return "JoyAxis[joyId="+joyId+", axisId="+axisId+", neg="+negative+"]";
+ }
+
+ public int triggerHashCode() {
+ return joyAxisHash(joyId, axisId, negative);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/controls/JoyButtonTrigger.java b/engine/src/core/com/jme3/input/controls/JoyButtonTrigger.java
new file mode 100644
index 0000000..5e2cfd6
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/JoyButtonTrigger.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+import com.jme3.input.Joystick;
+
+public class JoyButtonTrigger implements Trigger {
+
+ private final int joyId, buttonId;
+
+ /**
+ * Use {@link Joystick#assignButton(java.lang.String, int) } instead.
+ *
+ * @param joyId
+ * @param axisId
+ */
+ public JoyButtonTrigger(int joyId, int axisId) {
+ this.joyId = joyId;
+ this.buttonId = axisId;
+ }
+
+ public static int joyButtonHash(int joyId, int joyButton){
+ assert joyButton >= 0 && joyButton <= 255;
+ return (2048 * joyId) | 1536 | (joyButton & 0xff);
+ }
+
+ public int getAxisId() {
+ return buttonId;
+ }
+
+ public int getJoyId() {
+ return joyId;
+ }
+
+ public String getName() {
+ return "JoyButton[joyId="+joyId+", axisId="+buttonId+"]";
+ }
+
+ public int triggerHashCode() {
+ return joyButtonHash(joyId, buttonId);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/controls/KeyTrigger.java b/engine/src/core/com/jme3/input/controls/KeyTrigger.java
new file mode 100644
index 0000000..993b4e7
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/KeyTrigger.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+import com.jme3.input.KeyInput;
+
+/**
+ * A <code>KeyTrigger</code> is used as a mapping to keyboard keys.
+ *
+ * @author Kirill Vainer
+ */
+public class KeyTrigger implements Trigger {
+
+ private final int keyCode;
+
+ /**
+ * Create a new <code>KeyTrigger</code> for the given keycode.
+ *
+ * @param keyCode the code for the key, see constants in {@link KeyInput}.
+ */
+ public KeyTrigger(int keyCode){
+ this.keyCode = keyCode;
+ }
+
+ public String getName() {
+ return "KeyCode " + keyCode;
+ }
+
+ public int getKeyCode(){
+ return keyCode;
+ }
+
+ public static int keyHash(int keyCode){
+ assert keyCode >= 0 && keyCode <= 255;
+ return keyCode & 0xff;
+ }
+
+ public int triggerHashCode() {
+ return keyHash(keyCode);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/controls/MouseAxisTrigger.java b/engine/src/core/com/jme3/input/controls/MouseAxisTrigger.java
new file mode 100644
index 0000000..49de745
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/MouseAxisTrigger.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+import com.jme3.input.MouseInput;
+
+/**
+ * A <code>MouseAxisTrigger</code> is used as a mapping to mouse axis,
+ * a mouse axis is movement along the X axis (left/right), Y axis (up/down)
+ * and the mouse wheel (scroll up/down).
+ *
+ * @author Kirill Vainer
+ */
+public class MouseAxisTrigger implements Trigger {
+
+ private int mouseAxis;
+ private boolean negative;
+
+ /**
+ * Create a new <code>MouseAxisTrigger</code>.
+ * <p>
+ * @param mouseAxis Mouse axis. See AXIS_*** constants in {@link MouseInput}
+ * @param negative True if listen to negative axis events, false if
+ * listen to positive axis events.
+ */
+ public MouseAxisTrigger(int mouseAxis, boolean negative){
+ if (mouseAxis < 0 || mouseAxis > 2)
+ throw new IllegalArgumentException("Mouse Axis must be between 0 and 2");
+
+ this.mouseAxis = mouseAxis;
+ this.negative = negative;
+ }
+
+ public int getMouseAxis(){
+ return mouseAxis;
+ }
+
+ public boolean isNegative() {
+ return negative;
+ }
+
+ public String getName() {
+ String sign = negative ? "Negative" : "Positive";
+ switch (mouseAxis){
+ case MouseInput.AXIS_X: return "Mouse X Axis " + sign;
+ case MouseInput.AXIS_Y: return "Mouse Y Axis " + sign;
+ case MouseInput.AXIS_WHEEL: return "Mouse Wheel " + sign;
+ default: throw new AssertionError();
+ }
+ }
+
+ public static int mouseAxisHash(int mouseAxis, boolean negative){
+ assert mouseAxis >= 0 && mouseAxis <= 255;
+ return (negative ? 768 : 512) | (mouseAxis & 0xff);
+ }
+
+ public int triggerHashCode() {
+ return mouseAxisHash(mouseAxis, negative);
+ }
+}
diff --git a/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java b/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java
new file mode 100644
index 0000000..ff013d6
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/MouseButtonTrigger.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+import com.jme3.input.MouseInput;
+
+/**
+ * A <code>MouseButtonTrigger</code> is used as a mapping to receive events
+ * from mouse buttons. It is generally expected for a mouse to have at least
+ * a left and right mouse button, but some mice may have a lot more buttons
+ * than that.
+ *
+ * @author Kirill Vainer
+ */
+public class MouseButtonTrigger implements Trigger {
+
+ private final int mouseButton;
+
+ /**
+ * Create a new <code>MouseButtonTrigger</code> to receive mouse button events.
+ *
+ * @param mouseButton Mouse button index. See BUTTON_*** constants in
+ * {@link MouseInput}.
+ */
+ public MouseButtonTrigger(int mouseButton) {
+ if (mouseButton < 0)
+ throw new IllegalArgumentException("Mouse Button cannot be negative");
+
+ this.mouseButton = mouseButton;
+ }
+
+ public int getMouseButton() {
+ return mouseButton;
+ }
+
+ public String getName() {
+ return "Mouse Button " + mouseButton;
+ }
+
+ public static int mouseButtonHash(int mouseButton){
+ assert mouseButton >= 0 && mouseButton <= 255;
+ return 256 | (mouseButton & 0xff);
+ }
+
+ public int triggerHashCode() {
+ return mouseButtonHash(mouseButton);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/controls/TouchListener.java b/engine/src/core/com/jme3/input/controls/TouchListener.java
new file mode 100644
index 0000000..21c100f
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/TouchListener.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+import com.jme3.input.event.TouchEvent;
+
+/**
+ * <code>TouchListener</code> is used to receive events of inputs from smartphone touch devices
+ *
+ * @author larynx
+ */
+public interface TouchListener extends InputListener {
+ /**
+ * @param name the name of the event
+ * @param event the touch event
+ * @param tpf how much time has passed since the last frame
+ */
+ public void onTouch(String name, TouchEvent event, float tpf);
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/input/controls/TouchTrigger.java b/engine/src/core/com/jme3/input/controls/TouchTrigger.java
new file mode 100644
index 0000000..25124ab
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/TouchTrigger.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+/**
+ * Class to trigger TouchEvents, keycode can be TouchInput.ALL(=0) or TouchInput.KEYCODE_*
+ * @author larynx
+ *
+ */
+public class TouchTrigger implements Trigger {
+
+ private final int keyCode;
+
+ /**
+ * Constructor
+ * @param keyCode can be zero to get all events or TouchInput.KEYCODE_*
+ */
+ public TouchTrigger(int keyCode) {
+ super();
+ this.keyCode = keyCode;
+ }
+
+ @Override
+ public String getName() {
+ if (keyCode != 0)
+ return "TouchInput";
+ else
+ return "TouchInput KeyCode " + keyCode;
+ }
+
+ public static int touchHash(int keyCode){
+ assert keyCode >= 0 && keyCode <= 255;
+ return 0xfedcba98 + keyCode;
+ }
+
+ public int triggerHashCode() {
+ return touchHash(keyCode);
+ }
+
+ public int getKeyCode(){
+ return keyCode;
+ }
+}
diff --git a/engine/src/core/com/jme3/input/controls/Trigger.java b/engine/src/core/com/jme3/input/controls/Trigger.java
new file mode 100644
index 0000000..9f059e8
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/Trigger.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.controls;
+
+/**
+ * A trigger represents a physical input, such as a keyboard key, a mouse
+ * button, or joystick axis.
+ */
+public interface Trigger {
+
+ /**
+ * @return A user friendly name for the trigger.
+ */
+ public String getName();
+
+ /**
+ * Returns the hash code for the trigger.
+ *
+ * @return the hash code for the trigger.
+ */
+ public int triggerHashCode();
+}
diff --git a/engine/src/core/com/jme3/input/controls/package.html b/engine/src/core/com/jme3/input/controls/package.html
new file mode 100644
index 0000000..2303ea9
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/package.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.input.controls</code> package allows user code to listen
+to input events regardless of the type of input used.
+<p>
+Users will receive input in one of two forms, either
+{@link com.jme3.input.controls.AnalogListener analog input} or
+{@link com.jme3.input.controls.Action digital/action input}.
+
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/input/dummy/DummyInput.java b/engine/src/core/com/jme3/input/dummy/DummyInput.java
new file mode 100644
index 0000000..3ecb509
--- /dev/null
+++ b/engine/src/core/com/jme3/input/dummy/DummyInput.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.dummy;
+
+import com.jme3.input.Input;
+import com.jme3.input.RawInputListener;
+
+/**
+ * DummyInput as an implementation of <code>Input</code> that raises no
+ * input events.
+ *
+ * @author Kirill Vainer.
+ */
+public class DummyInput implements Input {
+
+ protected boolean inited = false;
+
+ public void initialize() {
+ if (inited)
+ throw new IllegalStateException("Input already initialized.");
+
+ inited = true;
+ }
+
+ public void update() {
+ if (!inited)
+ throw new IllegalStateException("Input not initialized.");
+ }
+
+ public void destroy() {
+ if (!inited)
+ throw new IllegalStateException("Input not initialized.");
+
+ inited = false;
+ }
+
+ public boolean isInitialized() {
+ return inited;
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ }
+
+ public long getInputTimeNanos() {
+ return System.currentTimeMillis() * 1000000;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/dummy/DummyKeyInput.java b/engine/src/core/com/jme3/input/dummy/DummyKeyInput.java
new file mode 100644
index 0000000..4d4efcb
--- /dev/null
+++ b/engine/src/core/com/jme3/input/dummy/DummyKeyInput.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.dummy;
+
+import com.jme3.input.KeyInput;
+
+/**
+ * DummyKeyInput as an implementation of <code>KeyInput</code> that raises no
+ * input events.
+ *
+ * @author Kirill Vainer.
+ */
+public class DummyKeyInput extends DummyInput implements KeyInput {
+
+ public int getKeyCount() {
+ if (!inited)
+ throw new IllegalStateException("Input not initialized.");
+
+ return 0;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/dummy/DummyMouseInput.java b/engine/src/core/com/jme3/input/dummy/DummyMouseInput.java
new file mode 100644
index 0000000..b862061
--- /dev/null
+++ b/engine/src/core/com/jme3/input/dummy/DummyMouseInput.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.dummy;
+
+import com.jme3.input.MouseInput;
+
+/**
+ * DummyMouseInput as an implementation of <code>MouseInput</code> that raises no
+ * input events.
+ *
+ * @author Kirill Vainer.
+ */
+public class DummyMouseInput extends DummyInput implements MouseInput {
+
+ public void setCursorVisible(boolean visible) {
+ if (!inited)
+ throw new IllegalStateException("Input not initialized.");
+ }
+
+ public int getButtonCount() {
+ return 0;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/dummy/package.html b/engine/src/core/com/jme3/input/dummy/package.html
new file mode 100644
index 0000000..13f5c55
--- /dev/null
+++ b/engine/src/core/com/jme3/input/dummy/package.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.input.dummy</code> package provides "dummy" or "null"
+implementations of the input interfaces.
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/input/event/InputEvent.java b/engine/src/core/com/jme3/input/event/InputEvent.java
new file mode 100644
index 0000000..bb3867d
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/InputEvent.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.event;
+
+import com.jme3.input.Input;
+
+/**
+ * An abstract input event.
+ */
+public abstract class InputEvent {
+
+ protected long time;
+
+
+ protected boolean consumed = false;
+
+ /**
+ * The time when the event occurred. This is relative to
+ * {@link Input#getInputTimeNanos() }.
+ *
+ * @return time when the event occured
+ */
+ public long getTime(){
+ return time;
+ }
+
+ /**
+ * Set the time when the event occurred.
+ *
+ * @param time time when the event occurred.
+ */
+ public void setTime(long time){
+ this.time = time;
+ }
+
+ /**
+ * Returns true if the input event has been consumed, meaning it is no longer valid
+ * and should not be forwarded to input listeners.
+ *
+ * @return true if the input event has been consumed
+ */
+ public boolean isConsumed() {
+ return consumed;
+ }
+
+ /**
+ * Call to mark this input event as consumed, meaning it is no longer valid
+ * and should not be forwarded to input listeners.
+ */
+ public void setConsumed() {
+ this.consumed = true;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/event/JoyAxisEvent.java b/engine/src/core/com/jme3/input/event/JoyAxisEvent.java
new file mode 100644
index 0000000..2896b0b
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/JoyAxisEvent.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.event;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.Joystick;
+
+/**
+ * Joystick axis event.
+ *
+ * @author Kirill Vainer
+ */
+public class JoyAxisEvent extends InputEvent {
+
+ private int joyIdx;
+ private int axisIdx;
+ private float value;
+
+ public JoyAxisEvent(int joyIdx, int axisIdx, float value) {
+ this.joyIdx = joyIdx;
+ this.axisIdx = axisIdx;
+ this.value = value;
+ }
+
+ /**
+ * Returns the joystick axis index.
+ *
+ * @return joystick axis index.
+ *
+ * @see Joystick#assignAxis(java.lang.String, java.lang.String, int)
+ */
+ public int getAxisIndex() {
+ return axisIdx;
+ }
+
+ /**
+ * The joystick index.
+ *
+ * @return joystick index.
+ *
+ * @see InputManager#getJoysticks()
+ */
+ public int getJoyIndex() {
+ return joyIdx;
+ }
+
+ /**
+ * The value of the axis.
+ *
+ * @return value of the axis.
+ */
+ public float getValue() {
+ return value;
+ }
+}
diff --git a/engine/src/core/com/jme3/input/event/JoyButtonEvent.java b/engine/src/core/com/jme3/input/event/JoyButtonEvent.java
new file mode 100644
index 0000000..906847f
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/JoyButtonEvent.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.event;
+
+import com.jme3.input.Joystick;
+
+/**
+ * Joystick button event.
+ *
+ * @author Kirill Vainer
+ */
+public class JoyButtonEvent extends InputEvent {
+
+ private int joyIdx;
+ private int btnIdx;
+ private boolean pressed;
+
+ public JoyButtonEvent(int joyIdx, int btnIdx, boolean pressed) {
+ this.joyIdx = joyIdx;
+ this.btnIdx = btnIdx;
+ this.pressed = pressed;
+ }
+
+ /**
+ * The button index.
+ *
+ * @return button index.
+ *
+ * @see Joystick#assignButton(java.lang.String, int)
+ */
+ public int getButtonIndex() {
+ return btnIdx;
+ }
+
+ /**
+ * The joystick index.
+ *
+ * @return joystick index.
+ *
+ * @see InputManager#getJoysticks()
+ */
+ public int getJoyIndex() {
+ return joyIdx;
+ }
+
+ /**
+ * Returns true if the event was a button press,
+ * returns false if the event was a button release.
+ *
+ * @return true if the event was a button press,
+ * false if the event was a button release.
+ */
+ public boolean isPressed() {
+ return pressed;
+ }
+
+
+
+}
diff --git a/engine/src/core/com/jme3/input/event/KeyInputEvent.java b/engine/src/core/com/jme3/input/event/KeyInputEvent.java
new file mode 100644
index 0000000..403635c
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/KeyInputEvent.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.event;
+
+import com.jme3.input.KeyInput;
+
+/**
+ * Keyboard key event.
+ *
+ * @author Kirill Vainer
+ */
+public class KeyInputEvent extends InputEvent {
+
+ private int keyCode;
+ private char keyChar;
+ private boolean pressed;
+ private boolean repeating;
+
+ public KeyInputEvent(int keyCode, char keyChar, boolean pressed, boolean repeating) {
+ this.keyCode = keyCode;
+ this.keyChar = keyChar;
+ this.pressed = pressed;
+ this.repeating = repeating;
+ }
+
+ /**
+ * Returns the key character. Returns 0 if the key has no character.
+ *
+ * @return the key character. 0 if the key has no character.
+ */
+ public char getKeyChar() {
+ return keyChar;
+ }
+
+ /**
+ * The key code.
+ * <p>
+ * See KEY_*** constants in {@link KeyInput}.
+ *
+ * @return key code.
+ */
+ public int getKeyCode() {
+ return keyCode;
+ }
+
+ /**
+ * Returns true if this event is key press, false is it was a key release.
+ *
+ * @return true if this event is key press, false is it was a key release.
+ */
+ public boolean isPressed() {
+ return pressed;
+ }
+
+ /**
+ * Returns true if this event is a repeat event. Not used anymore.
+ *
+ * @return true if this event is a repeat event
+ */
+ public boolean isRepeating() {
+ return repeating;
+ }
+
+ /**
+ * Returns true if this event is a key release, false if it was a key press.
+ *
+ * @return true if this event is a key release, false if it was a key press.
+ */
+ public boolean isReleased() {
+ return !pressed;
+ }
+
+ @Override
+ public String toString(){
+ String str = "Key(CODE="+keyCode;
+ if (keyChar != '\0')
+ str = str + ", CHAR=" + keyChar;
+
+ if (repeating){
+ return str + ", REPEATING)";
+ }else if (pressed){
+ return str + ", PRESSED)";
+ }else{
+ return str + ", RELEASED)";
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/input/event/MouseButtonEvent.java b/engine/src/core/com/jme3/input/event/MouseButtonEvent.java
new file mode 100644
index 0000000..66af54d
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/MouseButtonEvent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.event;
+
+import com.jme3.input.MouseInput;
+
+/**
+ * Mouse button press/release event.
+ *
+ * @author Kirill Vainer
+ */
+public class MouseButtonEvent extends InputEvent {
+
+ private int x;
+ private int y;
+ private int btnIndex;
+ private boolean pressed;
+
+ public MouseButtonEvent(int btnIndex, boolean pressed, int x, int y) {
+ this.btnIndex = btnIndex;
+ this.pressed = pressed;
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Returns the mouse button index.
+ * <p>
+ * See constants in {@link MouseInput}.
+ *
+ * @return the mouse button index.
+ */
+ public int getButtonIndex() {
+ return btnIndex;
+ }
+
+ /**
+ * Returns true if the mouse button was pressed, false if it was released.
+ *
+ * @return true if the mouse button was pressed, false if it was released.
+ */
+ public boolean isPressed() {
+ return pressed;
+ }
+
+ /**
+ * Returns true if the mouse button was released, false if it was pressed.
+ *
+ * @return true if the mouse button was released, false if it was pressed.
+ */
+ public boolean isReleased() {
+ return !pressed;
+ }
+
+ /**
+ * The X coordinate of the mouse when the event was generated.
+ * @return X coordinate of the mouse when the event was generated.
+ */
+ public int getX() {
+ return x;
+ }
+
+ /**
+ * The Y coordinate of the mouse when the event was generated.
+ * @return Y coordinate of the mouse when the event was generated.
+ */
+ public int getY() {
+ return y;
+ }
+
+ @Override
+ public String toString(){
+ String str = "MouseButton(BTN="+btnIndex;
+ if (pressed){
+ return str + ", PRESSED)";
+ }else{
+ return str + ", RELEASED)";
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/event/MouseMotionEvent.java b/engine/src/core/com/jme3/input/event/MouseMotionEvent.java
new file mode 100644
index 0000000..7439ecc
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/MouseMotionEvent.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.event;
+
+/**
+ * Mouse movement event.
+ * <p>
+ * Movement events are only generated if the mouse is on-screen.
+ *
+ * @author Kirill Vainer
+ */
+public class MouseMotionEvent extends InputEvent {
+
+ private int x, y, dx, dy, wheel, deltaWheel;
+
+ public MouseMotionEvent(int x, int y, int dx, int dy, int wheel, int deltaWheel) {
+ this.x = x;
+ this.y = y;
+ this.dx = dx;
+ this.dy = dy;
+ this.wheel = wheel;
+ this.deltaWheel = deltaWheel;
+ }
+
+ /**
+ * The change in wheel rotation.
+ *
+ * @return change in wheel rotation.
+ */
+ public int getDeltaWheel() {
+ return deltaWheel;
+ }
+
+ /**
+ * The change in X coordinate
+ * @return change in X coordinate
+ */
+ public int getDX() {
+ return dx;
+ }
+
+ /**
+ * The change in Y coordinate
+ *
+ * @return change in Y coordinate
+ */
+ public int getDY() {
+ return dy;
+ }
+
+ /**
+ * Current mouse wheel value
+ * @return Current mouse wheel value
+ */
+ public int getWheel() {
+ return wheel;
+ }
+
+ /**
+ * Current X coordinate
+ * @return Current X coordinate
+ */
+ public int getX() {
+ return x;
+ }
+
+ /**
+ * Current Y coordinate
+ * @return Current Y coordinate
+ */
+ public int getY() {
+ return y;
+ }
+
+ @Override
+ public String toString(){
+ return "MouseMotion(X="+x+", Y="+y+", DX="+dx+", DY="+dy+")";
+ }
+
+}
diff --git a/engine/src/core/com/jme3/input/event/TouchEvent.java b/engine/src/core/com/jme3/input/event/TouchEvent.java
new file mode 100644
index 0000000..8834c85
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/TouchEvent.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.input.event;
+
+/**
+ * <code>TouchEvent</code> represents a single event from multi-touch input devices
+ * @author larynx
+ */
+public class TouchEvent extends InputEvent {
+
+ public enum Type {
+
+ /**
+ * Touch down event, fields: posX, posY, pressure
+ */
+ DOWN,
+ /**
+ * Move/Drag event, fields: posX, posY, deltaX, deltaY, pressure
+ */
+ MOVE,
+ /**
+ * Touch up event, fields: posX, posY, pressure
+ */
+ UP,
+ /**
+ * Virtual keyboard or hardware key event down, fields: keyCode, characters
+ */
+ KEY_DOWN,
+ /**
+ * Virtual keyboard or hardware key event up, fields: keyCode, characters
+ */
+ KEY_UP,
+ // Single finger gestures
+ FLING,
+ TAP,
+ DOUBLETAP,
+ LONGPRESSED,
+ // Two finger scale events
+ /**
+ * Two finger scale event start, fields: posX/posY = getFocusX/Y, scaleFactor, scaleSpan
+ */
+ SCALE_START,
+ /**
+ * Two finger scale event, fields: posX/posY = getFocusX/Y, scaleFactor, scaleSpan
+ */
+ SCALE_MOVE,
+ /**
+ * Two finger scale event end, fields: posX/posY = getFocusX/Y, scaleFactor, scaleSpan
+ */
+ SCALE_END,
+ /**
+ * Scroll event
+ */
+ SCROLL,
+ /**
+ * The user has performed a down MotionEvent and not performed a move or up yet. This event is commonly used to provide visual feedback to the user to let them know that their action has been recognized i.e. highlight an element.
+ */
+ SHOWPRESS,
+ // Others
+ OUTSIDE,
+ IDLE
+ }
+ private Type type = Type.IDLE;
+ private int pointerId;
+ private float posX;
+ private float posY;
+ private float deltaX;
+ private float deltaY;
+ private float pressure;
+
+ // Used only with KEY* events
+ private int keyCode;
+ private String characters;
+ // Used only with SCALE* events
+ private float scaleFactor;
+ private float scaleSpan;
+
+ public TouchEvent() {
+ set(Type.IDLE, 0f, 0f, 0f, 0f);
+ }
+
+ public TouchEvent(Type type, float x, float y, float deltax, float deltay) {
+ set(type, x, y, deltax, deltay);
+ }
+
+ public void set(Type type) {
+ set(type, 0f, 0f, 0f, 0f);
+ }
+
+ public void set(Type type, float x, float y, float deltax, float deltay) {
+ this.type = type;
+ this.posX = x;
+ this.posY = y;
+ this.deltaX = deltax;
+ this.deltaY = deltay;
+ pointerId = 0;
+ pressure = 0;
+ keyCode = 0;
+ scaleFactor = 0;
+ scaleSpan = 0;
+ characters = "";
+ consumed = false;
+ }
+
+ /**
+ * Returns the type of touch event.
+ *
+ * @return the type of touch event.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ public float getX() {
+ return posX;
+ }
+
+ public float getY() {
+ return posY;
+ }
+
+ public float getDeltaX() {
+ return deltaX;
+ }
+
+ public float getDeltaY() {
+ return deltaY;
+ }
+
+ public float getPressure()
+ {
+ return pressure;
+ }
+
+ public void setPressure(float pressure)
+ {
+ this.pressure = pressure;
+ }
+
+ public int getPointerId()
+ {
+ return pointerId;
+ }
+
+ public void setPointerId(int pointerId) {
+ this.pointerId = pointerId;
+ }
+
+ public int getKeyCode() {
+ return keyCode;
+ }
+
+ public void setKeyCode(int keyCode) {
+ this.keyCode = keyCode;
+ }
+
+ public String getCharacters() {
+ return characters;
+ }
+
+ public void setCharacters(String characters) {
+ this.characters = characters;
+ }
+
+ public float getScaleFactor() {
+ return scaleFactor;
+ }
+
+ public void setScaleFactor(float scaleFactor) {
+ this.scaleFactor = scaleFactor;
+ }
+
+ public float getScaleSpan() {
+ return scaleSpan;
+ }
+
+ public void setScaleSpan(float scaleSpan) {
+ this.scaleSpan = scaleSpan;
+ }
+}
diff --git a/engine/src/core/com/jme3/input/event/package.html b/engine/src/core/com/jme3/input/event/package.html
new file mode 100644
index 0000000..eaa96fb
--- /dev/null
+++ b/engine/src/core/com/jme3/input/event/package.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.input.event</code> package contains low-level input events.
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/input/package.html b/engine/src/core/com/jme3/input/package.html
new file mode 100644
index 0000000..998a22a
--- /dev/null
+++ b/engine/src/core/com/jme3/input/package.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.input</code> package is used for all input handling in
+jMonkeyEngine. User code should use the {@link com.jme3.input.InputManager} to register
+for and receive input events. The <code>InputManager</code> can be
+retrieved for an application by using {@link com.jme3.app.Application#getInputManager()}.
+
+<h3>Usage</h3>
+
+<p>
+Using ActionListener:<br>
+<code>
+// Retrieve an input manager for the application "app"<br>
+InputManager inputManager = app.getInputManager();<br>
+<br>
+// Adds a new mapping "PrintHello" that will be invoked when the Return/Enter key is pressed<br>
+inputManager.addMapping("PrintHello", new KeyTrigger(KeyInput.KEY_RETURN));<br>
+// Adds a new ActionListener to get an event when enter is pressed.<br>
+inputManager.addListener(new ActionListener() {<br>
+ public void onAction(String name, boolean isPressed, float tpf) {<br>
+ // Only invoke the event when the mapping is "PrintHello" <br>
+ // and isPressed is true, meaning it was a key press and not release.<br>
+ if (name.equals("PrintHello") && isPressed){<br>
+ System.out.println("Hello!");<br>
+ }<br>
+ }<br>
+}, "PrintHello");<br>
+</code>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/light/AmbientLight.java b/engine/src/core/com/jme3/light/AmbientLight.java
new file mode 100644
index 0000000..736cc12
--- /dev/null
+++ b/engine/src/core/com/jme3/light/AmbientLight.java
@@ -0,0 +1,26 @@
+package com.jme3.light;
+
+import com.jme3.scene.Spatial;
+
+/**
+ * An ambient light adds a constant color to the scene.
+ * <p>
+ * Ambient lights are unaffected by the surface normal, and are constant
+ * regardless of the model's location. The material's ambient color is
+ * multiplied by the ambient light color to get the final ambient color of
+ * an object.
+ *
+ * @author Kirill Vainer
+ */
+public class AmbientLight extends Light {
+
+ @Override
+ public void computeLastDistance(Spatial owner) {
+ }
+
+ @Override
+ public Type getType() {
+ return Type.Ambient;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/light/DirectionalLight.java b/engine/src/core/com/jme3/light/DirectionalLight.java
new file mode 100644
index 0000000..e2bcdee
--- /dev/null
+++ b/engine/src/core/com/jme3/light/DirectionalLight.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.light;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * <code>DirectionalLight</code> is a light coming from a certain direction in world space.
+ * E.g sun or moon light.
+ * <p>
+ * Directional lights have no specific position in the scene, they always
+ * come from their direction regardless of where an object is placed.
+ */
+public class DirectionalLight extends Light {
+
+ protected Vector3f direction = new Vector3f(0f, -1f, 0f);
+
+ @Override
+ public void computeLastDistance(Spatial owner) {
+ lastDistance = 0; // directional lights are always closest to their owner
+ }
+
+ /**
+ * Returns the direction vector of the light.
+ *
+ * @return The direction vector of the light.
+ *
+ * @see DirectionalLight#setDirection(com.jme3.math.Vector3f)
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * Sets the direction of the light.
+ * <p>
+ * Represents the vector direction the light is coming from.
+ * (1, 0, 0) would represent a directional light coming from the X axis.
+ *
+ * @param dir the direction of the light.
+ */
+ public void setDirection(Vector3f dir){
+ direction.set(dir);
+ if (!direction.isUnitVector()) {
+ direction.normalizeLocal();
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return Type.Directional;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(direction, "direction", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ direction = (Vector3f) ic.readSavable("direction", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/light/Light.java b/engine/src/core/com/jme3/light/Light.java
new file mode 100644
index 0000000..965eaf9
--- /dev/null
+++ b/engine/src/core/com/jme3/light/Light.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.light;
+
+import com.jme3.export.*;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * Abstract class for representing a light source.
+ * <p>
+ * All light source types have a color.
+ */
+public abstract class Light implements Savable, Cloneable {
+
+ /**
+ * Describes the light type.
+ */
+ public enum Type {
+
+ /**
+ * Directional light
+ *
+ * @see DirectionalLight
+ */
+ Directional(0),
+
+ /**
+ * Point light
+ *
+ * @see PointLight
+ */
+ Point(1),
+
+ /**
+ * Spot light.
+ * <p>
+ * Not supported by jMonkeyEngine
+ */
+ Spot(2),
+
+ /**
+ * Ambient light
+ *
+ * @see AmbientLight
+ */
+ Ambient(3);
+
+ private int typeId;
+
+ Type(int type){
+ this.typeId = type;
+ }
+
+ /**
+ * Returns an index for the light type
+ * @return an index for the light type
+ */
+ public int getId(){
+ return typeId;
+ }
+ }
+
+ protected ColorRGBA color = new ColorRGBA(1f,1f,1f,1f);
+
+ /**
+ * Used in LightList for caching the distance
+ * to the owner spatial. Should be reset after the sorting.
+ */
+ protected transient float lastDistance = -1;
+
+ /**
+ * If light is disabled, it will not have any
+ */
+ protected boolean enabled = true;
+
+ /**
+ * The light name.
+ */
+ protected String name;
+
+ /**
+ * Returns the color of the light.
+ *
+ * @return The color of the light.
+ */
+ public ColorRGBA getColor() {
+ return color;
+ }
+
+ /**
+ * This method sets the light name.
+ *
+ * @param name the light name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Return the light name.
+ *
+ * @return the light name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /*
+ public void setLastDistance(float lastDistance){
+ this.lastDistance = lastDistance;
+ }
+
+ public float getLastDistance(){
+ return lastDistance;
+ }
+ */
+
+ /**
+ * Sets the light color.
+ *
+ * @param color the light color.
+ */
+ public void setColor(ColorRGBA color){
+ this.color.set(color);
+ }
+
+
+ /*
+ * Returns true if the light is enabled
+ *
+ * @return true if the light is enabled
+ *
+ * @see Light#setEnabled(boolean)
+ */
+ /*
+ public boolean isEnabled() {
+ return enabled;
+ }
+ */
+
+ @Override
+ public Light clone(){
+ try {
+ return (Light) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(color, "color", null);
+ oc.write(enabled, "enabled", true);
+ oc.write(name, "name", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ color = (ColorRGBA) ic.readSavable("color", null);
+ enabled = ic.readBoolean("enabled", true);
+ name = ic.readString("name", null);
+ }
+
+ /**
+ * Used internally to compute the last distance value.
+ */
+ protected abstract void computeLastDistance(Spatial owner);
+
+ /**
+ * Returns the light type
+ *
+ * @return the light type
+ *
+ * @see Type
+ */
+ public abstract Type getType();
+
+}
diff --git a/engine/src/core/com/jme3/light/LightList.java b/engine/src/core/com/jme3/light/LightList.java
new file mode 100644
index 0000000..00b6b4d
--- /dev/null
+++ b/engine/src/core/com/jme3/light/LightList.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.light;
+
+import com.jme3.export.*;
+import com.jme3.scene.Spatial;
+import com.jme3.util.SortUtil;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * <code>LightList</code> is used internally by {@link Spatial}s to manage
+ * lights that are attached to them.
+ *
+ * @author Kirill Vainer
+ */
+public final class LightList implements Iterable<Light>, Savable, Cloneable {
+
+ private Light[] list, tlist;
+ private float[] distToOwner;
+ private int listSize;
+ private Spatial owner;
+
+ private static final int DEFAULT_SIZE = 1;
+
+ private static final Comparator<Light> c = new Comparator<Light>() {
+ /**
+ * This assumes lastDistance have been computed in a previous step.
+ */
+ public int compare(Light l1, Light l2) {
+ if (l1.lastDistance < l2.lastDistance)
+ return -1;
+ else if (l1.lastDistance > l2.lastDistance)
+ return 1;
+ else
+ return 0;
+ }
+ };
+
+ /**
+ * Default constructor for serialization. Do not use
+ */
+ public LightList(){
+ }
+
+ /**
+ * Creates a <code>LightList</code> for the given {@link Spatial}.
+ *
+ * @param owner The spatial owner
+ */
+ public LightList(Spatial owner) {
+ listSize = 0;
+ list = new Light[DEFAULT_SIZE];
+ distToOwner = new float[DEFAULT_SIZE];
+ Arrays.fill(distToOwner, Float.NEGATIVE_INFINITY);
+ this.owner = owner;
+ }
+
+ /**
+ * Set the owner of the LightList. Only used for cloning.
+ * @param owner
+ */
+ public void setOwner(Spatial owner){
+ this.owner = owner;
+ }
+
+ private void doubleSize(){
+ Light[] temp = new Light[list.length * 2];
+ float[] temp2 = new float[list.length * 2];
+ System.arraycopy(list, 0, temp, 0, list.length);
+ System.arraycopy(distToOwner, 0, temp2, 0, list.length);
+ list = temp;
+ distToOwner = temp2;
+ }
+
+ /**
+ * Adds a light to the list. List size is doubled if there is no room.
+ *
+ * @param l
+ * The light to add.
+ */
+ public void add(Light l) {
+ if (listSize == list.length) {
+ doubleSize();
+ }
+ list[listSize] = l;
+ distToOwner[listSize++] = Float.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * Remove the light at the given index.
+ *
+ * @param index
+ */
+ public void remove(int index){
+ if (index >= listSize || index < 0)
+ throw new IndexOutOfBoundsException();
+
+ listSize --;
+ if (index == listSize){
+ list[listSize] = null;
+ return;
+ }
+
+ for (int i = index; i < listSize; i++){
+ list[i] = list[i+1];
+ }
+ list[listSize] = null;
+ }
+
+ /**
+ * Removes the given light from the LightList.
+ *
+ * @param l the light to remove
+ */
+ public void remove(Light l){
+ for (int i = 0; i < listSize; i++){
+ if (list[i] == l){
+ remove(i);
+ return;
+ }
+ }
+ }
+
+ /**
+ * @return The size of the list.
+ */
+ public int size(){
+ return listSize;
+ }
+
+ /**
+ * @return the light at the given index.
+ * @throws IndexOutOfBoundsException If the given index is outside bounds.
+ */
+ public Light get(int num){
+ if (num >= listSize || num < 0)
+ throw new IndexOutOfBoundsException();
+
+ return list[num];
+ }
+
+ /**
+ * Resets list size to 0.
+ */
+ public void clear() {
+ if (listSize == 0)
+ return;
+
+ for (int i = 0; i < listSize; i++)
+ list[i] = null;
+
+ if (tlist != null)
+ Arrays.fill(tlist, null);
+
+ listSize = 0;
+ }
+
+ /**
+ * Sorts the elements in the list acording to their Comparator.
+ * There are two reasons why lights should be resorted.
+ * First, if the lights have moved, that means their distance to
+ * the spatial changed.
+ * Second, if the spatial itself moved, it means the distance from it to
+ * the individual lights might have changed.
+ *
+ *
+ * @param transformChanged Whether the spatial's transform has changed
+ */
+ public void sort(boolean transformChanged) {
+ if (listSize > 1) {
+ // resize or populate our temporary array as necessary
+ if (tlist == null || tlist.length != list.length) {
+ tlist = list.clone();
+ } else {
+ System.arraycopy(list, 0, tlist, 0, list.length);
+ }
+
+ if (transformChanged){
+ // check distance of each light
+ for (int i = 0; i < listSize; i++){
+ list[i].computeLastDistance(owner);
+ }
+ }
+
+ // now merge sort tlist into list
+ SortUtil.msort(tlist, list, 0, listSize - 1, c);
+ }
+ }
+
+ /**
+ * Updates a "world-space" light list, using the spatial's local-space
+ * light list and its parent's world-space light list.
+ *
+ * @param local
+ * @param parent
+ */
+ public void update(LightList local, LightList parent){
+ // clear the list as it will be reconstructed
+ // using the arguments
+ clear();
+
+ while (list.length <= local.listSize){
+ doubleSize();
+ }
+
+ // add the lights from the local list
+ System.arraycopy(local.list, 0, list, 0, local.listSize);
+ for (int i = 0; i < local.listSize; i++){
+// list[i] = local.list[i];
+ distToOwner[i] = Float.NEGATIVE_INFINITY;
+ }
+
+ // if the spatial has a parent node, add the lights
+ // from the parent list as well
+ if (parent != null){
+ int sz = local.listSize + parent.listSize;
+ while (list.length <= sz)
+ doubleSize();
+
+ for (int i = 0; i < parent.listSize; i++){
+ int p = i + local.listSize;
+ list[p] = parent.list[i];
+ distToOwner[p] = Float.NEGATIVE_INFINITY;
+ }
+
+ listSize = local.listSize + parent.listSize;
+ }else{
+ listSize = local.listSize;
+ }
+ }
+
+ /**
+ * Returns an iterator that can be used to iterate over this LightList.
+ *
+ * @return an iterator that can be used to iterate over this LightList.
+ */
+ public Iterator<Light> iterator() {
+ return new Iterator<Light>(){
+
+ int index = 0;
+
+ public boolean hasNext() {
+ return index < size();
+ }
+
+ public Light next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return list[index++];
+ }
+
+ public void remove() {
+ LightList.this.remove(--index);
+ }
+ };
+ }
+
+ @Override
+ public LightList clone(){
+ try{
+ LightList clone = (LightList) super.clone();
+
+ clone.owner = null;
+ clone.list = list.clone();
+ clone.distToOwner = distToOwner.clone();
+ clone.tlist = null; // list used for sorting only
+
+ return clone;
+ }catch (CloneNotSupportedException ex){
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+// oc.write(owner, "owner", null);
+
+ ArrayList<Light> lights = new ArrayList<Light>();
+ for (int i = 0; i < listSize; i++){
+ lights.add(list[i]);
+ }
+ oc.writeSavableArrayList(lights, "lights", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+// owner = (Spatial) ic.readSavable("owner", null);
+
+ List<Light> lights = ic.readSavableArrayList("lights", null);
+ listSize = lights.size();
+
+ // NOTE: make sure the array has a length of at least 1
+ int arraySize = Math.max(DEFAULT_SIZE, listSize);
+ list = new Light[arraySize];
+ distToOwner = new float[arraySize];
+
+ for (int i = 0; i < listSize; i++){
+ list[i] = lights.get(i);
+ }
+
+ Arrays.fill(distToOwner, Float.NEGATIVE_INFINITY);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/light/PointLight.java b/engine/src/core/com/jme3/light/PointLight.java
new file mode 100644
index 0000000..e1deeac
--- /dev/null
+++ b/engine/src/core/com/jme3/light/PointLight.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.light;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * Represents a point light.
+ * A point light emits light from a given position into all directions in space.
+ * E.g a lamp or a bright effect. Point light positions are in world space.
+ * <p>
+ * In addition to a position, point lights also have a radius which
+ * can be used to attenuate the influence of the light depending on the
+ * distance between the light and the effected object.
+ *
+ */
+public class PointLight extends Light {
+
+ protected Vector3f position = new Vector3f();
+ protected float radius = 0;
+ protected float invRadius = 0;
+
+ @Override
+ public void computeLastDistance(Spatial owner) {
+ if (owner.getWorldBound() != null) {
+ BoundingVolume bv = owner.getWorldBound();
+ lastDistance = bv.distanceSquaredTo(position);
+ } else {
+ lastDistance = owner.getWorldTranslation().distanceSquared(position);
+ }
+ }
+
+ /**
+ * Returns the world space position of the light.
+ *
+ * @return the world space position of the light.
+ *
+ * @see PointLight#setPosition(com.jme3.math.Vector3f)
+ */
+ public Vector3f getPosition() {
+ return position;
+ }
+
+ /**
+ * Set the world space position of the light.
+ *
+ * @param position the world space position of the light.
+ */
+ public void setPosition(Vector3f position) {
+ this.position.set(position);
+ }
+
+ /**
+ * Returns the radius of the light influence. A radius of 0 means
+ * the light has no attenuation.
+ *
+ * @return the radius of the light
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * Set the radius of the light influence.
+ * <p>
+ * Setting a non-zero radius indicates the light should use attenuation.
+ * If a pixel's distance to this light's position
+ * is greater than the light's radius, then the pixel will not be
+ * effected by this light, if the distance is less than the radius, then
+ * the magnitude of the influence is equal to distance / radius.
+ *
+ * @param radius the radius of the light influence.
+ *
+ * @throws IllegalArgumentException If radius is negative
+ */
+ public void setRadius(float radius) {
+ if (radius < 0) {
+ throw new IllegalArgumentException("Light radius cannot be negative");
+ }
+ this.radius = radius;
+ if(radius!=0){
+ this.invRadius = 1 / radius;
+ }else{
+ this.invRadius = 0;
+ }
+ }
+
+ /**
+ * for internal use only
+ * @return the inverse of the radius
+ */
+ public float getInvRadius() {
+ return invRadius;
+ }
+
+ @Override
+ public Light.Type getType() {
+ return Light.Type.Point;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(position, "position", null);
+ oc.write(radius, "radius", 0f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ position = (Vector3f) ic.readSavable("position", null);
+ radius = ic.readFloat("radius", 0f);
+ if(radius!=0){
+ this.invRadius = 1 / radius;
+ }else{
+ this.invRadius = 0;
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/light/SpotLight.java b/engine/src/core/com/jme3/light/SpotLight.java
new file mode 100644
index 0000000..4c653a5
--- /dev/null
+++ b/engine/src/core/com/jme3/light/SpotLight.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.light;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.export.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * Represents a spot light.
+ * A spot light emmit a cone of light from a position and in a direction.
+ * It can be used to fake torch lights or car's lights.
+ * <p>
+ * In addition to a position and a direction, spot lights also have a range which
+ * can be used to attenuate the influence of the light depending on the
+ * distance between the light and the effected object.
+ * Also the angle of the cone can be tweaked by changing the spot inner angle and the spot outer angle.
+ * the spot inner angle determin the cone of light where light has full influence.
+ * the spot outer angle determin the cone global cone of light of the spot light.
+ * the light intensity slowly decrease between the inner cone and the outer cone.
+ * @author Nehon
+ */
+public class SpotLight extends Light implements Savable {
+
+ protected Vector3f position = new Vector3f();
+ protected Vector3f direction = new Vector3f(0,-1,0);
+ protected float spotInnerAngle = FastMath.QUARTER_PI / 8;
+ protected float spotOuterAngle = FastMath.QUARTER_PI / 6;
+ protected float spotRange = 100;
+ protected float invSpotRange = 1 / 100;
+ protected float packedAngleCos=0;
+
+ public SpotLight() {
+ super();
+ computePackedCos();
+ }
+
+ private void computePackedCos() {
+ float innerCos=FastMath.cos(spotInnerAngle);
+ float outerCos=FastMath.cos(spotOuterAngle);
+ packedAngleCos=(int)(innerCos*1000);
+ packedAngleCos+=outerCos;
+ }
+
+ @Override
+ protected void computeLastDistance(Spatial owner) {
+ if (owner.getWorldBound() != null) {
+ BoundingVolume bv = owner.getWorldBound();
+ lastDistance = bv.distanceSquaredTo(position);
+ } else {
+ lastDistance = owner.getWorldTranslation().distanceSquared(position);
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return Type.Spot;
+ }
+
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ public void setDirection(Vector3f direction) {
+ this.direction.set(direction);
+ }
+
+ public Vector3f getPosition() {
+ return position;
+ }
+
+ public void setPosition(Vector3f position) {
+ this.position.set(position);
+ }
+
+ public float getSpotRange() {
+ return spotRange;
+ }
+
+ /**
+ * Set the range of the light influence.
+ * <p>
+ * Setting a non-zero range indicates the light should use attenuation.
+ * If a pixel's distance to this light's position
+ * is greater than the light's range, then the pixel will not be
+ * effected by this light, if the distance is less than the range, then
+ * the magnitude of the influence is equal to distance / range.
+ *
+ * @param spotRange the range of the light influence.
+ *
+ * @throws IllegalArgumentException If spotRange is negative
+ */
+ public void setSpotRange(float spotRange) {
+ if (spotRange < 0) {
+ throw new IllegalArgumentException("SpotLight range cannot be negative");
+ }
+ this.spotRange = spotRange;
+ if (spotRange != 0) {
+ this.invSpotRange = 1 / spotRange;
+ } else {
+ this.invSpotRange = 0;
+ }
+ }
+
+ /**
+ * for internal use only
+ * @return the inverse of the spot range
+ */
+ public float getInvSpotRange() {
+ return invSpotRange;
+ }
+
+ /**
+ * returns the spot inner angle
+ * @return the spot inner angle
+ */
+ public float getSpotInnerAngle() {
+ return spotInnerAngle;
+ }
+
+ /**
+ * Sets the inner angle of the cone of influence.
+ * This angle is the angle between the spot direction axis and the inner border of the cone of influence.
+ * @param spotInnerAngle
+ */
+ public void setSpotInnerAngle(float spotInnerAngle) {
+ this.spotInnerAngle = spotInnerAngle;
+ computePackedCos();
+ }
+
+ /**
+ * returns the spot outer angle
+ * @return the spot outer angle
+ */
+ public float getSpotOuterAngle() {
+ return spotOuterAngle;
+ }
+
+ /**
+ * Sets the outer angle of the cone of influence.
+ * This angle is the angle between the spot direction axis and the outer border of the cone of influence.
+ * this should be greater than the inner angle or the result will be unexpected.
+ * @param spotOuterAngle
+ */
+ public void setSpotOuterAngle(float spotOuterAngle) {
+ this.spotOuterAngle = spotOuterAngle;
+ computePackedCos();
+ }
+
+ /**
+ * for internal use only
+ * @return the cosines of the inner and outter angle packed in a float
+ */
+ public float getPackedAngleCos() {
+ return packedAngleCos;
+ }
+
+
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(direction, "direction", new Vector3f());
+ oc.write(position, "position", new Vector3f());
+ oc.write(spotInnerAngle, "spotInnerAngle", FastMath.QUARTER_PI / 8);
+ oc.write(spotOuterAngle, "spotOuterAngle", FastMath.QUARTER_PI / 6);
+ oc.write(spotRange, "spotRange", 100);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ spotInnerAngle = ic.readFloat("spotInnerAngle", FastMath.QUARTER_PI / 8);
+ spotOuterAngle = ic.readFloat("spotOuterAngle", FastMath.QUARTER_PI / 6);
+ direction = (Vector3f) ic.readSavable("direction", new Vector3f());
+ position = (Vector3f) ic.readSavable("position", new Vector3f());
+ spotRange = ic.readFloat("spotRange", 100);
+ if (spotRange != 0) {
+ this.invSpotRange = 1 / spotRange;
+ } else {
+ this.invSpotRange = 0;
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/light/package.html b/engine/src/core/com/jme3/light/package.html
new file mode 100644
index 0000000..ac65816
--- /dev/null
+++ b/engine/src/core/com/jme3/light/package.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.light</code> package contains various lights that can be placed
+in a scene.
+<p>
+There are 3 types of lights currently supported in jMonkeyEngine:
+<ul>
+ <li>Point Light - Point lights emit from a certain location and have a certain influence radius (optional)</li>
+ <li>Directional Light - Directional lights will always appear to emit from a certain direction
+ regardless of an object's position</li>
+ <li>Ambient Light - Ambient lights have no location or direction;
+ they simply emit a constant color that is applied to the entire scene</li>
+</ul>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/material/FixedFuncBinding.java b/engine/src/core/com/jme3/material/FixedFuncBinding.java
new file mode 100644
index 0000000..e316ad8
--- /dev/null
+++ b/engine/src/core/com/jme3/material/FixedFuncBinding.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.material;
+
+/**
+ * Fixed function binding is used to specify a binding for a {@link MatParam}
+ * in case that shaders are not supported on the system.
+ *
+ * @author Kirill Vainer
+ */
+public enum FixedFuncBinding {
+ /**
+ * Specifies the material ambient color.
+ * Same as GL_AMBIENT for OpenGL.
+ */
+ MaterialAmbient,
+
+ /**
+ * Specifies the material diffuse color.
+ * Same as GL_DIFFUSE for OpenGL.
+ */
+ MaterialDiffuse,
+
+ /**
+ * Specifies the material specular color.
+ * Same as GL_SPECULAR for OpenGL
+ */
+ MaterialSpecular,
+
+ /**
+ * Specifies the color of the object.
+ * <p>
+ * Used only for non-lit materials.
+ */
+ Color,
+
+ /**
+ * Specifies the material shininess value.
+ *
+ * Same as GL_SHININESS for OpenGL.
+ */
+ MaterialShininess,
+
+ /**
+ * Use vertex color as an additional diffuse color, if lighting is enabled.
+ * If lighting is disabled, vertex color is modulated with
+ * {@link #Color material color}.
+ */
+ UseVertexColor
+}
diff --git a/engine/src/core/com/jme3/material/MatParam.java b/engine/src/core/com/jme3/material/MatParam.java
new file mode 100644
index 0000000..b0ef117
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MatParam.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material;
+
+import com.jme3.asset.TextureKey;
+import com.jme3.export.*;
+import com.jme3.math.*;
+import com.jme3.renderer.GL1Renderer;
+import com.jme3.renderer.Renderer;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.IOException;
+
+/**
+ * Describes a material parameter. This is used for both defining a name and type
+ * as well as a material parameter value.
+ *
+ * @author Kirill Vainer
+ */
+public class MatParam implements Savable, Cloneable {
+
+ protected VarType type;
+ protected String name;
+ protected String prefixedName;
+ protected Object value;
+ protected FixedFuncBinding ffBinding;
+
+ /**
+ * Create a new material parameter. For internal use only.
+ */
+ public MatParam(VarType type, String name, Object value, FixedFuncBinding ffBinding) {
+ this.type = type;
+ this.name = name;
+ this.prefixedName = "m_" + name;
+ this.value = value;
+ this.ffBinding = ffBinding;
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public MatParam() {
+ }
+
+ /**
+ * Returns the fixed function binding.
+ *
+ * @return the fixed function binding.
+ */
+ public FixedFuncBinding getFixedFuncBinding() {
+ return ffBinding;
+ }
+
+ /**
+ * Returns the material parameter type.
+ *
+ * @return the material parameter type.
+ */
+ public VarType getVarType() {
+ return type;
+ }
+
+ /**
+ * Returns the name of the material parameter.
+ * @return the name of the material parameter.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the name with "m_" prefixed to it.
+ *
+ * @return the name with "m_" prefixed to it
+ */
+ public String getPrefixedName() {
+ return prefixedName;
+ }
+
+ /**
+ * Used internally
+ * @param name
+ */
+ void setName(String name) {
+ this.name = name;
+ this.prefixedName = "m_" + name;
+ }
+
+ /**
+ * Returns the value of this material parameter.
+ * <p>
+ * Material parameters that are used for material definitions
+ * will not have a value, unless there's a default value declared
+ * in the definition.
+ *
+ * @return the value of this material parameter.
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of this material parameter.
+ * <p>
+ * It is assumed the value is of the same {@link MatParam#getVarType() type}
+ * as this material parameter.
+ *
+ * @param value the value of this material parameter.
+ */
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ void apply(Renderer r, Technique technique) {
+ TechniqueDef techDef = technique.getDef();
+ if (techDef.isUsingShaders()) {
+ technique.updateUniformParam(getPrefixedName(), getVarType(), getValue(), true);
+ }
+ if (ffBinding != null && r instanceof GL1Renderer) {
+ ((GL1Renderer) r).setFixedFuncBinding(ffBinding, getValue());
+ }
+ }
+
+ /**
+ * Returns the material parameter value as it would appear in a J3M
+ * file. E.g.<br/>
+ * <code>
+ * MaterialParameters {<br/>
+ * ABC : 1 2 3 4<br/>
+ * }<br/>
+ * </code>
+ * Assuming "ABC" is a Vector4 parameter, then the value
+ * "1 2 3 4" would be returned by this method.
+ * <br/><br/>
+ * @return material parameter value as it would appear in a J3M file.
+ */
+ public String getValueAsString() {
+ switch (type) {
+ case Boolean:
+ case Float:
+ case Int:
+ return value.toString();
+ case Vector2:
+ Vector2f v2 = (Vector2f) value;
+ return v2.getX() + " " + v2.getY();
+/*
+This may get used at a later point of time
+When arrays can be inserted in J3M files
+
+ case Vector2Array:
+ Vector2f[] v2Arr = (Vector2f[]) value;
+ String v2str = "";
+ for (int i = 0; i < v2Arr.length ; i++) {
+ v2str += v2Arr[i].getX() + " " + v2Arr[i].getY() + "\n";
+ }
+ return v2str;
+*/
+ case Vector3:
+ Vector3f v3 = (Vector3f) value;
+ return v3.getX() + " " + v3.getY() + " " + v3.getZ();
+/*
+ case Vector3Array:
+ Vector3f[] v3Arr = (Vector3f[]) value;
+ String v3str = "";
+ for (int i = 0; i < v3Arr.length ; i++) {
+ v3str += v3Arr[i].getX() + " "
+ + v3Arr[i].getY() + " "
+ + v3Arr[i].getZ() + "\n";
+ }
+ return v3str;
+ case Vector4Array:
+ // can be either ColorRGBA, Vector4f or Quaternion
+ if (value instanceof Vector4f) {
+ Vector4f[] v4arr = (Vector4f[]) value;
+ String v4str = "";
+ for (int i = 0; i < v4arr.length ; i++) {
+ v4str += v4arr[i].getX() + " "
+ + v4arr[i].getY() + " "
+ + v4arr[i].getZ() + " "
+ + v4arr[i].getW() + "\n";
+ }
+ return v4str;
+ } else if (value instanceof ColorRGBA) {
+ ColorRGBA[] colorArr = (ColorRGBA[]) value;
+ String colStr = "";
+ for (int i = 0; i < colorArr.length ; i++) {
+ colStr += colorArr[i].getRed() + " "
+ + colorArr[i].getGreen() + " "
+ + colorArr[i].getBlue() + " "
+ + colorArr[i].getAlpha() + "\n";
+ }
+ return colStr;
+ } else if (value instanceof Quaternion) {
+ Quaternion[] quatArr = (Quaternion[]) value;
+ String quatStr = "";
+ for (int i = 0; i < quatArr.length ; i++) {
+ quatStr += quatArr[i].getX() + " "
+ + quatArr[i].getY() + " "
+ + quatArr[i].getZ() + " "
+ + quatArr[i].getW() + "\n";
+ }
+ return quatStr;
+ } else {
+ throw new UnsupportedOperationException("Unexpected Vector4Array type: " + value);
+ }
+*/
+ case Vector4:
+ // can be either ColorRGBA, Vector4f or Quaternion
+ if (value instanceof Vector4f) {
+ Vector4f v4 = (Vector4f) value;
+ return v4.getX() + " " + v4.getY() + " "
+ + v4.getZ() + " " + v4.getW();
+ } else if (value instanceof ColorRGBA) {
+ ColorRGBA color = (ColorRGBA) value;
+ return color.getRed() + " " + color.getGreen() + " "
+ + color.getBlue() + " " + color.getAlpha();
+ } else if (value instanceof Quaternion) {
+ Quaternion quat = (Quaternion) value;
+ return quat.getX() + " " + quat.getY() + " "
+ + quat.getZ() + " " + quat.getW();
+ } else {
+ throw new UnsupportedOperationException("Unexpected Vector4 type: " + value);
+ }
+ case Texture2D:
+ case Texture3D:
+ case TextureArray:
+ case TextureBuffer:
+ case TextureCubeMap:
+ Texture texVal = (Texture) value;
+ TextureKey texKey = (TextureKey) texVal.getKey();
+ if (texKey == null){
+ throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M");
+ }
+
+ String ret = "";
+ if (texKey.isFlipY()) {
+ ret += "Flip ";
+ }
+ if (texVal.getWrap(Texture.WrapAxis.S) == WrapMode.Repeat) {
+ ret += "Repeat ";
+ }
+
+ return ret + texKey.getName();
+ default:
+ return null; // parameter type not supported in J3M
+ }
+ }
+
+ @Override
+ public MatParam clone() {
+ try {
+ MatParam param = (MatParam) super.clone();
+ return param;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(type, "varType", null);
+ oc.write(name, "name", null);
+ oc.write(ffBinding, "ff_binding", null);
+ if (value instanceof Savable) {
+ Savable s = (Savable) value;
+ oc.write(s, "value_savable", null);
+ } else if (value instanceof Float) {
+ Float f = (Float) value;
+ oc.write(f.floatValue(), "value_float", 0f);
+ } else if (value instanceof Integer) {
+ Integer i = (Integer) value;
+ oc.write(i.intValue(), "value_int", 0);
+ } else if (value instanceof Boolean) {
+ Boolean b = (Boolean) value;
+ oc.write(b.booleanValue(), "value_bool", false);
+ }
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ type = ic.readEnum("varType", VarType.class, null);
+ name = ic.readString("name", null);
+ ffBinding = ic.readEnum("ff_binding", FixedFuncBinding.class, null);
+ switch (getVarType()) {
+ case Boolean:
+ value = ic.readBoolean("value_bool", false);
+ break;
+ case Float:
+ value = ic.readFloat("value_float", 0f);
+ break;
+ case Int:
+ value = ic.readInt("value_int", 0);
+ break;
+ default:
+ value = ic.readSavable("value_savable", null);
+ break;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof MatParam)) {
+ return false;
+ }
+
+ MatParam otherParam = (MatParam) other;
+ return otherParam.type == type
+ && otherParam.name.equals(name);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 17 * hash + (this.type != null ? this.type.hashCode() : 0);
+ hash = 17 * hash + (this.name != null ? this.name.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return type.name() + " " + name + " : " + getValueAsString();
+ }
+}
diff --git a/engine/src/core/com/jme3/material/MatParamTexture.java b/engine/src/core/com/jme3/material/MatParamTexture.java
new file mode 100644
index 0000000..fc8b469
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MatParamTexture.java
@@ -0,0 +1,67 @@
+package com.jme3.material;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.renderer.Renderer;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+
+public class MatParamTexture extends MatParam {
+
+ private Texture texture;
+ private int unit;
+
+ public MatParamTexture(VarType type, String name, Texture texture, int unit) {
+ super(type, name, texture, null);
+ this.texture = texture;
+ this.unit = unit;
+ }
+
+ public MatParamTexture() {
+ }
+
+ public Texture getTextureValue() {
+ return texture;
+ }
+
+ public void setTextureValue(Texture value) {
+ this.value = value;
+ this.texture = value;
+ }
+
+ public void setUnit(int unit) {
+ this.unit = unit;
+ }
+
+ public int getUnit() {
+ return unit;
+ }
+
+ @Override
+ public void apply(Renderer r, Technique technique) {
+ TechniqueDef techDef = technique.getDef();
+ r.setTexture(getUnit(), getTextureValue());
+ if (techDef.isUsingShaders()) {
+ technique.updateUniformParam(getPrefixedName(), getVarType(), getUnit(), true);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(unit, "texture_unit", -1);
+ oc.write(texture, "texture", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ unit = ic.readInt("texture_unit", -1);
+ texture = (Texture) ic.readSavable("texture", null);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/material/Material.java b/engine/src/core/com/jme3/material/Material.java
new file mode 100644
index 0000000..8d43853
--- /dev/null
+++ b/engine/src/core/com/jme3/material/Material.java
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * <p/>
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material;
+
+import com.jme3.asset.Asset;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.light.*;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.material.TechniqueDef.LightMode;
+import com.jme3.material.TechniqueDef.ShadowMode;
+import com.jme3.math.*;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Uniform;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import com.jme3.util.ListMap;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>Material</code> describes the rendering style for a given
+ * {@link Geometry}.
+ * <p>A material is essentially a list of {@link MatParam parameters},
+ * those parameters map to uniforms which are defined in a shader.
+ * Setting the parameters can modify the behavior of a
+ * shader.
+ * <p/>
+ * @author Kirill Vainer
+ */
+public class Material implements Asset, Cloneable, Savable, Comparable<Material> {
+
+ // Version #2: Fixed issue with RenderState.apply*** flags not getting exported
+ public static final int SAVABLE_VERSION = 2;
+
+ private static final Logger logger = Logger.getLogger(Material.class.getName());
+ private static final RenderState additiveLight = new RenderState();
+ private static final RenderState depthOnly = new RenderState();
+ private static final Quaternion nullDirLight = new Quaternion(0, -1, 0, -1);
+
+ static {
+ depthOnly.setDepthTest(true);
+ depthOnly.setDepthWrite(true);
+ depthOnly.setFaceCullMode(RenderState.FaceCullMode.Back);
+ depthOnly.setColorWrite(false);
+
+ additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
+ additiveLight.setDepthWrite(false);
+ }
+ private AssetKey key;
+ private String name;
+ private MaterialDef def;
+ private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
+ private Technique technique;
+ private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
+ private int nextTexUnit = 0;
+ private RenderState additionalState = null;
+ private RenderState mergedRenderState = new RenderState();
+ private boolean transparent = false;
+ private boolean receivesShadows = false;
+ private int sortingId = -1;
+ private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
+
+ public Material(MaterialDef def) {
+ if (def == null) {
+ throw new NullPointerException("Material definition cannot be null");
+ }
+ this.def = def;
+
+ // Load default values from definition (if any)
+ for (MatParam param : def.getMaterialParams()){
+ if (param.getValue() != null){
+ setParam(param.getName(), param.getVarType(), param.getValue());
+ }
+ }
+ }
+
+ public Material(AssetManager contentMan, String defName) {
+ this((MaterialDef) contentMan.loadAsset(new AssetKey(defName)));
+ }
+
+ /**
+ * Do not use this constructor. Serialization purposes only.
+ */
+ public Material() {
+ }
+
+ /**
+ * Returns the asset key name of the asset from which this material was loaded.
+ *
+ * <p>This value will be <code>null</code> unless this material was loaded
+ * from a .j3m file.
+ *
+ * @return Asset key name of the j3m file
+ */
+ public String getAssetName() {
+ return key != null ? key.getName() : null;
+ }
+
+ /**
+ * @return the name of the material (not the same as the asset name), the returned value can be null
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * This method sets the name of the material.
+ * The name is not the same as the asset name.
+ * It can be null and there is no guarantee of its uniqness.
+ * @param name the name of the material
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setKey(AssetKey key) {
+ this.key = key;
+ }
+
+ public AssetKey getKey() {
+ return key;
+ }
+
+ /**
+ * Returns the sorting ID or sorting index for this material.
+ *
+ * <p>The sorting ID is used internally by the system to sort rendering
+ * of geometries. It sorted to reduce shader switches, if the shaders
+ * are equal, then it is sorted by textures.
+ *
+ * @return The sorting ID used for sorting geometries for rendering.
+ */
+ public int getSortId() {
+ Technique t = getActiveTechnique();
+ if (sortingId == -1 && t != null && t.getShader() != null) {
+ int texId = -1;
+ for (int i = 0; i < paramValues.size(); i++) {
+ MatParam param = paramValues.getValue(i);
+ if (param instanceof MatParamTexture) {
+ MatParamTexture tex = (MatParamTexture) param;
+ if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) {
+ if (texId == -1) {
+ texId = 0;
+ }
+ texId += tex.getTextureValue().getImage().getId() % 0xff;
+ }
+ }
+ }
+ sortingId = texId + t.getShader().getId() * 1000;
+ }
+ return sortingId;
+ }
+
+ /**
+ * Uses the sorting ID for each material to compare them.
+ *
+ * @param m The other material to compare to.
+ *
+ * @return zero if the materials are equal, returns a negative value
+ * if <code>this</code> has a lower sorting ID than <code>m</code>,
+ * otherwise returns a positive value.
+ */
+ public int compareTo(Material m) {
+ return m.getSortId() - getSortId();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof Material){
+ return ((Material)obj).compareTo(this) == 0;
+ }
+ return super.equals(obj);
+ }
+
+ /**
+ * Clones this material. The result is returned.
+ */
+ @Override
+ public Material clone() {
+ try {
+ Material mat = (Material) super.clone();
+
+ if (additionalState != null) {
+ mat.additionalState = additionalState.clone();
+ }
+ mat.technique = null;
+ mat.techniques = new HashMap<String, Technique>();
+
+ mat.paramValues = new ListMap<String, MatParam>();
+ for (int i = 0; i < paramValues.size(); i++) {
+ Map.Entry<String, MatParam> entry = paramValues.getEntry(i);
+ mat.paramValues.put(entry.getKey(), entry.getValue().clone());
+ }
+
+ return mat;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Returns the currently active technique.
+ * <p>
+ * The technique is selected automatically by the {@link RenderManager}
+ * based on system capabilities. Users may select their own
+ * technique by using
+ * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }.
+ *
+ * @return the currently active technique.
+ *
+ * @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+ */
+ public Technique getActiveTechnique() {
+ return technique;
+ }
+
+ /**
+ * Check if the transparent value marker is set on this material.
+ * @return True if the transparent value marker is set on this material.
+ * @see #setTransparent(boolean)
+ */
+ public boolean isTransparent() {
+ return transparent;
+ }
+
+ /**
+ * Set the transparent value marker.
+ *
+ * <p>This value is merely a marker, by itself it does nothing.
+ * Generally model loaders will use this marker to indicate further
+ * up that the material is transparent and therefore any geometries
+ * using it should be put into the {@link Bucket#Transparent transparent
+ * bucket}.
+ *
+ * @param transparent the transparent value marker.
+ */
+ public void setTransparent(boolean transparent) {
+ this.transparent = transparent;
+ }
+
+ /**
+ * Check if the material should receive shadows or not.
+ *
+ * @return True if the material should receive shadows.
+ *
+ * @see Material#setReceivesShadows(boolean)
+ */
+ public boolean isReceivesShadows() {
+ return receivesShadows;
+ }
+
+ /**
+ * Set if the material should receive shadows or not.
+ *
+ * <p>This value is merely a marker, by itself it does nothing.
+ * Generally model loaders will use this marker to indicate
+ * the material should receive shadows and therefore any
+ * geometries using it should have the {@link ShadowMode#Receive} set
+ * on them.
+ *
+ * @param receivesShadows if the material should receive shadows or not.
+ */
+ public void setReceivesShadows(boolean receivesShadows) {
+ this.receivesShadows = receivesShadows;
+ }
+
+ /**
+ * Acquire the additional {@link RenderState render state} to apply
+ * for this material.
+ *
+ * <p>The first call to this method will create an additional render
+ * state which can be modified by the user to apply any render
+ * states in addition to the ones used by the renderer. Only render
+ * states which are modified in the additional render state will be applied.
+ *
+ * @return The additional render state.
+ */
+ public RenderState getAdditionalRenderState() {
+ if (additionalState == null) {
+ additionalState = RenderState.ADDITIONAL.clone();
+ }
+ return additionalState;
+ }
+
+ /**
+ * Get the material definition (j3md file info) that <code>this</code>
+ * material is implementing.
+ *
+ * @return the material definition this material implements.
+ */
+ public MaterialDef getMaterialDef() {
+ return def;
+ }
+
+ /**
+ * Returns the parameter set on this material with the given name,
+ * returns <code>null</code> if the parameter is not set.
+ *
+ * @param name The parameter name to look up.
+ * @return The MatParam if set, or null if not set.
+ */
+ public MatParam getParam(String name) {
+ MatParam param = paramValues.get(name);
+ return param;
+ }
+
+ /**
+ * Returns the texture parameter set on this material with the given name,
+ * returns <code>null</code> if the parameter is not set.
+ *
+ * @param name The parameter name to look up.
+ * @return The MatParamTexture if set, or null if not set.
+ */
+ public MatParamTexture getTextureParam(String name) {
+ MatParam param = paramValues.get(name);
+ if (param instanceof MatParamTexture) {
+ return (MatParamTexture) param;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a collection of all parameters set on this material.
+ *
+ * @return a collection of all parameters set on this material.
+ *
+ * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
+ */
+ public Collection<MatParam> getParams() {
+ return paramValues.values();
+ }
+
+ private String checkSetParam(VarType type, String name) {
+ MatParam paramDef = def.getMaterialParam(name);
+ String newName = name;
+
+ if (paramDef == null && name.startsWith("m_")) {
+ newName = name.substring(2);
+ paramDef = def.getMaterialParam(newName);
+ if (paramDef == null) {
+ throw new IllegalArgumentException("Material parameter is not defined: " + name);
+ } else {
+ logger.log(Level.WARNING, "Material parameter {0} uses a deprecated naming convention use {1} instead ", new Object[]{name, newName});
+ }
+ } else if (paramDef == null) {
+ throw new IllegalArgumentException("Material parameter is not defined: " + name);
+ }
+
+ if (type != null && paramDef.getVarType() != type) {
+ logger.log(Level.WARNING, "Material parameter being set: {0} with "
+ + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()} );
+ }
+
+ return newName;
+ }
+
+ /**
+ * Pass a parameter to the material shader.
+ *
+ * @param name the name of the parameter defined in the material definition (j3md)
+ * @param type the type of the parameter {@link VarType}
+ * @param value the value of the parameter
+ */
+ public void setParam(String name, VarType type, Object value) {
+ name = checkSetParam(type, name);
+
+ MatParam val = getParam(name);
+ if (technique != null) {
+ technique.notifySetParam(name, type, value);
+ }
+ if (val == null) {
+ MatParam paramDef = def.getMaterialParam(name);
+ paramValues.put(name, new MatParam(type, name, value, paramDef.getFixedFuncBinding()));
+ } else {
+ val.setValue(value);
+ }
+ }
+
+ /**
+ * Clear a parameter from this material. The parameter must exist
+ * @param name the name of the parameter to clear
+ */
+ public void clearParam(String name) {
+ //On removal, we don't check if the param exists in the paramDef, and just go on with the process.
+ // name = checkSetParam(null, name);
+
+ MatParam matParam = getParam(name);
+ if (matParam != null) {
+ paramValues.remove(name);
+ if (technique != null) {
+ technique.notifyClearParam(name);
+ }
+ if (matParam instanceof MatParamTexture) {
+ int texUnit = ((MatParamTexture) matParam).getUnit();
+ nextTexUnit--;
+ for (MatParam param : paramValues.values()) {
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texParam = (MatParamTexture) param;
+ if (texParam.getUnit() > texUnit) {
+ texParam.setUnit(texParam.getUnit() - 1);
+ }
+ }
+ }
+ }
+ }
+// else {
+// throw new IllegalArgumentException("The given parameter is not set.");
+// }
+ }
+
+ private void clearTextureParam(String name) {
+ name = checkSetParam(null, name);
+
+ MatParamTexture val = getTextureParam(name);
+ if (val == null) {
+ throw new IllegalArgumentException("The given texture for parameter \"" + name + "\" is null.");
+ }
+
+ int texUnit = val.getUnit();
+ paramValues.remove(name);
+ nextTexUnit--;
+ for (MatParam param : paramValues.values()) {
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texParam = (MatParamTexture) param;
+ if (texParam.getUnit() > texUnit) {
+ texParam.setUnit(texParam.getUnit() - 1);
+ }
+ }
+ }
+
+ sortingId = -1;
+ }
+
+ /**
+ * Set a texture parameter.
+ *
+ * @param name The name of the parameter
+ * @param type The variable type {@link VarType}
+ * @param value The texture value of the parameter.
+ *
+ * @throws IllegalArgumentException is value is null
+ */
+ public void setTextureParam(String name, VarType type, Texture value) {
+ if (value == null) {
+ throw new IllegalArgumentException();
+ }
+
+ name = checkSetParam(type, name);
+ MatParamTexture val = getTextureParam(name);
+ if (val == null) {
+ paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++));
+ } else {
+ val.setTextureValue(value);
+ }
+
+ if (technique != null) {
+ technique.notifySetParam(name, type, nextTexUnit - 1);
+ }
+
+ // need to recompute sort ID
+ sortingId = -1;
+ }
+
+ /**
+ * Pass a texture to the material shader.
+ *
+ * @param name the name of the texture defined in the material definition
+ * (j3md) (for example Texture for Lighting.j3md)
+ * @param value the Texture object previously loaded by the asset manager
+ */
+ public void setTexture(String name, Texture value) {
+ if (value == null) {
+ // clear it
+ clearTextureParam(name);
+ return;
+ }
+
+ VarType paramType = null;
+ switch (value.getType()) {
+ case TwoDimensional:
+ paramType = VarType.Texture2D;
+ break;
+ case TwoDimensionalArray:
+ paramType = VarType.TextureArray;
+ break;
+ case ThreeDimensional:
+ paramType = VarType.Texture3D;
+ break;
+ case CubeMap:
+ paramType = VarType.TextureCubeMap;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + value.getType());
+ }
+
+ setTextureParam(name, paramType, value);
+ }
+
+ /**
+ * Pass a Matrix4f to the material shader.
+ *
+ * @param name the name of the matrix defined in the material definition (j3md)
+ * @param value the Matrix4f object
+ */
+ public void setMatrix4(String name, Matrix4f value) {
+ setParam(name, VarType.Matrix4, value);
+ }
+
+ /**
+ * Pass a boolean to the material shader.
+ *
+ * @param name the name of the boolean defined in the material definition (j3md)
+ * @param value the boolean value
+ */
+ public void setBoolean(String name, boolean value) {
+ setParam(name, VarType.Boolean, value);
+ }
+
+ /**
+ * Pass a float to the material shader.
+ *
+ * @param name the name of the float defined in the material definition (j3md)
+ * @param value the float value
+ */
+ public void setFloat(String name, float value) {
+ setParam(name, VarType.Float, value);
+ }
+
+ /**
+ * Pass an int to the material shader.
+ *
+ * @param name the name of the int defined in the material definition (j3md)
+ * @param value the int value
+ */
+ public void setInt(String name, int value) {
+ setParam(name, VarType.Int, value);
+ }
+
+ /**
+ * Pass a Color to the material shader.
+ *
+ * @param name the name of the color defined in the material definition (j3md)
+ * @param value the ColorRGBA value
+ */
+ public void setColor(String name, ColorRGBA value) {
+ setParam(name, VarType.Vector4, value);
+ }
+
+ /**
+ * Pass a Vector2f to the material shader.
+ *
+ * @param name the name of the Vector2f defined in the material definition (j3md)
+ * @param value the Vector2f value
+ */
+ public void setVector2(String name, Vector2f value) {
+ setParam(name, VarType.Vector2, value);
+ }
+
+ /**
+ * Pass a Vector3f to the material shader.
+ *
+ * @param name the name of the Vector3f defined in the material definition (j3md)
+ * @param value the Vector3f value
+ */
+ public void setVector3(String name, Vector3f value) {
+ setParam(name, VarType.Vector3, value);
+ }
+
+ /**
+ * Pass a Vector4f to the material shader.
+ *
+ * @param name the name of the Vector4f defined in the material definition (j3md)
+ * @param value the Vector4f value
+ */
+ public void setVector4(String name, Vector4f value) {
+ setParam(name, VarType.Vector4, value);
+ }
+
+ private ColorRGBA getAmbientColor(LightList lightList) {
+ ambientLightColor.set(0, 0, 0, 1);
+ for (int j = 0; j < lightList.size(); j++) {
+ Light l = lightList.get(j);
+ if (l instanceof AmbientLight) {
+ ambientLightColor.addLocal(l.getColor());
+ }
+ }
+ ambientLightColor.a = 1.0f;
+ return ambientLightColor;
+ }
+
+ /**
+ * Uploads the lights in the light list as two uniform arrays.<br/><br/>
+ * * <p>
+ * <code>uniform vec4 g_LightColor[numLights];</code><br/>
+ * // g_LightColor.rgb is the diffuse/specular color of the light.<br/>
+ * // g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/>
+ * // 2 = Spot. <br/>
+ * <br/>
+ * <code>uniform vec4 g_LightPosition[numLights];</code><br/>
+ * // g_LightPosition.xyz is the position of the light (for point lights)<br/>
+ * // or the direction of the light (for directional lights).<br/>
+ * // g_LightPosition.w is the inverse radius (1/r) of the light (for attenuation) <br/>
+ * </p>
+ */
+ protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {
+ if (numLights == 0) { // this shader does not do lighting, ignore.
+ return;
+ }
+
+ LightList lightList = g.getWorldLightList();
+ Uniform lightColor = shader.getUniform("g_LightColor");
+ Uniform lightPos = shader.getUniform("g_LightPosition");
+ Uniform lightDir = shader.getUniform("g_LightDirection");
+ lightColor.setVector4Length(numLights);
+ lightPos.setVector4Length(numLights);
+ lightDir.setVector4Length(numLights);
+
+ Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
+ ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+
+ int lightIndex = 0;
+
+ for (int i = 0; i < numLights; i++) {
+ if (lightList.size() <= i) {
+ lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+ lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+ } else {
+ Light l = lightList.get(i);
+ ColorRGBA color = l.getColor();
+ lightColor.setVector4InArray(color.getRed(),
+ color.getGreen(),
+ color.getBlue(),
+ l.getType().getId(),
+ i);
+
+ switch (l.getType()) {
+ case Directional:
+ DirectionalLight dl = (DirectionalLight) l;
+ Vector3f dir = dl.getDirection();
+ lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightIndex);
+ break;
+ case Point:
+ PointLight pl = (PointLight) l;
+ Vector3f pos = pl.getPosition();
+ float invRadius = pl.getInvRadius();
+ lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);
+ break;
+ case Spot:
+ SpotLight sl = (SpotLight) l;
+ Vector3f pos2 = sl.getPosition();
+ Vector3f dir2 = sl.getDirection();
+ float invRange = sl.getInvSpotRange();
+ float spotAngleCos = sl.getPackedAngleCos();
+
+ lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);
+ lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);
+ break;
+ case Ambient:
+ // skip this light. Does not increase lightIndex
+ continue;
+ default:
+ throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
+ }
+ }
+
+ lightIndex++;
+ }
+
+ while (lightIndex < numLights) {
+ lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+ lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+
+ lightIndex++;
+ }
+ }
+
+ protected void renderMultipassLighting(Shader shader, Geometry g, RenderManager rm) {
+
+ Renderer r = rm.getRenderer();
+ LightList lightList = g.getWorldLightList();
+ Uniform lightDir = shader.getUniform("g_LightDirection");
+ Uniform lightColor = shader.getUniform("g_LightColor");
+ Uniform lightPos = shader.getUniform("g_LightPosition");
+ Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
+ boolean isFirstLight = true;
+ boolean isSecondLight = false;
+
+ for (int i = 0; i < lightList.size(); i++) {
+ Light l = lightList.get(i);
+ if (l instanceof AmbientLight) {
+ continue;
+ }
+
+ if (isFirstLight) {
+ // set ambient color for first light only
+ ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+ isFirstLight = false;
+ isSecondLight = true;
+ } else if (isSecondLight) {
+ ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
+ // apply additive blending for 2nd and future lights
+ r.applyRenderState(additiveLight);
+ isSecondLight = false;
+ }
+
+ TempVars vars = TempVars.get();
+ Quaternion tmpLightDirection = vars.quat1;
+ Quaternion tmpLightPosition = vars.quat2;
+ ColorRGBA tmpLightColor = vars.color;
+ Vector4f tmpVec = vars.vect4f;
+
+ ColorRGBA color = l.getColor();
+ tmpLightColor.set(color);
+ tmpLightColor.a = l.getType().getId();
+ lightColor.setValue(VarType.Vector4, tmpLightColor);
+
+ switch (l.getType()) {
+ case Directional:
+ DirectionalLight dl = (DirectionalLight) l;
+ Vector3f dir = dl.getDirection();
+
+ tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
+ lightPos.setValue(VarType.Vector4, tmpLightPosition);
+ tmpLightDirection.set(0, 0, 0, 0);
+ lightDir.setValue(VarType.Vector4, tmpLightDirection);
+ break;
+ case Point:
+ PointLight pl = (PointLight) l;
+ Vector3f pos = pl.getPosition();
+ float invRadius = pl.getInvRadius();
+
+ tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
+ lightPos.setValue(VarType.Vector4, tmpLightPosition);
+ tmpLightDirection.set(0, 0, 0, 0);
+ lightDir.setValue(VarType.Vector4, tmpLightDirection);
+ break;
+ case Spot:
+ SpotLight sl = (SpotLight) l;
+ Vector3f pos2 = sl.getPosition();
+ Vector3f dir2 = sl.getDirection();
+ float invRange = sl.getInvSpotRange();
+ float spotAngleCos = sl.getPackedAngleCos();
+
+ tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
+ lightPos.setValue(VarType.Vector4, tmpLightPosition);
+
+ //We transform the spot directoin in view space here to save 5 varying later in the lighting shader
+ //one vec4 less and a vec4 that becomes a vec3
+ //the downside is that spotAngleCos decoding happen now in the frag shader.
+ tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(),0);
+ rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
+ tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
+
+ lightDir.setValue(VarType.Vector4, tmpLightDirection);
+
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
+ }
+ vars.release();
+ r.setShader(shader);
+ r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
+ }
+
+ if (isFirstLight && lightList.size() > 0) {
+ // There are only ambient lights in the scene. Render
+ // a dummy "normal light" so we can see the ambient
+ ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+ lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
+ lightPos.setValue(VarType.Vector4, nullDirLight);
+ r.setShader(shader);
+ r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
+ }
+ }
+
+ /**
+ * Select the technique to use for rendering this material.
+ * <p>
+ * If <code>name</code> is "Default", then one of the
+ * {@link MaterialDef#getDefaultTechniques() default techniques}
+ * on the material will be selected. Otherwise, the named technique
+ * will be found in the material definition.
+ * <p>
+ * Any candidate technique for selection (either default or named)
+ * must be verified to be compatible with the system, for that, the
+ * <code>renderManager</code> is queried for capabilities.
+ *
+ * @param name The name of the technique to select, pass "Default" to
+ * select one of the default techniques.
+ * @param renderManager The {@link RenderManager render manager}
+ * to query for capabilities.
+ *
+ * @throws IllegalArgumentException If "Default" is passed and no default
+ * techniques are available on the material definition, or if a name
+ * is passed but there's no technique by that name.
+ * @throws UnsupportedOperationException If no candidate technique supports
+ * the system capabilities.
+ */
+ public void selectTechnique(String name, RenderManager renderManager) {
+ // check if already created
+ Technique tech = techniques.get(name);
+ if (tech == null) {
+ // When choosing technique, we choose one that
+ // supports all the caps.
+ EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
+
+ if (name.equals("Default")) {
+ List<TechniqueDef> techDefs = def.getDefaultTechniques();
+ if (techDefs == null || techDefs.isEmpty()) {
+ throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
+ }
+
+ TechniqueDef lastTech = null;
+ for (TechniqueDef techDef : techDefs) {
+ if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
+ // use the first one that supports all the caps
+ tech = new Technique(this, techDef);
+ techniques.put(name, tech);
+ break;
+ }
+ lastTech = techDef;
+ }
+ if (tech == null) {
+ throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n"
+ + " is supported by the video hardware. The caps "
+ + lastTech.getRequiredCaps() + " are required.");
+ }
+
+ } else {
+ // create "special" technique instance
+ TechniqueDef techDef = def.getTechniqueDef(name);
+ if (techDef == null) {
+ throw new IllegalArgumentException("For material " + def.getName() + ", technique not found: " + name);
+ }
+
+ if (!rendererCaps.containsAll(techDef.getRequiredCaps())) {
+ throw new UnsupportedOperationException("The explicitly chosen technique '" + name + "' on material '" + def.getName() + "'\n"
+ + "requires caps " + techDef.getRequiredCaps() + " which are not "
+ + "supported by the video renderer");
+ }
+
+ tech = new Technique(this, techDef);
+ techniques.put(name, tech);
+ }
+ } else if (technique == tech) {
+ // attempting to switch to an already
+ // active technique.
+ return;
+ }
+
+ technique = tech;
+ tech.makeCurrent(def.getAssetManager());
+
+ // shader was changed
+ sortingId = -1;
+ }
+
+ private void autoSelectTechnique(RenderManager rm) {
+ if (technique == null) {
+ // NOTE: Not really needed anymore since we have technique
+ // selection by caps. Rename all "FixedFunc" techniques to "Default"
+ // and remove this hack.
+ if (!rm.getRenderer().getCaps().contains(Caps.GLSL100)) {
+ selectTechnique("FixedFunc", rm);
+ } else {
+ selectTechnique("Default", rm);
+ }
+ } else if (technique.isNeedReload()) {
+ technique.makeCurrent(def.getAssetManager());
+ }
+ }
+
+ /**
+ * Preloads this material for the given render manager.
+ * <p>
+ * Preloading the material can ensure that when the material is first
+ * used for rendering, there won't be any delay since the material has
+ * been already been setup for rendering.
+ *
+ * @param rm The render manager to preload for
+ */
+ public void preload(RenderManager rm) {
+ autoSelectTechnique(rm);
+
+ Renderer r = rm.getRenderer();
+ TechniqueDef techDef = technique.getDef();
+
+ Collection<MatParam> params = paramValues.values();
+ for (MatParam param : params) {
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texParam = (MatParamTexture) param;
+ r.setTexture(0, texParam.getTextureValue());
+ } else {
+ if (!techDef.isUsingShaders()) {
+ continue;
+ }
+
+ technique.updateUniformParam(param.getName(),
+ param.getVarType(),
+ param.getValue(), true);
+ }
+ }
+
+ Shader shader = technique.getShader();
+ if (techDef.isUsingShaders()) {
+ r.setShader(shader);
+ }
+ }
+
+ private void clearUniformsSetByCurrent(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+ int size = uniforms.size();
+ for (int i = 0; i < size; i++) {
+ Uniform u = uniforms.getValue(i);
+ u.clearSetByCurrentMaterial();
+ }
+ }
+
+ private void resetUniformsNotSetByCurrent(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+ int size = uniforms.size();
+ for (int i = 0; i < size; i++) {
+ Uniform u = uniforms.getValue(i);
+ if (!u.isSetByCurrentMaterial()) {
+ u.clearValue();
+ }
+ }
+ }
+
+ /**
+ * Called by {@link RenderManager} to render the geometry by
+ * using this material.
+ *
+ * @param geom The geometry to render
+ * @param rm The render manager requesting the rendering
+ */
+ public void render(Geometry geom, RenderManager rm) {
+ autoSelectTechnique(rm);
+
+ Renderer r = rm.getRenderer();
+
+ TechniqueDef techDef = technique.getDef();
+
+ if (techDef.getLightMode() == LightMode.MultiPass
+ && geom.getWorldLightList().size() == 0) {
+ return;
+ }
+
+ if (rm.getForcedRenderState() != null) {
+ r.applyRenderState(rm.getForcedRenderState());
+ } else {
+ if (techDef.getRenderState() != null) {
+ r.applyRenderState(techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
+ } else {
+ r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
+ }
+ }
+
+
+ // update camera and world matrices
+ // NOTE: setWorldTransform should have been called already
+ if (techDef.isUsingShaders()) {
+ // reset unchanged uniform flag
+ clearUniformsSetByCurrent(technique.getShader());
+ rm.updateUniformBindings(technique.getWorldBindUniforms());
+ }
+
+ // setup textures and uniforms
+ for (int i = 0; i < paramValues.size(); i++) {
+ MatParam param = paramValues.getValue(i);
+ param.apply(r, technique);
+ }
+
+ Shader shader = technique.getShader();
+
+ // send lighting information, if needed
+ switch (techDef.getLightMode()) {
+ case Disable:
+ r.setLighting(null);
+ break;
+ case SinglePass:
+ updateLightListUniforms(shader, geom, 4);
+ break;
+ case FixedPipeline:
+ r.setLighting(geom.getWorldLightList());
+ break;
+ case MultiPass:
+ // NOTE: Special case!
+ resetUniformsNotSetByCurrent(shader);
+ renderMultipassLighting(shader, geom, rm);
+ // very important, notice the return statement!
+ return;
+ }
+
+ // upload and bind shader
+ if (techDef.isUsingShaders()) {
+ // any unset uniforms will be set to 0
+ resetUniformsNotSetByCurrent(shader);
+ r.setShader(shader);
+ }
+
+ r.renderMesh(geom.getMesh(), geom.getLodLevel(), 1);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(def.getAssetName(), "material_def", null);
+ oc.write(additionalState, "render_state", null);
+ oc.write(transparent, "is_transparent", false);
+ oc.writeStringSavableMap(paramValues, "parameters", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+
+ additionalState = (RenderState) ic.readSavable("render_state", null);
+ transparent = ic.readBoolean("is_transparent", false);
+
+ // Load the material def
+ String defName = ic.readString("material_def", null);
+ HashMap<String, MatParam> params = (HashMap<String, MatParam>) ic.readStringSavableMap("parameters", null);
+
+ boolean enableVcolor = false;
+ boolean separateTexCoord = false;
+ boolean applyDefaultValues = false;
+ boolean guessRenderStateApply = false;
+
+ int ver = ic.getSavableVersion(Material.class);
+ if (ver < 1){
+ applyDefaultValues = true;
+ }
+ if (ver < 2){
+ guessRenderStateApply = true;
+ }
+ if (im.getFormatVersion() == 0) {
+ // Enable compatibility with old models
+ if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")) {
+ // Using VertexColor, switch to Unshaded and set VertexColor=true
+ enableVcolor = true;
+ defName = "Common/MatDefs/Misc/Unshaded.j3md";
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md")
+ || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) {
+ // Using SimpleTextured/SolidColor, just switch to Unshaded
+ defName = "Common/MatDefs/Misc/Unshaded.j3md";
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")) {
+ // Using WireColor, set wireframe renderstate = true and use Unshaded
+ getAdditionalRenderState().setWireframe(true);
+ defName = "Common/MatDefs/Misc/Unshaded.j3md";
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")) {
+ // Uses unshaded, ensure that the proper param is set
+ MatParam value = params.get("SeperateTexCoord");
+ if (value != null && ((Boolean) value.getValue()) == true) {
+ params.remove("SeperateTexCoord");
+ separateTexCoord = true;
+ }
+ }
+ assert applyDefaultValues && guessRenderStateApply;
+ }
+
+ def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
+ paramValues = new ListMap<String, MatParam>();
+
+ // load the textures and update nextTexUnit
+ for (Map.Entry<String, MatParam> entry : params.entrySet()) {
+ MatParam param = entry.getValue();
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texVal = (MatParamTexture) param;
+
+ if (nextTexUnit < texVal.getUnit() + 1) {
+ nextTexUnit = texVal.getUnit() + 1;
+ }
+
+ // the texture failed to load for this param
+ // do not add to param values
+ if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
+ continue;
+ }
+ }
+ param.setName(checkSetParam(param.getVarType(), param.getName()));
+ paramValues.put(param.getName(), param);
+ }
+
+ if (applyDefaultValues){
+ // compatability with old versions where default vars were
+ // not available
+ for (MatParam param : def.getMaterialParams()){
+ if (param.getValue() != null && paramValues.get(param.getName()) == null){
+ setParam(param.getName(), param.getVarType(), param.getValue());
+ }
+ }
+ }
+ if (guessRenderStateApply && additionalState != null){
+ // Try to guess values of "apply" render state based on defaults
+ // if value != default then set apply to true
+ additionalState.applyPolyOffset = additionalState.offsetEnabled;
+ additionalState.applyAlphaFallOff = additionalState.alphaTest;
+ additionalState.applyAlphaTest = additionalState.alphaTest;
+ additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off;
+ additionalState.applyColorWrite = !additionalState.colorWrite;
+ additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back;
+ additionalState.applyDepthTest = !additionalState.depthTest;
+ additionalState.applyDepthWrite = !additionalState.depthWrite;
+ additionalState.applyPointSprite = additionalState.pointSprite;
+ additionalState.applyStencilTest = additionalState.stencilTest;
+ additionalState.applyWireFrame = additionalState.wireframe;
+ }
+ if (enableVcolor) {
+ setBoolean("VertexColor", true);
+ }
+ if (separateTexCoord) {
+ setBoolean("SeparateTexCoord", true);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/material/MaterialDef.java b/engine/src/core/com/jme3/material/MaterialDef.java
new file mode 100644
index 0000000..e7ec3fc
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MaterialDef.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.material;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.shader.VarType;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Describes a J3MD (Material definition).
+ *
+ * @author Kirill Vainer
+ */
+public class MaterialDef {
+
+ private static final Logger logger = Logger.getLogger(MaterialDef.class.getName());
+
+ private String name;
+ private String assetName;
+ private AssetManager assetManager;
+
+ private List<TechniqueDef> defaultTechs;
+ private Map<String, TechniqueDef> techniques;
+ private Map<String, MatParam> matParams;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public MaterialDef(){
+ }
+
+ /**
+ * Creates a new material definition with the given name.
+ *
+ * @param assetManager The asset manager to use to load shaders
+ * @param name The debug name of the material definition
+ */
+ public MaterialDef(AssetManager assetManager, String name){
+ this.assetManager = assetManager;
+ this.name = name;
+ techniques = new HashMap<String, TechniqueDef>();
+ matParams = new HashMap<String, MatParam>();
+ defaultTechs = new ArrayList<TechniqueDef>();
+ logger.log(Level.INFO, "Loaded material definition: {0}", name);
+ }
+
+ /**
+ * Returns the asset key name of the asset from which this material
+ * definition was loaded.
+ *
+ * @return Asset key name of the j3md file
+ */
+ public String getAssetName() {
+ return assetName;
+ }
+
+ /**
+ * Set the asset key name.
+ *
+ * @param assetName the asset key name
+ */
+ public void setAssetName(String assetName) {
+ this.assetName = assetName;
+ }
+
+ /**
+ * Returns the AssetManager passed in the constructor.
+ *
+ * @return the AssetManager passed in the constructor.
+ */
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ /**
+ * The debug name of the material definition.
+ *
+ * @return debug name of the material definition.
+ */
+ public String getName(){
+ return name;
+ }
+
+ /**
+ * Adds a new material parameter.
+ *
+ * @param type Type of the parameter
+ * @param name Name of the parameter
+ * @param value Default value of the parameter
+ * @param ffBinding Fixed function binding for the parameter
+ */
+ public void addMaterialParam(VarType type, String name, Object value, FixedFuncBinding ffBinding) {
+ matParams.put(name, new MatParam(type, name, value, ffBinding));
+ }
+
+ /**
+ * Returns the material parameter with the given name.
+ *
+ * @param name The name of the parameter to retrieve
+ *
+ * @return The material parameter, or null if it does not exist.
+ */
+ public MatParam getMaterialParam(String name){
+ return matParams.get(name);
+ }
+
+ /**
+ * Returns a collection of all material parameters declared in this
+ * material definition.
+ * <p>
+ * Modifying the material parameters or the collection will lead
+ * to undefined results.
+ *
+ * @return All material parameters declared in this definition.
+ */
+ public Collection<MatParam> getMaterialParams(){
+ return matParams.values();
+ }
+
+ /**
+ * Adds a new technique definition to this material definition.
+ * <p>
+ * If the technique name is "Default", it will be added
+ * to the list of {@link MaterialDef#getDefaultTechniques() default techniques}.
+ *
+ * @param technique The technique definition to add.
+ */
+ public void addTechniqueDef(TechniqueDef technique){
+ if (technique.getName().equals("Default")){
+ defaultTechs.add(technique);
+ }else{
+ techniques.put(technique.getName(), technique);
+ }
+ }
+
+ /**
+ * Returns a list of all default techniques.
+ *
+ * @return a list of all default techniques.
+ */
+ public List<TechniqueDef> getDefaultTechniques(){
+ return defaultTechs;
+ }
+
+ /**
+ * Returns a technique definition with the given name.
+ * This does not include default techniques which can be
+ * retrieved via {@link MaterialDef#getDefaultTechniques() }.
+ *
+ * @param name The name of the technique definition to find
+ *
+ * @return The technique definition, or null if cannot be found.
+ */
+ public TechniqueDef getTechniqueDef(String name) {
+ return techniques.get(name);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/material/MaterialList.java b/engine/src/core/com/jme3/material/MaterialList.java
new file mode 100644
index 0000000..9f2a512
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MaterialList.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.material;
+
+import java.util.HashMap;
+
+/**
+ * A map from material name to a material. Used by loaders to locate
+ * materials for meshes inside a model.
+ *
+ * @author Kirill Vainer
+ */
+public class MaterialList extends HashMap<String, Material> {
+}
diff --git a/engine/src/core/com/jme3/material/RenderState.java b/engine/src/core/com/jme3/material/RenderState.java
new file mode 100644
index 0000000..37897fd
--- /dev/null
+++ b/engine/src/core/com/jme3/material/RenderState.java
@@ -0,0 +1,1070 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material;
+
+import com.jme3.export.*;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import java.io.IOException;
+
+/**
+ * <code>RenderState</code> specifies material rendering properties that cannot
+ * be controlled by a shader on a {@link Material}. The properties
+ * allow manipulation of rendering features such as depth testing, alpha blending,
+ * face culling, stencil operations, and much more.
+ *
+ * @author Kirill Vainer
+ */
+public class RenderState implements Cloneable, Savable {
+
+ /**
+ * The <code>DEFAULT</code> render state is the one used by default
+ * on all materials unless changed otherwise by the user.
+ *
+ * <p>
+ * It has the following properties:
+ * <ul>
+ * <li>Back Face Culling</li>
+ * <li>Depth Testing Enabled</li>
+ * <li>Depth Writing Enabled</li>
+ * </ul>
+ */
+ public static final RenderState DEFAULT = new RenderState();
+
+ /**
+ * The <code>NULL</code> render state is identical to the {@link RenderState#DEFAULT}
+ * render state except that depth testing and face culling are disabled.
+ */
+ public static final RenderState NULL = new RenderState();
+
+ /**
+ * The <code>ADDITIONAL</code> render state is identical to the
+ * {@link RenderState#DEFAULT} render state except that all apply
+ * values are set to false. This allows the <code>ADDITIONAL</code> render
+ * state to be combined with other state but only influencing values
+ * that were changed from the original.
+ */
+ public static final RenderState ADDITIONAL = new RenderState();
+
+ /**
+ * <code>TestFunction</code> specifies the testing function for stencil test
+ * function and alpha test function.
+ *
+ * <p>The functions work similarly as described except that for stencil
+ * test function, the reference value given in the stencil command is
+ * the input value while the reference is the value already in the stencil
+ * buffer.
+ */
+ public enum TestFunction {
+
+ /**
+ * The test always fails
+ */
+ Never,
+ /**
+ * The test succeeds if the input value is equal to the reference value.
+ */
+ Equal,
+ /**
+ * The test succeeds if the input value is less than the reference value.
+ */
+ Less,
+ /**
+ * The test succeeds if the input value is less than or equal to
+ * the reference value.
+ */
+ LessOrEqual,
+ /**
+ * The test succeeds if the input value is greater than the reference value.
+ */
+ Greater,
+ /**
+ * The test succeeds if the input value is greater than or equal to
+ * the reference value.
+ */
+ GreaterOrEqual,
+ /**
+ * The test succeeds if the input value does not equal the
+ * reference value.
+ */
+ NotEqual,
+ /**
+ * The test always passes
+ */
+ Always,}
+
+ /**
+ * <code>BlendMode</code> specifies the blending operation to use.
+ *
+ * @see RenderState#setBlendMode(com.jme3.material.RenderState.BlendMode)
+ */
+ public enum BlendMode {
+
+ /**
+ * No blending mode is used.
+ */
+ Off,
+ /**
+ * Additive blending. For use with glows and particle emitters.
+ * <p>
+ * Result = Source Color + Destination Color -> (GL_ONE, GL_ONE)
+ */
+ Additive,
+ /**
+ * Premultiplied alpha blending, for use with premult alpha textures.
+ * <p>
+ * Result = Source Color + (Dest Color * (1 - Source Alpha) ) -> (GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
+ */
+ PremultAlpha,
+ /**
+ * Additive blending that is multiplied with source alpha.
+ * For use with glows and particle emitters.
+ * <p>
+ * Result = (Source Alpha * Source Color) + Dest Color -> (GL_SRC_ALPHA, GL_ONE)
+ */
+ AlphaAdditive,
+ /**
+ * Color blending, blends in color from dest color
+ * using source color.
+ * <p>
+ * Result = Source Color + (1 - Source Color) * Dest Color -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR)
+ */
+ Color,
+ /**
+ * Alpha blending, interpolates to source color from dest color
+ * using source alpha.
+ * <p>
+ * Result = Source Alpha * Source Color +
+ * (1 - Source Alpha) * Dest Color -> (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
+ */
+ Alpha,
+ /**
+ * Multiplies the source and dest colors.
+ * <p>
+ * Result = Source Color * Dest Color -> (GL_DST_COLOR, GL_ZERO)
+ */
+ Modulate,
+ /**
+ * Multiplies the source and dest colors then doubles the result.
+ * <p>
+ * Result = 2 * Source Color * Dest Color -> (GL_DST_COLOR, GL_SRC_COLOR)
+ */
+ ModulateX2
+ }
+
+ /**
+ * <code>FaceCullMode</code> specifies the criteria for faces to be culled.
+ *
+ * @see RenderState#setFaceCullMode(com.jme3.material.RenderState.FaceCullMode)
+ */
+ public enum FaceCullMode {
+
+ /**
+ * Face culling is disabled.
+ */
+ Off,
+ /**
+ * Cull front faces
+ */
+ Front,
+ /**
+ * Cull back faces
+ */
+ Back,
+ /**
+ * Cull both front and back faces.
+ */
+ FrontAndBack
+ }
+
+ /**
+ * <code>StencilOperation</code> specifies the stencil operation to use
+ * in a certain scenario as specified in {@link RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilFunction,
+ * com.jme3.material.RenderState.StencilFunction)}
+ */
+ public enum StencilOperation {
+
+ /**
+ * Keep the current value.
+ */
+ Keep,
+ /**
+ * Set the value to 0
+ */
+ Zero,
+ /**
+ * Replace the value in the stencil buffer with the reference value.
+ */
+ Replace,
+
+ /**
+ * Increment the value in the stencil buffer, clamp once reaching
+ * the maximum value.
+ */
+ Increment,
+
+ /**
+ * Increment the value in the stencil buffer and wrap to 0 when
+ * reaching the maximum value.
+ */
+ IncrementWrap,
+ /**
+ * Decrement the value in the stencil buffer and clamp once reaching 0.
+ */
+ Decrement,
+ /**
+ * Decrement the value in the stencil buffer and wrap to the maximum
+ * value when reaching 0.
+ */
+ DecrementWrap,
+
+ /**
+ * Does a bitwise invert of the value in the stencil buffer.
+ */
+ Invert
+ }
+
+ static {
+ NULL.cullMode = FaceCullMode.Off;
+ NULL.depthTest = false;
+ }
+
+ static {
+ ADDITIONAL.applyPointSprite = false;
+ ADDITIONAL.applyWireFrame = false;
+ ADDITIONAL.applyCullMode = false;
+ ADDITIONAL.applyDepthWrite = false;
+ ADDITIONAL.applyDepthTest = false;
+ ADDITIONAL.applyColorWrite = false;
+ ADDITIONAL.applyBlendMode = false;
+ ADDITIONAL.applyAlphaTest = false;
+ ADDITIONAL.applyAlphaFallOff = false;
+ ADDITIONAL.applyPolyOffset = false;
+ }
+
+ boolean pointSprite = false;
+ boolean applyPointSprite = true;
+
+ boolean wireframe = false;
+ boolean applyWireFrame = true;
+
+ FaceCullMode cullMode = FaceCullMode.Back;
+ boolean applyCullMode = true;
+
+ boolean depthWrite = true;
+ boolean applyDepthWrite = true;
+
+ boolean depthTest = true;
+ boolean applyDepthTest = true;
+
+ boolean colorWrite = true;
+ boolean applyColorWrite = true;
+
+ BlendMode blendMode = BlendMode.Off;
+ boolean applyBlendMode = true;
+
+ boolean alphaTest = false;
+ boolean applyAlphaTest = true;
+
+ float alphaFallOff = 0;
+ boolean applyAlphaFallOff = true;
+
+ float offsetFactor = 0;
+ float offsetUnits = 0;
+ boolean offsetEnabled = false;
+ boolean applyPolyOffset = true;
+
+ boolean stencilTest = false;
+ boolean applyStencilTest = false;
+ StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep;
+ StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep;
+ StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep;
+ StencilOperation backStencilStencilFailOperation = StencilOperation.Keep;
+ StencilOperation backStencilDepthFailOperation = StencilOperation.Keep;
+ StencilOperation backStencilDepthPassOperation = StencilOperation.Keep;
+ TestFunction frontStencilFunction = TestFunction.Always;
+ TestFunction backStencilFunction = TestFunction.Always;
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(pointSprite, "pointSprite", false);
+ oc.write(wireframe, "wireframe", false);
+ oc.write(cullMode, "cullMode", FaceCullMode.Back);
+ oc.write(depthWrite, "depthWrite", true);
+ oc.write(depthTest, "depthTest", true);
+ oc.write(colorWrite, "colorWrite", true);
+ oc.write(blendMode, "blendMode", BlendMode.Off);
+ oc.write(alphaTest, "alphaTest", false);
+ oc.write(alphaFallOff, "alphaFallOff", 0);
+ oc.write(offsetEnabled, "offsetEnabled", false);
+ oc.write(offsetFactor, "offsetFactor", 0);
+ oc.write(offsetUnits, "offsetUnits", 0);
+ oc.write(stencilTest, "stencilTest", false);
+ oc.write(frontStencilStencilFailOperation, "frontStencilStencilFailOperation", StencilOperation.Keep);
+ oc.write(frontStencilDepthFailOperation, "frontStencilDepthFailOperation", StencilOperation.Keep);
+ oc.write(frontStencilDepthPassOperation, "frontStencilDepthPassOperation", StencilOperation.Keep);
+ oc.write(backStencilStencilFailOperation, "frontStencilStencilFailOperation", StencilOperation.Keep);
+ oc.write(backStencilDepthFailOperation, "backStencilDepthFailOperation", StencilOperation.Keep);
+ oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
+ oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
+ oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
+
+ // Only "additional render state" has them set to false by default
+ oc.write(applyPointSprite, "applyPointSprite", true);
+ oc.write(applyWireFrame, "applyWireFrame", true);
+ oc.write(applyCullMode, "applyCullMode", true);
+ oc.write(applyDepthWrite, "applyDepthWrite", true);
+ oc.write(applyDepthTest, "applyDepthTest", true);
+ oc.write(applyColorWrite, "applyColorWrite", true);
+ oc.write(applyBlendMode, "applyBlendMode", true);
+ oc.write(applyAlphaTest, "applyAlphaTest", true);
+ oc.write(applyAlphaFallOff, "applyAlphaFallOff", true);
+ oc.write(applyPolyOffset, "applyPolyOffset", true);
+
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ pointSprite = ic.readBoolean("pointSprite", false);
+ wireframe = ic.readBoolean("wireframe", false);
+ cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back);
+ depthWrite = ic.readBoolean("depthWrite", true);
+ depthTest = ic.readBoolean("depthTest", true);
+ colorWrite = ic.readBoolean("colorWrite", true);
+ blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off);
+ alphaTest = ic.readBoolean("alphaTest", false);
+ alphaFallOff = ic.readFloat("alphaFallOff", 0);
+ offsetEnabled = ic.readBoolean("offsetEnabled", false);
+ offsetFactor = ic.readFloat("offsetFactor", 0);
+ offsetUnits = ic.readFloat("offsetUnits", 0);
+ stencilTest = ic.readBoolean("stencilTest", false);
+ frontStencilStencilFailOperation = ic.readEnum("frontStencilStencilFailOperation", StencilOperation.class, StencilOperation.Keep);
+ frontStencilDepthFailOperation = ic.readEnum("frontStencilDepthFailOperation", StencilOperation.class, StencilOperation.Keep);
+ frontStencilDepthPassOperation = ic.readEnum("frontStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
+ backStencilStencilFailOperation = ic.readEnum("backStencilStencilFailOperation", StencilOperation.class, StencilOperation.Keep);
+ backStencilDepthFailOperation = ic.readEnum("backStencilDepthFailOperation", StencilOperation.class, StencilOperation.Keep);
+ backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
+ frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always);
+ backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
+
+ applyPointSprite = ic.readBoolean("applyPointSprite", true);
+ applyWireFrame = ic.readBoolean("applyWireFrame", true);
+ applyCullMode = ic.readBoolean("applyCullMode", true);
+ applyDepthWrite = ic.readBoolean("applyDepthWrite", true);
+ applyDepthTest = ic.readBoolean("applyDepthTest", true);
+ applyColorWrite = ic.readBoolean("applyColorWrite", true);
+ applyBlendMode = ic.readBoolean("applyBlendMode", true);
+ applyAlphaTest = ic.readBoolean("applyAlphaTest", true);
+ applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true);
+ applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
+ }
+
+ /**
+ * Create a clone of this <code>RenderState</code>
+ *
+ * @return Clone of this render state.
+ */
+ @Override
+ public RenderState clone() {
+ try {
+ return (RenderState) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Enables point sprite mode.
+ *
+ * <p>When point sprite is enabled, any meshes
+ * with the type of {@link Mode#Points} will be rendered as 2D quads
+ * with texturing enabled. Fragment shaders can write to the
+ * <code>gl_PointCoord</code> variable to manipulate the texture coordinate
+ * for each pixel. The size of the 2D quad can be controlled by writing
+ * to the <code>gl_PointSize</code> variable in the vertex shader.
+ *
+ * @param pointSprite Enables Point Sprite mode.
+ */
+ public void setPointSprite(boolean pointSprite) {
+ applyPointSprite = true;
+ this.pointSprite = pointSprite;
+ }
+
+ /**
+ * Sets the alpha fall off value for alpha testing.
+ *
+ * <p>If the pixel's alpha value is greater than the
+ * <code>alphaFallOff</code> then the pixel will be rendered, otherwise
+ * the pixel will be discarded.
+ *
+ * @param alphaFallOff The alpha of all rendered pixels must be higher
+ * than this value to be rendered. This value should be between 0 and 1.
+ *
+ * @see RenderState#setAlphaTest(boolean)
+ */
+ public void setAlphaFallOff(float alphaFallOff) {
+ applyAlphaFallOff = true;
+ this.alphaFallOff = alphaFallOff;
+ }
+
+ /**
+ * Enable alpha testing.
+ *
+ * <p>When alpha testing is enabled, all input pixels' alpha are compared
+ * to the {@link RenderState#setAlphaFallOff(float) constant alpha falloff}.
+ * If the input alpha is greater than the falloff, the pixel will be rendered,
+ * otherwise it will be discarded.
+ *
+ * @param alphaTest Set to true to enable alpha testing.
+ *
+ * @see RenderState#setAlphaFallOff(float)
+ */
+ public void setAlphaTest(boolean alphaTest) {
+ applyAlphaTest = true;
+ this.alphaTest = alphaTest;
+ }
+
+ /**
+ * Enable writing color.
+ *
+ * <p>When color write is enabled, the result of a fragment shader, the
+ * <code>gl_FragColor</code>, will be rendered into the color buffer
+ * (including alpha).
+ *
+ * @param colorWrite Set to true to enable color writing.
+ */
+ public void setColorWrite(boolean colorWrite) {
+ applyColorWrite = true;
+ this.colorWrite = colorWrite;
+ }
+
+ /**
+ * Set the face culling mode.
+ *
+ * <p>See the {@link FaceCullMode} enum on what each value does.
+ * Face culling will project the triangle's points onto the screen
+ * and determine if the triangle is in counter-clockwise order or
+ * clockwise order. If a triangle is in counter-clockwise order, then
+ * it is considered a front-facing triangle, otherwise, it is considered
+ * a back-facing triangle.
+ *
+ * @param cullMode the face culling mode.
+ */
+ public void setFaceCullMode(FaceCullMode cullMode) {
+ applyCullMode = true;
+ this.cullMode = cullMode;
+ }
+
+ /**
+ * Set the blending mode.
+ *
+ * <p>When blending is enabled, (<code>blendMode</code> is not {@link BlendMode#Off})
+ * the input pixel will be blended with the pixel
+ * already in the color buffer. The blending operation is determined
+ * by the {@link BlendMode}. For example, the {@link BlendMode#Additive}
+ * will add the input pixel's color to the color already in the color buffer:
+ * <br/>
+ * <code>Result = Source Color + Destination Color</code>
+ *
+ * @param blendMode The blend mode to use. Set to {@link BlendMode#Off}
+ * to disable blending.
+ */
+ public void setBlendMode(BlendMode blendMode) {
+ applyBlendMode = true;
+ this.blendMode = blendMode;
+ }
+
+ /**
+ * Enable depth testing.
+ *
+ * <p>When depth testing is enabled, a pixel must pass the depth test
+ * before it is written to the color buffer.
+ * The input pixel's depth value must be less than or equal than
+ * the value already in the depth buffer to pass the depth test.
+ *
+ * @param depthTest Enable or disable depth testing.
+ */
+ public void setDepthTest(boolean depthTest) {
+ applyDepthTest = true;
+ this.depthTest = depthTest;
+ }
+
+ /**
+ * Enable depth writing.
+ *
+ * <p>After passing the {@link RenderState#setDepthTest(boolean) depth test},
+ * a pixel's depth value will be written into the depth buffer if
+ * depth writing is enabled.
+ *
+ * @param depthWrite True to enable writing to the depth buffer.
+ */
+ public void setDepthWrite(boolean depthWrite) {
+ applyDepthWrite = true;
+ this.depthWrite = depthWrite;
+ }
+
+ /**
+ * Enables wireframe rendering mode.
+ *
+ * <p>When in wireframe mode, {@link Mesh meshes} rendered in triangle mode
+ * will not be solid, but instead, only the edges of the triangles
+ * will be rendered.
+ *
+ * @param wireframe True to enable wireframe mode.
+ */
+ public void setWireframe(boolean wireframe) {
+ applyWireFrame = true;
+ this.wireframe = wireframe;
+ }
+
+ /**
+ * Offsets the on-screen z-order of the material's polygons, to combat visual artefacts like
+ * stitching, bleeding and z-fighting for overlapping polygons.
+ * Factor and units are summed to produce the depth offset.
+ * This offset is applied in screen space,
+ * typically with positive Z pointing into the screen.
+ * Typical values are (1.0f, 1.0f) or (-1.0f, -1.0f)
+ *
+ * @see <a href="http://www.opengl.org/resources/faq/technical/polygonoffset.htm" rel="nofollow">http://www.opengl.org/resources/faq/technical/polygonoffset.htm</a>
+ * @param factor scales the maximum Z slope, with respect to X or Y of the polygon
+ * @param units scales the minimum resolvable depth buffer value
+ **/
+ public void setPolyOffset(float factor, float units) {
+ applyPolyOffset = true;
+ offsetEnabled = true;
+ offsetFactor = factor;
+ offsetUnits = units;
+ }
+
+ /**
+ * Enable stencil testing.
+ *
+ * <p>Stencil testing can be used to filter pixels according to the stencil
+ * buffer. Objects can be rendered with some stencil operation to manipulate
+ * the values in the stencil buffer, then, other objects can be rendered
+ * to test against the values written previously.
+ *
+ * @param enabled Set to true to enable stencil functionality. If false
+ * all other parameters are ignored.
+ *
+ * @param _frontStencilStencilFailOperation Sets the operation to occur when
+ * a front-facing triangle fails the front stencil function.
+ * @param _frontStencilDepthFailOperation Sets the operation to occur when
+ * a front-facing triangle fails the depth test.
+ * @param _frontStencilDepthPassOperation Set the operation to occur when
+ * a front-facing triangle passes the depth test.
+ * @param _backStencilStencilFailOperation Set the operation to occur when
+ * a back-facing triangle fails the back stencil function.
+ * @param _backStencilDepthFailOperation Set the operation to occur when
+ * a back-facing triangle fails the depth test.
+ * @param _backStencilDepthPassOperation Set the operation to occur when
+ * a back-facing triangle passes the depth test.
+ * @param _frontStencilFunction Set the test function for front-facing triangles.
+ * @param _backStencilFunction Set the test function for back-facing triangles.
+ */
+ public void setStencil(boolean enabled,
+ StencilOperation _frontStencilStencilFailOperation,
+ StencilOperation _frontStencilDepthFailOperation,
+ StencilOperation _frontStencilDepthPassOperation,
+ StencilOperation _backStencilStencilFailOperation,
+ StencilOperation _backStencilDepthFailOperation,
+ StencilOperation _backStencilDepthPassOperation,
+ TestFunction _frontStencilFunction,
+ TestFunction _backStencilFunction) {
+
+ stencilTest = enabled;
+ applyStencilTest = true;
+ this.frontStencilStencilFailOperation = _frontStencilStencilFailOperation;
+ this.frontStencilDepthFailOperation = _frontStencilDepthFailOperation;
+ this.frontStencilDepthPassOperation = _frontStencilDepthPassOperation;
+ this.backStencilStencilFailOperation = _backStencilStencilFailOperation;
+ this.backStencilDepthFailOperation = _backStencilDepthFailOperation;
+ this.backStencilDepthPassOperation = _backStencilDepthPassOperation;
+ this.frontStencilFunction = _frontStencilFunction;
+ this.backStencilFunction = _backStencilFunction;
+ }
+
+ /**
+ * Check if stencil test is enabled.
+ *
+ * @return True if stencil test is enabled.
+ */
+ public boolean isStencilTest() {
+ return stencilTest;
+ }
+
+ /**
+ * Retrieve the front stencil fail operation.
+ *
+ * @return the front stencil fail operation.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public StencilOperation getFrontStencilStencilFailOperation() {
+ return frontStencilStencilFailOperation;
+ }
+
+ /**
+ * Retrieve the front depth test fail operation.
+ *
+ * @return the front depth test fail operation.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public StencilOperation getFrontStencilDepthFailOperation() {
+ return frontStencilDepthFailOperation;
+ }
+
+ /**
+ * Retrieve the front depth test pass operation.
+ *
+ * @return the front depth test pass operation.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public StencilOperation getFrontStencilDepthPassOperation() {
+ return frontStencilDepthPassOperation;
+ }
+
+ /**
+ * Retrieve the back stencil fail operation.
+ *
+ * @return the back stencil fail operation.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public StencilOperation getBackStencilStencilFailOperation() {
+ return backStencilStencilFailOperation;
+ }
+
+ /**
+ * Retrieve the back depth test fail operation.
+ *
+ * @return the back depth test fail operation.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public StencilOperation getBackStencilDepthFailOperation() {
+ return backStencilDepthFailOperation;
+ }
+
+ /**
+ * Retrieve the back depth test pass operation.
+ *
+ * @return the back depth test pass operation.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public StencilOperation getBackStencilDepthPassOperation() {
+ return backStencilDepthPassOperation;
+ }
+
+ /**
+ * Retrieve the front stencil function.
+ *
+ * @return the front stencil function.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public TestFunction getFrontStencilFunction() {
+ return frontStencilFunction;
+ }
+
+ /**
+ * Retrieve the back stencil function.
+ *
+ * @return the back stencil function.
+ *
+ * @see RenderState#setStencil(boolean,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.StencilOperation,
+ * com.jme3.material.RenderState.TestFunction,
+ * com.jme3.material.RenderState.TestFunction)
+ */
+ public TestFunction getBackStencilFunction() {
+ return backStencilFunction;
+ }
+
+ /**
+ * Retrieve the blend mode.
+ *
+ * @return the blend mode.
+ */
+ public BlendMode getBlendMode() {
+ return blendMode;
+ }
+
+ /**
+ * Check if point sprite mode is enabled
+ *
+ * @return True if point sprite mode is enabled.
+ *
+ * @see RenderState#setPointSprite(boolean)
+ */
+ public boolean isPointSprite() {
+ return pointSprite;
+ }
+
+ /**
+ * Check if alpha test is enabled.
+ *
+ * @return True if alpha test is enabled.
+ *
+ * @see RenderState#setAlphaTest(boolean)
+ */
+ public boolean isAlphaTest() {
+ return alphaTest;
+ }
+
+ /**
+ * Retrieve the face cull mode.
+ *
+ * @return the face cull mode.
+ *
+ * @see RenderState#setFaceCullMode(com.jme3.material.RenderState.FaceCullMode)
+ */
+ public FaceCullMode getFaceCullMode() {
+ return cullMode;
+ }
+
+ /**
+ * Check if depth test is enabled.
+ *
+ * @return True if depth test is enabled.
+ *
+ * @see RenderState#setDepthTest(boolean)
+ */
+ public boolean isDepthTest() {
+ return depthTest;
+ }
+
+ /**
+ * Check if depth write is enabled.
+ *
+ * @return True if depth write is enabled.
+ *
+ * @see RenderState#setDepthWrite(boolean)
+ */
+ public boolean isDepthWrite() {
+ return depthWrite;
+ }
+
+ /**
+ * Check if wireframe mode is enabled.
+ *
+ * @return True if wireframe mode is enabled.
+ *
+ * @see RenderState#setWireframe(boolean)
+ */
+ public boolean isWireframe() {
+ return wireframe;
+ }
+
+ /**
+ * Check if color writing is enabled.
+ *
+ * @return True if color writing is enabled.
+ *
+ * @see RenderState#setColorWrite(boolean)
+ */
+ public boolean isColorWrite() {
+ return colorWrite;
+ }
+
+ /**
+ * Retrieve the poly offset factor value.
+ *
+ * @return the poly offset factor value.
+ *
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public float getPolyOffsetFactor() {
+ return offsetFactor;
+ }
+
+ /**
+ * Retrieve the poly offset units value.
+ *
+ * @return the poly offset units value.
+ *
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public float getPolyOffsetUnits() {
+ return offsetUnits;
+ }
+
+ /**
+ * Check if polygon offset is enabled.
+ *
+ * @return True if polygon offset is enabled.
+ *
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public boolean isPolyOffset() {
+ return offsetEnabled;
+ }
+
+ /**
+ * Retrieve the alpha falloff value.
+ *
+ * @return the alpha falloff value.
+ *
+ * @see RenderState#setAlphaFallOff(float)
+ */
+ public float getAlphaFallOff() {
+ return alphaFallOff;
+ }
+
+ public boolean isApplyAlphaFallOff() {
+ return applyAlphaFallOff;
+ }
+
+ public boolean isApplyAlphaTest() {
+ return applyAlphaTest;
+ }
+
+ public boolean isApplyBlendMode() {
+ return applyBlendMode;
+ }
+
+ public boolean isApplyColorWrite() {
+ return applyColorWrite;
+ }
+
+ public boolean isApplyCullMode() {
+ return applyCullMode;
+ }
+
+ public boolean isApplyDepthTest() {
+ return applyDepthTest;
+ }
+
+ public boolean isApplyDepthWrite() {
+ return applyDepthWrite;
+ }
+
+ public boolean isApplyPointSprite() {
+ return applyPointSprite;
+ }
+
+ public boolean isApplyPolyOffset() {
+ return applyPolyOffset;
+ }
+
+ public boolean isApplyWireFrame() {
+ return applyWireFrame;
+ }
+
+ /**
+ * Merges <code>this</code> state and <code>additionalState</code> into
+ * the parameter <code>state</code> based on a specific criteria.
+ *
+ * <p>The criteria for this merge is the following:<br/>
+ * For every given property, such as alpha test or depth write, check
+ * if it was modified from the original in the <code>additionalState</code>
+ * if it was modified, then copy the property from the <code>additionalState</code>
+ * into the parameter <code>state</code>, otherwise, copy the property from <code>this</code>
+ * into the parameter <code>state</code>. If <code>additionalState</code>
+ * is <code>null</code>, then no modifications are made and <code>this</code> is returned,
+ * otherwise, the parameter <code>state</code> is returned with the result
+ * of the merge.
+ *
+ * @param additionalState The <code>additionalState</code>, from which data is taken only
+ * if it was modified by the user.
+ * @param state Contains output of the method if <code>additionalState</code>
+ * is not null.
+ * @return <code>state</code> if <code>additionalState</code> is non-null,
+ * otherwise returns <code>this</code>
+ */
+ public RenderState copyMergedTo(RenderState additionalState, RenderState state) {
+ if (additionalState == null) {
+ return this;
+ }
+
+ if (additionalState.applyPointSprite) {
+ state.pointSprite = additionalState.pointSprite;
+ } else {
+ state.pointSprite = pointSprite;
+ }
+ if (additionalState.applyWireFrame) {
+ state.wireframe = additionalState.wireframe;
+ } else {
+ state.wireframe = wireframe;
+ }
+
+ if (additionalState.applyCullMode) {
+ state.cullMode = additionalState.cullMode;
+ } else {
+ state.cullMode = cullMode;
+ }
+ if (additionalState.applyDepthWrite) {
+ state.depthWrite = additionalState.depthWrite;
+ } else {
+ state.depthWrite = depthWrite;
+ }
+ if (additionalState.applyDepthTest) {
+ state.depthTest = additionalState.depthTest;
+ } else {
+ state.depthTest = depthTest;
+ }
+ if (additionalState.applyColorWrite) {
+ state.colorWrite = additionalState.colorWrite;
+ } else {
+ state.colorWrite = colorWrite;
+ }
+ if (additionalState.applyBlendMode) {
+ state.blendMode = additionalState.blendMode;
+ } else {
+ state.blendMode = blendMode;
+ }
+ if (additionalState.applyAlphaTest) {
+ state.alphaTest = additionalState.alphaTest;
+ } else {
+ state.alphaTest = alphaTest;
+ }
+
+ if (additionalState.applyAlphaFallOff) {
+ state.alphaFallOff = additionalState.alphaFallOff;
+ } else {
+ state.alphaFallOff = alphaFallOff;
+ }
+ if (additionalState.applyPolyOffset) {
+ state.offsetEnabled = additionalState.offsetEnabled;
+ state.offsetFactor = additionalState.offsetFactor;
+ state.offsetUnits = additionalState.offsetUnits;
+ } else {
+ state.offsetEnabled = offsetEnabled;
+ state.offsetFactor = offsetFactor;
+ state.offsetUnits = offsetUnits;
+ }
+ if (additionalState.applyStencilTest){
+ state.stencilTest = additionalState.stencilTest;
+
+ state.frontStencilStencilFailOperation = additionalState.frontStencilStencilFailOperation;
+ state.frontStencilDepthFailOperation = additionalState.frontStencilDepthFailOperation;
+ state.frontStencilDepthPassOperation = additionalState.frontStencilDepthPassOperation;
+
+ state.backStencilStencilFailOperation = additionalState.backStencilStencilFailOperation;
+ state.backStencilDepthFailOperation = additionalState.backStencilDepthFailOperation;
+ state.backStencilDepthPassOperation = additionalState.backStencilDepthPassOperation;
+
+ state.frontStencilFunction = additionalState.frontStencilFunction;
+ state.backStencilFunction = additionalState.backStencilFunction;
+ }else{
+ state.stencilTest = stencilTest;
+
+ state.frontStencilStencilFailOperation = frontStencilStencilFailOperation;
+ state.frontStencilDepthFailOperation = frontStencilDepthFailOperation;
+ state.frontStencilDepthPassOperation = frontStencilDepthPassOperation;
+
+ state.backStencilStencilFailOperation = backStencilStencilFailOperation;
+ state.backStencilDepthFailOperation = backStencilDepthFailOperation;
+ state.backStencilDepthPassOperation = backStencilDepthPassOperation;
+
+ state.frontStencilFunction = frontStencilFunction;
+ state.backStencilFunction = backStencilFunction;
+ }
+ return state;
+ }
+
+ @Override
+ public String toString() {
+ return "RenderState[\n" + "pointSprite=" + pointSprite + "\napplyPointSprite=" + applyPointSprite + "\nwireframe=" + wireframe + "\napplyWireFrame=" + applyWireFrame + "\ncullMode=" + cullMode + "\napplyCullMode=" + applyCullMode + "\ndepthWrite=" + depthWrite + "\napplyDepthWrite=" + applyDepthWrite + "\ndepthTest=" + depthTest + "\napplyDepthTest=" + applyDepthTest + "\ncolorWrite=" + colorWrite + "\napplyColorWrite=" + applyColorWrite + "\nblendMode=" + blendMode + "\napplyBlendMode=" + applyBlendMode + "\nalphaTest=" + alphaTest + "\napplyAlphaTest=" + applyAlphaTest + "\nalphaFallOff=" + alphaFallOff + "\napplyAlphaFallOff=" + applyAlphaFallOff + "\noffsetEnabled=" + offsetEnabled + "\napplyPolyOffset=" + applyPolyOffset + "\noffsetFactor=" + offsetFactor + "\noffsetUnits=" + offsetUnits + "\n]";
+ }
+}
diff --git a/engine/src/core/com/jme3/material/Technique.java b/engine/src/core/com/jme3/material/Technique.java
new file mode 100644
index 0000000..5ae20a5
--- /dev/null
+++ b/engine/src/core/com/jme3/material/Technique.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.shader.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Represents a technique instance.
+ */
+public class Technique implements Savable {
+
+ private static final Logger logger = Logger.getLogger(Technique.class.getName());
+ private TechniqueDef def;
+ private Material owner;
+ private ArrayList<Uniform> worldBindUniforms;
+ private DefineList defines;
+ private Shader shader;
+ private boolean needReload = true;
+
+ /**
+ * Creates a new technique instance that implements the given
+ * technique definition.
+ *
+ * @param owner The material that will own this technique
+ * @param def The technique definition being implemented.
+ */
+ public Technique(Material owner, TechniqueDef def) {
+ this.owner = owner;
+ this.def = def;
+ if (def.isUsingShaders()) {
+ this.worldBindUniforms = new ArrayList<Uniform>();
+ this.defines = new DefineList();
+ }
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Technique() {
+ }
+
+ /**
+ * Returns the technique definition that is implemented by this technique
+ * instance.
+ *
+ * @return the technique definition that is implemented by this technique
+ * instance.
+ */
+ public TechniqueDef getDef() {
+ return def;
+ }
+
+ /**
+ * Returns the shader currently used by this technique instance.
+ * <p>
+ * Shaders are typically loaded dynamically when the technique is first
+ * used, therefore, this variable will most likely be null most of the time.
+ *
+ * @return the shader currently used by this technique instance.
+ */
+ public Shader getShader() {
+ return shader;
+ }
+
+ /**
+ * Returns a list of uniforms that implements the world parameters
+ * that were requested by the material definition.
+ *
+ * @return a list of uniforms implementing the world parameters.
+ */
+ public List<Uniform> getWorldBindUniforms() {
+ return worldBindUniforms;
+ }
+
+ /**
+ * Called by the material to tell the technique a parameter was modified
+ */
+ void notifySetParam(String paramName, VarType type, Object value) {
+ String defineName = def.getShaderParamDefine(paramName);
+ if (defineName != null) {
+ needReload = defines.set(defineName, type, value);
+ }
+ if (shader != null) {
+ updateUniformParam(paramName, type, value);
+ }
+ }
+
+ /**
+ * Called by the material to tell the technique a parameter was cleared
+ */
+ void notifyClearParam(String paramName) {
+ String defineName = def.getShaderParamDefine(paramName);
+ if (defineName != null) {
+ needReload = defines.remove(defineName);
+ }
+ if (shader != null) {
+ if (!paramName.startsWith("m_")) {
+ paramName = "m_" + paramName;
+ }
+ shader.removeUniform(paramName);
+ }
+ }
+
+ void updateUniformParam(String paramName, VarType type, Object value, boolean ifNotOwner) {
+ Uniform u = shader.getUniform(paramName);
+
+// if (ifNotOwner && u.getLastChanger() == owner)
+// return;
+
+ switch (type) {
+ case Texture2D: // fall intentional
+ case Texture3D:
+ case TextureArray:
+ case TextureCubeMap:
+ case Int:
+ u.setValue(VarType.Int, value);
+ break;
+ default:
+ u.setValue(type, value);
+ break;
+ }
+// u.setLastChanger(owner);
+ }
+
+ void updateUniformParam(String paramName, VarType type, Object value) {
+ updateUniformParam(paramName, type, value, false);
+ }
+
+ /**
+ * Returns true if the technique must be reloaded.
+ * <p>
+ * If a technique needs to reload, then the {@link Material} should
+ * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
+ * technique.
+ *
+ * @return true if the technique must be reloaded.
+ */
+ public boolean isNeedReload() {
+ return needReload;
+ }
+
+ /**
+ * Prepares the technique for use by loading the shader and setting
+ * the proper defines based on material parameters.
+ *
+ * @param assetManager The asset manager to use for loading shaders.
+ */
+ public void makeCurrent(AssetManager assetManager) {
+ // check if reload is needed..
+ if (def.isUsingShaders()) {
+ DefineList newDefines = new DefineList();
+ Collection<MatParam> params = owner.getParams();
+ for (MatParam param : params) {
+ String defineName = def.getShaderParamDefine(param.getName());
+ if (defineName != null) {
+ newDefines.set(defineName, param.getVarType(), param.getValue());
+ }
+ }
+
+ if (!needReload && defines.getCompiled().equals(newDefines.getCompiled())) {
+ newDefines = null;
+ // defines have not been changed..
+ } else {
+ defines.clear();
+ defines.addFrom(newDefines);
+ // defines changed, recompile needed
+ loadShader(assetManager);
+ }
+ }
+ }
+
+ private void loadShader(AssetManager manager) {
+ // recompute define list
+ DefineList allDefines = new DefineList();
+ allDefines.addFrom(def.getShaderPresetDefines());
+ allDefines.addFrom(defines);
+
+ ShaderKey key = new ShaderKey(def.getVertexShaderName(),
+ def.getFragmentShaderName(),
+ allDefines,
+ def.getShaderLanguage());
+ shader = manager.loadShader(key);
+ if (shader == null) {
+ logger.warning("Failed to reload shader!");
+ return;
+ }
+
+ // refresh the uniform links
+ //owner.updateUniformLinks();
+
+ // register the world bound uniforms
+ worldBindUniforms.clear();
+ if (def.getWorldBindings() != null) {
+ for (UniformBinding binding : def.getWorldBindings()) {
+ Uniform uniform = shader.getUniform("g_" + binding.name());
+ uniform.setBinding(binding);
+ if (uniform != null) {
+ worldBindUniforms.add(uniform);
+ }
+ }
+ }
+
+ needReload = false;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(def, "def", null);
+ // TODO:
+ // oc.write(owner, "owner", null);
+ oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null);
+ oc.write(defines, "defines", null);
+ oc.write(shader, "shader", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ def = (TechniqueDef) ic.readSavable("def", null);
+ worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null);
+ defines = (DefineList) ic.readSavable("defines", null);
+ shader = (Shader) ic.readSavable("shader", null);
+ //if (shader != null)
+ // owner.updateUniformLinks();
+ }
+}
diff --git a/engine/src/core/com/jme3/material/TechniqueDef.java b/engine/src/core/com/jme3/material/TechniqueDef.java
new file mode 100644
index 0000000..aaeb340
--- /dev/null
+++ b/engine/src/core/com/jme3/material/TechniqueDef.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.material;
+
+import com.jme3.export.*;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.Renderer;
+import com.jme3.shader.DefineList;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VarType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Describes a technique definition.
+ *
+ * @author Kirill Vainer
+ */
+public class TechniqueDef implements Savable {
+
+ /**
+ * Describes light rendering mode.
+ */
+ public enum LightMode {
+ /**
+ * Disable light-based rendering
+ */
+ Disable,
+
+ /**
+ * Enable light rendering by using a single pass.
+ * <p>
+ * An array of light positions and light colors is passed to the shader
+ * containing the world light list for the geometry being rendered.
+ */
+ SinglePass,
+
+ /**
+ * Enable light rendering by using multi-pass rendering.
+ * <p>
+ * The geometry will be rendered once for each light. Each time the
+ * light position and light color uniforms are updated to contain
+ * the values for the current light. The ambient light color uniform
+ * is only set to the ambient light color on the first pass, future
+ * passes have it set to black.
+ */
+ MultiPass,
+
+ /**
+ * Enable light rendering by using the
+ * {@link Renderer#setLighting(com.jme3.light.LightList) renderer's setLighting}
+ * method.
+ * <p>
+ * The specific details of rendering the lighting is up to the
+ * renderer implementation.
+ */
+ FixedPipeline,
+ }
+
+ public enum ShadowMode {
+ Disable,
+ InPass,
+ PostPass,
+ }
+
+ private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
+ private String name;
+
+ private String vertName;
+ private String fragName;
+ private String shaderLang;
+ private DefineList presetDefines;
+ private boolean usesShaders;
+
+ private RenderState renderState;
+ private LightMode lightMode = LightMode.Disable;
+ private ShadowMode shadowMode = ShadowMode.Disable;
+
+ private HashMap<String, String> defineParams;
+ private ArrayList<UniformBinding> worldBinds;
+
+ /**
+ * Creates a new technique definition.
+ * <p>
+ * Used internally by the J3M/J3MD loader.
+ *
+ * @param name The name of the technique, should be set to <code>null</code>
+ * for default techniques.
+ */
+ public TechniqueDef(String name){
+ this.name = name == null ? "Default" : name;
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public TechniqueDef(){
+ }
+
+ /**
+ * Returns the name of this technique as specified in the J3MD file.
+ * Default techniques have the name "Default".
+ *
+ * @return the name of this technique
+ */
+ public String getName(){
+ return name;
+ }
+
+ /**
+ * Returns the light mode.
+ * @return the light mode.
+ * @see LightMode
+ */
+ public LightMode getLightMode() {
+ return lightMode;
+ }
+
+ /**
+ * Set the light mode
+ *
+ * @param lightMode the light mode
+ *
+ * @see LightMode
+ */
+ public void setLightMode(LightMode lightMode) {
+ this.lightMode = lightMode;
+ }
+
+ /**
+ * Returns the shadow mode.
+ * @return the shadow mode.
+ */
+ public ShadowMode getShadowMode() {
+ return shadowMode;
+ }
+
+ /**
+ * Set the shadow mode.
+ *
+ * @param shadowMode the shadow mode.
+ *
+ * @see ShadowMode
+ */
+ public void setShadowMode(ShadowMode shadowMode) {
+ this.shadowMode = shadowMode;
+ }
+
+ /**
+ * Returns the render state that this technique is using
+ * @return the render state that this technique is using
+ * @see #setRenderState(com.jme3.material.RenderState)
+ */
+ public RenderState getRenderState() {
+ return renderState;
+ }
+
+ /**
+ * Sets the render state that this technique is using.
+ *
+ * @param renderState the render state that this technique is using.
+ *
+ * @see RenderState
+ */
+ public void setRenderState(RenderState renderState) {
+ this.renderState = renderState;
+ }
+
+ /**
+ * Returns true if this technique uses shaders, false otherwise.
+ *
+ * @return true if this technique uses shaders, false otherwise.
+ *
+ * @see #setShaderFile(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public boolean isUsingShaders(){
+ return usesShaders;
+ }
+
+ /**
+ * Gets the {@link Caps renderer capabilities} that are required
+ * by this technique.
+ *
+ * @return the required renderer capabilities
+ */
+ public EnumSet<Caps> getRequiredCaps() {
+ return requiredCaps;
+ }
+
+ /**
+ * Sets the shaders that this technique definition will use.
+ *
+ * @param vertexShader The name of the vertex shader
+ * @param fragmentShader The name of the fragment shader
+ * @param shaderLanguage The shader language
+ */
+ public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){
+ this.vertName = vertexShader;
+ this.fragName = fragmentShader;
+ this.shaderLang = shaderLanguage;
+
+ Caps langCap = Caps.valueOf(shaderLanguage);
+ requiredCaps.add(langCap);
+
+ usesShaders = true;
+ }
+
+ /**
+ * Returns the define name which the given material parameter influences.
+ *
+ * @param paramName The parameter name to look up
+ * @return The define name
+ *
+ * @see #addShaderParamDefine(java.lang.String, java.lang.String)
+ */
+ public String getShaderParamDefine(String paramName){
+ if (defineParams == null)
+ return null;
+
+ return defineParams.get(paramName);
+ }
+
+ /**
+ * Adds a define linked to a material parameter.
+ * <p>
+ * Any time the material parameter on the parent material is altered,
+ * the appropriate define on the technique will be modified as well.
+ * See the method
+ * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
+ * on the exact details of how the material parameter changes the define.
+ *
+ * @param paramName The name of the material parameter to link to.
+ * @param defineName The name of the define parameter, e.g. USE_LIGHTING
+ */
+ public void addShaderParamDefine(String paramName, String defineName){
+ if (defineParams == null)
+ defineParams = new HashMap<String, String>();
+
+ defineParams.put(paramName, defineName);
+ }
+
+ /**
+ * Returns the {@link DefineList} for the preset defines.
+ *
+ * @return the {@link DefineList} for the preset defines.
+ *
+ * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
+ */
+ public DefineList getShaderPresetDefines() {
+ return presetDefines;
+ }
+
+ /**
+ * Adds a preset define.
+ * <p>
+ * Preset defines do not depend upon any parameters to be activated,
+ * they are always passed to the shader as long as this technique is used.
+ *
+ * @param defineName The name of the define parameter, e.g. USE_LIGHTING
+ * @param type The type of the define. See
+ * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
+ * to see why it matters.
+ *
+ * @param value The value of the define
+ */
+ public void addShaderPresetDefine(String defineName, VarType type, Object value){
+ if (presetDefines == null)
+ presetDefines = new DefineList();
+
+ presetDefines.set(defineName, type, value);
+ }
+
+ /**
+ * Returns the name of the fragment shader used by the technique, or null
+ * if no fragment shader is specified.
+ *
+ * @return the name of the fragment shader to be used.
+ */
+ public String getFragmentShaderName() {
+ return fragName;
+ }
+
+
+ /**
+ * Returns the name of the vertex shader used by the technique, or null
+ * if no vertex shader is specified.
+ *
+ * @return the name of the vertex shader to be used.
+ */
+ public String getVertexShaderName() {
+ return vertName;
+ }
+
+ /**
+ * Returns the shader language of the shaders used in this technique.
+ *
+ * @return the shader language of the shaders used in this technique.
+ */
+ public String getShaderLanguage() {
+ return shaderLang;
+ }
+
+ /**
+ * Adds a new world parameter by the given name.
+ *
+ * @param name The world parameter to add.
+ * @return True if the world parameter name was found and added
+ * to the list of world parameters, false otherwise.
+ */
+ public boolean addWorldParam(String name) {
+ if (worldBinds == null){
+ worldBinds = new ArrayList<UniformBinding>();
+ }
+
+ try {
+ worldBinds.add( UniformBinding.valueOf(name) );
+ return true;
+ } catch (IllegalArgumentException ex){
+ return false;
+ }
+ }
+
+ /**
+ * Returns a list of world parameters that are used by this
+ * technique definition.
+ *
+ * @return The list of world parameters
+ */
+ public List<UniformBinding> getWorldBindings() {
+ return worldBinds;
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", null);
+ oc.write(vertName, "vertName", null);
+ oc.write(fragName, "fragName", null);
+ oc.write(shaderLang, "shaderLang", null);
+ oc.write(presetDefines, "presetDefines", null);
+ oc.write(lightMode, "lightMode", LightMode.Disable);
+ oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
+ oc.write(renderState, "renderState", null);
+ oc.write(usesShaders, "usesShaders", false);
+ // TODO: Finish this when Map<String, String> export is available
+// oc.write(defineParams, "defineParams", null);
+ // TODO: Finish this when List<Enum> export is available
+// oc.write(worldBinds, "worldBinds", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", null);
+ vertName = ic.readString("vertName", null);
+ fragName = ic.readString("fragName", null);
+ shaderLang = ic.readString("shaderLang", null);
+ presetDefines = (DefineList) ic.readSavable("presetDefines", null);
+ lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
+ shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
+ renderState = (RenderState) ic.readSavable("renderState", null);
+ usesShaders = ic.readBoolean("usesShaders", false);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/material/package.html b/engine/src/core/com/jme3/material/package.html
new file mode 100644
index 0000000..9af9cc8
--- /dev/null
+++ b/engine/src/core/com/jme3/material/package.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.material</code> package contains classes for manipulating
+jMonkeyEngine materials.
+Materials are applied to {@link com.jme3.scene.Geometry geometries} in the
+scene.
+Each geometry has a single material which is used to render that
+geometry.
+<p>
+Materials (also known as material instances) are extended from
+material definitions.
+
+<h3>Material definitions</h3>
+<p>
+Material definitions provide the "logic" for the material. Usually a shader that
+will handle drawing the object, and corresponding parameters that allow
+configuration of the shader.
+Material definitions can be created through J3MD files.
+The J3MD file abstracts the shader and its configuration away from the user, allowing a
+simple interface where one can simply set a few parameters on the material to change its
+appearance and the way its handled.
+
+<h3>Techniques</h3>
+<p>
+Techniques specify a specific way of rendering a material. Typically
+a technique is used to implement the same material for each configuration
+of the system. For GPUs that do not support shaders, a "fixed function pipeline"
+technique could exist to take care of rendering for that configuration
+
+<h3>Render states</h3>
+<p>
+See {@link com.jme3.material.RenderState}.
+
+<h3>Example Usage</h3>
+<p>
+Creating a textured material
+<code>
+// Create a material instance
+Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+
+// Load the texture.
+Texture tex = assetManager.loadTexture("Textures/Test/Test.jpg");
+
+// Set the parameters
+mat.setTexture("ColorMap", tex);
+</code>
+
+
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/math/AbstractTriangle.java b/engine/src/core/com/jme3/math/AbstractTriangle.java
new file mode 100644
index 0000000..1ea6da5
--- /dev/null
+++ b/engine/src/core/com/jme3/math/AbstractTriangle.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+
+public abstract class AbstractTriangle implements Collidable {
+
+ public abstract Vector3f get1();
+ public abstract Vector3f get2();
+ public abstract Vector3f get3();
+ public abstract void set(Vector3f v1, Vector3f v2, Vector3f v3);
+
+ public int collideWith(Collidable other, CollisionResults results){
+ return other.collideWith(this, results);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/math/ColorRGBA.java b/engine/src/core/com/jme3/math/ColorRGBA.java
new file mode 100644
index 0000000..a22d453
--- /dev/null
+++ b/engine/src/core/com/jme3/math/ColorRGBA.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+/**
+ * <code>ColorRGBA</code> defines a color made from a collection of red, green
+ * and blue values. An alpha value determines is transparency. All values must
+ * be between 0 and 1. If any value is set higher or lower than these
+ * constraints they are clamped to the min or max. That is, if a value smaller
+ * than zero is set the value clamps to zero. If a value higher than 1 is
+ * passed, that value is clamped to 1. However, because the attributes r, g, b,
+ * a are public for efficiency reasons, they can be directly modified with
+ * invalid values. The client should take care when directly addressing the
+ * values. A call to clamp will assure that the values are within the
+ * constraints.
+ *
+ * @author Mark Powell
+ * @version $Id: ColorRGBA.java,v 1.29 2007/09/09 18:25:14 irrisor Exp $
+ */
+public final class ColorRGBA implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+ /**
+ * the color black (0,0,0).
+ */
+ public static final ColorRGBA Black = new ColorRGBA(0f, 0f, 0f, 1f);
+ /**
+ * the color white (1,1,1).
+ */
+ public static final ColorRGBA White = new ColorRGBA(1f, 1f, 1f, 1f);
+ /**
+ * the color gray (.2,.2,.2).
+ */
+ public static final ColorRGBA DarkGray = new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f);
+ /**
+ * the color gray (.5,.5,.5).
+ */
+ public static final ColorRGBA Gray = new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f);
+ /**
+ * the color gray (.8,.8,.8).
+ */
+ public static final ColorRGBA LightGray = new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f);
+ /**
+ * the color red (1,0,0).
+ */
+ public static final ColorRGBA Red = new ColorRGBA(1f, 0f, 0f, 1f);
+ /**
+ * the color green (0,1,0).
+ */
+ public static final ColorRGBA Green = new ColorRGBA(0f, 1f, 0f, 1f);
+ /**
+ * the color blue (0,0,1).
+ */
+ public static final ColorRGBA Blue = new ColorRGBA(0f, 0f, 1f, 1f);
+ /**
+ * the color yellow (1,1,0).
+ */
+ public static final ColorRGBA Yellow = new ColorRGBA(1f, 1f, 0f, 1f);
+ /**
+ * the color magenta (1,0,1).
+ */
+ public static final ColorRGBA Magenta = new ColorRGBA(1f, 0f, 1f, 1f);
+ /**
+ * the color cyan (0,1,1).
+ */
+ public static final ColorRGBA Cyan = new ColorRGBA(0f, 1f, 1f, 1f);
+ /**
+ * the color orange (251/255, 130/255,0).
+ */
+ public static final ColorRGBA Orange = new ColorRGBA(251f / 255f, 130f / 255f, 0f, 1f);
+ /**
+ * the color brown (65/255, 40/255, 25/255).
+ */
+ public static final ColorRGBA Brown = new ColorRGBA(65f / 255f, 40f / 255f, 25f / 255f, 1f);
+ /**
+ * the color pink (1, 0.68, 0.68).
+ */
+ public static final ColorRGBA Pink = new ColorRGBA(1f, 0.68f, 0.68f, 1f);
+ /**
+ * the black color with no alpha (0, 0, 0, 0);
+ */
+ public static final ColorRGBA BlackNoAlpha = new ColorRGBA(0f, 0f, 0f, 0f);
+ /**
+ * The red component of the color.
+ */
+ public float r;
+ /**
+ * The green component of the color.
+ */
+ public float g;
+ /**
+ * the blue component of the color.
+ */
+ public float b;
+ /**
+ * the alpha component of the color. 0 is transparent and 1 is opaque
+ */
+ public float a;
+
+ /**
+ * Constructor instantiates a new <code>ColorRGBA</code> object. This
+ * color is the default "white" with all values 1.
+ *
+ */
+ public ColorRGBA() {
+ r = g = b = a = 1.0f;
+ }
+
+ /**
+ * Constructor instantiates a new <code>ColorRGBA</code> object. The
+ * values are defined as passed parameters. These values are then clamped
+ * to insure that they are between 0 and 1.
+ * @param r the red component of this color.
+ * @param g the green component of this color.
+ * @param b the blue component of this color.
+ * @param a the alpha component of this color.
+ */
+ public ColorRGBA(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ /**
+ * Copy constructor creates a new <code>ColorRGBA</code> object, based on
+ * a provided color.
+ * @param rgba the <code>ColorRGBA</code> object to copy.
+ */
+ public ColorRGBA(ColorRGBA rgba) {
+ this.a = rgba.a;
+ this.r = rgba.r;
+ this.g = rgba.g;
+ this.b = rgba.b;
+ }
+
+ /**
+ *
+ * <code>set</code> sets the RGBA values of this color. The values are then
+ * clamped to insure that they are between 0 and 1.
+ *
+ * @param r the red component of this color.
+ * @param g the green component of this color.
+ * @param b the blue component of this color.
+ * @param a the alpha component of this color.
+ * @return this
+ */
+ public ColorRGBA set(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ return this;
+ }
+
+ /**
+ * <code>set</code> sets the values of this color to those set by a parameter
+ * color.
+ *
+ * @param rgba ColorRGBA the color to set this color to.
+ * @return this
+ */
+ public ColorRGBA set(ColorRGBA rgba) {
+ if (rgba == null) {
+ r = 0;
+ g = 0;
+ b = 0;
+ a = 0;
+ } else {
+ r = rgba.r;
+ g = rgba.g;
+ b = rgba.b;
+ a = rgba.a;
+ }
+ return this;
+ }
+
+ /**
+ * <code>clamp</code> insures that all values are between 0 and 1. If any
+ * are less than 0 they are set to zero. If any are more than 1 they are
+ * set to one.
+ *
+ */
+ public void clamp() {
+ if (r < 0) {
+ r = 0;
+ } else if (r > 1) {
+ r = 1;
+ }
+
+ if (g < 0) {
+ g = 0;
+ } else if (g > 1) {
+ g = 1;
+ }
+
+ if (b < 0) {
+ b = 0;
+ } else if (b > 1) {
+ b = 1;
+ }
+
+ if (a < 0) {
+ a = 0;
+ } else if (a > 1) {
+ a = 1;
+ }
+ }
+
+ /**
+ *
+ * <code>getColorArray</code> retrieves the color values of this object as
+ * a four element float array.
+ * @return the float array that contains the color elements.
+ */
+ public float[] getColorArray() {
+ return new float[]{r, g, b, a};
+ }
+
+ /**
+ * Stores the current r/g/b/a values into the tempf array. The tempf array must have a
+ * length of 4 or greater, or an array index out of bounds exception will be thrown.
+ * @param store The array of floats to store the values into.
+ * @return The float[] after storage.
+ */
+ public float[] getColorArray(float[] store) {
+ store[0] = r;
+ store[1] = g;
+ store[2] = b;
+ store[3] = a;
+ return store;
+ }
+
+ public float getAlpha() {
+ return a;
+ }
+
+ public float getRed() {
+ return r;
+ }
+
+ public float getBlue() {
+ return b;
+ }
+
+ public float getGreen() {
+ return g;
+ }
+
+ /**
+ * Sets this color to the interpolation by changeAmnt from this to the finalColor
+ * this=(1-changeAmnt)*this + changeAmnt * finalColor
+ * @param finalColor The final color to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from this towards finalColor
+ */
+ public void interpolate(ColorRGBA finalColor, float changeAmnt) {
+ this.r = (1 - changeAmnt) * this.r + changeAmnt * finalColor.r;
+ this.g = (1 - changeAmnt) * this.g + changeAmnt * finalColor.g;
+ this.b = (1 - changeAmnt) * this.b + changeAmnt * finalColor.b;
+ this.a = (1 - changeAmnt) * this.a + changeAmnt * finalColor.a;
+ }
+
+ /**
+ * Sets this color to the interpolation by changeAmnt from beginColor to finalColor
+ * this=(1-changeAmnt)*beginColor + changeAmnt * finalColor
+ * @param beginColor The begining color (changeAmnt=0)
+ * @param finalColor The final color to interpolate towards (changeAmnt=1)
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginColor towards finalColor
+ */
+ public void interpolate(ColorRGBA beginColor, ColorRGBA finalColor, float changeAmnt) {
+ this.r = (1 - changeAmnt) * beginColor.r + changeAmnt * finalColor.r;
+ this.g = (1 - changeAmnt) * beginColor.g + changeAmnt * finalColor.g;
+ this.b = (1 - changeAmnt) * beginColor.b + changeAmnt * finalColor.b;
+ this.a = (1 - changeAmnt) * beginColor.a + changeAmnt * finalColor.a;
+ }
+
+ /**
+ *
+ * <code>randomColor</code> is a utility method that generates a random
+ * color.
+ *
+ * @return a random color.
+ */
+ public static ColorRGBA randomColor() {
+ ColorRGBA rVal = new ColorRGBA(0, 0, 0, 1);
+ rVal.r = FastMath.nextRandomFloat();
+ rVal.g = FastMath.nextRandomFloat();
+ rVal.b = FastMath.nextRandomFloat();
+ return rVal;
+ }
+
+ /**
+ * Multiplies each r/g/b/a of this color by the r/g/b/a of the given color and
+ * returns the result as a new ColorRGBA. Used as a way of combining colors and lights.
+ * @param c The color to multiply.
+ * @return The new ColorRGBA. this*c
+ */
+ public ColorRGBA mult(ColorRGBA c) {
+ return new ColorRGBA(c.r * r, c.g * g, c.b * b, c.a * a);
+ }
+
+ /**
+ * Multiplies each r/g/b/a of this color by the given scalar and
+ * returns the result as a new ColorRGBA. Used as a way of making colors dimmer
+ * or brighter..
+ * @param scalar The scalar to multiply.
+ * @return The new ColorRGBA. this*scalar
+ */
+ public ColorRGBA mult(float scalar) {
+ return new ColorRGBA(scalar * r, scalar * g, scalar * b, scalar * a);
+ }
+
+ /**
+ * Multiplies each r/g/b/a of this color by the r/g/b/a of the given color and
+ * returns the result as a new ColorRGBA. Used as a way of combining colors and lights.
+ * @param scalar scalar to multiply with
+ * @return The new ColorRGBA. this*c
+ */
+ public ColorRGBA multLocal(float scalar) {
+ this.r *= scalar;
+ this.g *= scalar;
+ this.b *= scalar;
+ this.a *= scalar;
+ return this;
+ }
+
+ /**
+ * Adds each r/g/b/a of this color by the r/g/b/a of the given color and
+ * returns the result as a new ColorRGBA.
+ * @param c The color to add.
+ * @return The new ColorRGBA. this+c
+ */
+ public ColorRGBA add(ColorRGBA c) {
+ return new ColorRGBA(c.r + r, c.g + g, c.b + b, c.a + a);
+ }
+
+ /**
+ * Multiplies each r/g/b/a of this color by the r/g/b/a of the given color and
+ * returns the result as a new ColorRGBA. Used as a way of combining colors and lights.
+ * @param c The color to multiply.
+ * @return The new ColorRGBA. this*c
+ */
+ public ColorRGBA addLocal(ColorRGBA c) {
+ set(c.r + r, c.g + g, c.b + b, c.a + a);
+ return this;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this color.
+ * The format of the string is:<br>
+ * <Class Name>: [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA]
+ * @return the string representation of this color.
+ */
+ public String toString() {
+ return "Color[" + r + ", " + g + ", " + b + ", " + a + "]";
+ }
+
+ @Override
+ public ColorRGBA clone() {
+ try {
+ return (ColorRGBA) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this ColorRGBA into the given float[] object.
+ *
+ * @param floats
+ * The float[] to take this ColorRGBA. If null, a new float[4] is
+ * created.
+ * @return The array, with R, G, B, A float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[4];
+ }
+ floats[0] = r;
+ floats[1] = g;
+ floats[2] = b;
+ floats[3] = a;
+ return floats;
+ }
+
+ /**
+ * <code>equals</code> returns true if this color is logically equivalent
+ * to a given color. That is, if the values of the two colors are the same.
+ * False is returned otherwise.
+ * @param o the object to compare againts.
+ * @return true if the colors are equal, false otherwise.
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof ColorRGBA)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ ColorRGBA comp = (ColorRGBA) o;
+ if (Float.compare(r, comp.r) != 0) {
+ return false;
+ }
+ if (Float.compare(g, comp.g) != 0) {
+ return false;
+ }
+ if (Float.compare(b, comp.b) != 0) {
+ return false;
+ }
+ if (Float.compare(a, comp.a) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * <code>hashCode</code> returns a unique code for this color object based
+ * on it's values. If two colors are logically equivalent, they will return
+ * the same hash code value.
+ * @return the hash code value of this color.
+ */
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(r);
+ hash += 37 * hash + Float.floatToIntBits(g);
+ hash += 37 * hash + Float.floatToIntBits(b);
+ hash += 37 * hash + Float.floatToIntBits(a);
+ return hash;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(r, "r", 0);
+ capsule.write(g, "g", 0);
+ capsule.write(b, "b", 0);
+ capsule.write(a, "a", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ r = capsule.readFloat("r", 0);
+ g = capsule.readFloat("g", 0);
+ b = capsule.readFloat("b", 0);
+ a = capsule.readFloat("a", 0);
+ }
+
+ public byte[] asBytesRGBA() {
+ byte[] store = new byte[4];
+ store[0] = (byte) ((int) (r * 255) & 0xFF);
+ store[1] = (byte) ((int) (g * 255) & 0xFF);
+ store[2] = (byte) ((int) (b * 255) & 0xFF);
+ store[3] = (byte) ((int) (a * 255) & 0xFF);
+ return store;
+ }
+
+ public int asIntARGB() {
+ int argb = (((int) (a * 255) & 0xFF) << 24)
+ | (((int) (r * 255) & 0xFF) << 16)
+ | (((int) (g * 255) & 0xFF) << 8)
+ | (((int) (b * 255) & 0xFF));
+ return argb;
+ }
+
+ public int asIntRGBA() {
+ int rgba = (((int) (r * 255) & 0xFF) << 24)
+ | (((int) (g * 255) & 0xFF) << 16)
+ | (((int) (b * 255) & 0xFF) << 8)
+ | (((int) (a * 255) & 0xFF));
+ return rgba;
+ }
+
+ public int asIntABGR() {
+ int abgr = (((int) (a * 255) & 0xFF) << 24)
+ | (((int) (b * 255) & 0xFF) << 16)
+ | (((int) (g * 255) & 0xFF) << 8)
+ | (((int) (r * 255) & 0xFF));
+ return abgr;
+ }
+
+ public void fromIntARGB(int color) {
+ a = ((byte) (color >> 24) & 0xFF) / 255f;
+ r = ((byte) (color >> 16) & 0xFF) / 255f;
+ g = ((byte) (color >> 8) & 0xFF) / 255f;
+ b = ((byte) (color) & 0xFF) / 255f;
+ }
+
+ public void fromIntRGBA(int color) {
+ r = ((byte) (color >> 24) & 0xFF) / 255f;
+ g = ((byte) (color >> 16) & 0xFF) / 255f;
+ b = ((byte) (color >> 8) & 0xFF) / 255f;
+ a = ((byte) (color) & 0xFF) / 255f;
+ }
+
+ /**
+ * Transform the current ColorRGBA to a Vector3f using
+ * x = r, y = g, z = b. The Alpha value is not used.
+ *
+ * This method is useful to use for shaders assignment.
+ * @return A Vector3f containing the RGB value of current color definition.
+ */
+ public Vector3f toVector3f() {
+ return new Vector3f(r, g, b);
+ }
+
+ /**
+ * Transform the current ColorRGBA to a Vector4f using
+ * x = r, y = g, z = b, w = a.
+ *
+ * This method is useful to use for shaders assignment.
+ * @return A Vector4f containing the RGBA value of current color definition.
+ */
+ public Vector4f toVector4f() {
+ return new Vector4f(r, g, b, a);
+ }
+}
diff --git a/engine/src/core/com/jme3/math/CurveAndSurfaceMath.java b/engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
new file mode 100644
index 0000000..079880f
--- /dev/null
+++ b/engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
@@ -0,0 +1,133 @@
+package com.jme3.math;
+
+import com.jme3.math.Spline.SplineType;
+import java.util.List;
+
+/**
+ * This class offers methods to help with curves and surfaces calculations.
+ * @author Marcin Roguski (Kealthas)
+ */
+public class CurveAndSurfaceMath {
+ private static final float KNOTS_MINIMUM_DELTA = 0.0001f;
+
+ /**
+ * A private constructor is defined to avoid instatiation of this class.
+ */
+ private CurveAndSurfaceMath() {}
+
+ /**
+ * This method interpolates tha data for the nurbs curve.
+ * @param u
+ * the u value
+ * @param nurbSpline
+ * the nurbs spline definition
+ * @param store
+ * the resulting point in 3D space
+ */
+ public static void interpolateNurbs(float u, Spline nurbSpline, Vector3f store) {
+ if (nurbSpline.getType() != SplineType.Nurb) {
+ throw new IllegalArgumentException("Given spline is not of a NURB type!");
+ }
+ List<Vector3f> controlPoints = nurbSpline.getControlPoints();
+ float[] weights = nurbSpline.getWeights();
+ List<Float> knots = nurbSpline.getKnots();
+ int controlPointAmount = controlPoints.size();
+
+ store.set(Vector3f.ZERO);
+ float delimeter = 0;
+ for (int i = 0; i < controlPointAmount; ++i) {
+ float val = weights[i] * CurveAndSurfaceMath.computeBaseFunctionValue(i, nurbSpline.getBasisFunctionDegree(), u, knots);
+ store.addLocal(nurbSpline.getControlPoints().get(i)
+ .mult(val));
+ delimeter += val;
+ }
+ store.divideLocal(delimeter);
+ }
+
+ /**
+ * This method interpolates tha data for the nurbs surface.
+ *
+ * @param u
+ * the u value
+ * @param v
+ * the v value
+ * @param controlPoints
+ * the nurbs' control points
+ * @param knots
+ * the nurbs' knots
+ * @param basisUFunctionDegree
+ * the degree of basis U function
+ * @param basisVFunctionDegree
+ * the degree of basis V function
+ * @param store
+ * the resulting point in 3D space
+ */
+ public static void interpolate(float u, float v, List<List<Vector4f>> controlPoints, List<Float>[] knots,
+ int basisUFunctionDegree, int basisVFunctionDegree, Vector3f store) {
+ store.set(Vector3f.ZERO);
+ float delimeter = 0;
+ int vControlPointsAmount = controlPoints.size();
+ int uControlPointsAmount = controlPoints.get(0).size();
+ for (int i = 0; i < vControlPointsAmount; ++i) {
+ for (int j = 0; j < uControlPointsAmount; ++j) {
+ Vector4f controlPoint = controlPoints.get(i).get(j);
+ float val = controlPoint.w
+ * CurveAndSurfaceMath.computeBaseFunctionValue(i, basisVFunctionDegree, v, knots[1])
+ * CurveAndSurfaceMath.computeBaseFunctionValue(j, basisUFunctionDegree, u, knots[0]);
+ store.addLocal(controlPoint.x * val, controlPoint.y * val, controlPoint.z * val);
+ delimeter += val;
+ }
+ }
+ store.divideLocal(delimeter);
+ }
+
+ /**
+ * This method prepares the knots to be used. If the knots represent non-uniform B-splines (first and last knot values are being
+ * repeated) it leads to NaN results during calculations. This method adds a small number to each of such knots to avoid NaN's.
+ * @param knots
+ * the knots to be prepared to use
+ * @param basisFunctionDegree
+ * the degree of basis function
+ */
+ // TODO: improve this; constant delta may lead to errors if the difference between tha last repeated
+ // point and the following one is lower than it
+ public static void prepareNurbsKnots(List<Float> knots, int basisFunctionDegree) {
+ float delta = KNOTS_MINIMUM_DELTA;
+ float prevValue = knots.get(0).floatValue();
+ for(int i=1;i<knots.size();++i) {
+ float value = knots.get(i).floatValue();
+ if(value<=prevValue) {
+ value += delta;
+ knots.set(i, Float.valueOf(value));
+ delta += KNOTS_MINIMUM_DELTA;
+ } else {
+ delta = KNOTS_MINIMUM_DELTA;//reset the delta's value
+ }
+
+ prevValue = value;
+ }
+ }
+
+ /**
+ * This method computes the base function value for the NURB curve.
+ * @param i
+ * the knot index
+ * @param k
+ * the base function degree
+ * @param t
+ * the knot value
+ * @param knots
+ * the knots' values
+ * @return the base function value
+ */
+ private static float computeBaseFunctionValue(int i, int k, float t, List<Float> knots) {
+ if (k == 1) {
+ return knots.get(i) <= t && t < knots.get(i + 1) ? 1.0f : 0.0f;
+ } else {
+ return (t - knots.get(i)) / (knots.get(i + k - 1) - knots.get(i)) *
+ CurveAndSurfaceMath.computeBaseFunctionValue(i, k - 1, t, knots)
+ + (knots.get(i + k) - t) / (knots.get(i + k) - knots.get(i + 1)) *
+ CurveAndSurfaceMath.computeBaseFunctionValue(i + 1, k - 1, t, knots);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Eigen3f.java b/engine/src/core/com/jme3/math/Eigen3f.java
new file mode 100644
index 0000000..e356057
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Eigen3f.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class Eigen3f implements java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Eigen3f.class
+ .getName());
+
+ float[] eigenValues = new float[3];
+ Vector3f[] eigenVectors = new Vector3f[3];
+
+ static final double ONE_THIRD_DOUBLE = 1.0 / 3.0;
+ static final double ROOT_THREE_DOUBLE = Math.sqrt(3.0);
+
+
+ public Eigen3f() {
+
+ }
+
+ public Eigen3f(Matrix3f data) {
+ calculateEigen(data);
+ }
+
+ public void calculateEigen(Matrix3f data) {
+ // prep work...
+ eigenVectors[0] = new Vector3f();
+ eigenVectors[1] = new Vector3f();
+ eigenVectors[2] = new Vector3f();
+
+ Matrix3f scaledData = new Matrix3f(data);
+ float maxMagnitude = scaleMatrix(scaledData);
+
+ // Compute the eigenvalues using double-precision arithmetic.
+ double roots[] = new double[3];
+ computeRoots(scaledData, roots);
+ eigenValues[0] = (float) roots[0];
+ eigenValues[1] = (float) roots[1];
+ eigenValues[2] = (float) roots[2];
+
+ float[] maxValues = new float[3];
+ Vector3f[] maxRows = new Vector3f[3];
+ maxRows[0] = new Vector3f();
+ maxRows[1] = new Vector3f();
+ maxRows[2] = new Vector3f();
+
+ for (int i = 0; i < 3; i++) {
+ Matrix3f tempMatrix = new Matrix3f(scaledData);
+ tempMatrix.m00 -= eigenValues[i];
+ tempMatrix.m11 -= eigenValues[i];
+ tempMatrix.m22 -= eigenValues[i];
+ float[] val = new float[1];
+ val[0] = maxValues[i];
+ if (!positiveRank(tempMatrix, val, maxRows[i])) {
+ // Rank was 0 (or very close to 0), so just return.
+ // Rescale back to the original size.
+ if (maxMagnitude > 1f) {
+ for (int j = 0; j < 3; j++) {
+ eigenValues[j] *= maxMagnitude;
+ }
+ }
+
+ eigenVectors[0].set(Vector3f.UNIT_X);
+ eigenVectors[1].set(Vector3f.UNIT_Y);
+ eigenVectors[2].set(Vector3f.UNIT_Z);
+ return;
+ }
+ maxValues[i] = val[0];
+ }
+
+ float maxCompare = maxValues[0];
+ int i = 0;
+ if (maxValues[1] > maxCompare) {
+ maxCompare = maxValues[1];
+ i = 1;
+ }
+ if (maxValues[2] > maxCompare) {
+ i = 2;
+ }
+
+ // use the largest row to compute and order the eigen vectors.
+ switch (i) {
+ case 0:
+ maxRows[0].normalizeLocal();
+ computeVectors(scaledData, maxRows[0], 1, 2, 0);
+ break;
+ case 1:
+ maxRows[1].normalizeLocal();
+ computeVectors(scaledData, maxRows[1], 2, 0, 1);
+ break;
+ case 2:
+ maxRows[2].normalizeLocal();
+ computeVectors(scaledData, maxRows[2], 0, 1, 2);
+ break;
+ }
+
+ // Rescale the values back to the original size.
+ if (maxMagnitude > 1f) {
+ for (i = 0; i < 3; i++) {
+ eigenValues[i] *= maxMagnitude;
+ }
+ }
+ }
+
+ /**
+ * Scale the matrix so its entries are in [-1,1]. The scaling is applied
+ * only when at least one matrix entry has magnitude larger than 1.
+ *
+ * @return the max magnitude in this matrix
+ */
+ private float scaleMatrix(Matrix3f mat) {
+
+ float max = FastMath.abs(mat.m00);
+ float abs = FastMath.abs(mat.m01);
+
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m02);
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m11);
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m12);
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m22);
+ if (abs > max) {
+ max = abs;
+ }
+
+ if (max > 1f) {
+ float fInvMax = 1f / max;
+ mat.multLocal(fInvMax);
+ }
+
+ return max;
+ }
+
+ /**
+ * Compute the eigenvectors of the given Matrix, using the
+ * @param mat
+ * @param vect
+ * @param index1
+ * @param index2
+ * @param index3
+ */
+ private void computeVectors(Matrix3f mat, Vector3f vect, int index1,
+ int index2, int index3) {
+ Vector3f vectorU = new Vector3f(), vectorV = new Vector3f();
+ Vector3f.generateComplementBasis(vectorU, vectorV, vect);
+
+ Vector3f tempVect = mat.mult(vectorU);
+ float p00 = eigenValues[index3] - vectorU.dot(tempVect);
+ float p01 = vectorV.dot(tempVect);
+ float p11 = eigenValues[index3] - vectorV.dot(mat.mult(vectorV));
+ float invLength;
+ float max = FastMath.abs(p00);
+ int row = 0;
+ float fAbs = FastMath.abs(p01);
+ if (fAbs > max) {
+ max = fAbs;
+ }
+ fAbs = FastMath.abs(p11);
+ if (fAbs > max) {
+ max = fAbs;
+ row = 1;
+ }
+
+ if (max >= FastMath.ZERO_TOLERANCE) {
+ if (row == 0) {
+ invLength = FastMath.invSqrt(p00 * p00 + p01 * p01);
+ p00 *= invLength;
+ p01 *= invLength;
+ vectorU.mult(p01, eigenVectors[index3])
+ .addLocal(vectorV.mult(p00));
+ } else {
+ invLength = FastMath.invSqrt(p11 * p11 + p01 * p01);
+ p11 *= invLength;
+ p01 *= invLength;
+ vectorU.mult(p11, eigenVectors[index3])
+ .addLocal(vectorV.mult(p01));
+ }
+ } else {
+ if (row == 0) {
+ eigenVectors[index3] = vectorV;
+ } else {
+ eigenVectors[index3] = vectorU;
+ }
+ }
+
+ Vector3f vectorS = vect.cross(eigenVectors[index3]);
+ mat.mult(vect, tempVect);
+ p00 = eigenValues[index1] - vect.dot(tempVect);
+ p01 = vectorS.dot(tempVect);
+ p11 = eigenValues[index1] - vectorS.dot(mat.mult(vectorS));
+ max = FastMath.abs(p00);
+ row = 0;
+ fAbs = FastMath.abs(p01);
+ if (fAbs > max) {
+ max = fAbs;
+ }
+ fAbs = FastMath.abs(p11);
+ if (fAbs > max) {
+ max = fAbs;
+ row = 1;
+ }
+
+ if (max >= FastMath.ZERO_TOLERANCE) {
+ if (row == 0) {
+ invLength = FastMath.invSqrt(p00 * p00 + p01 * p01);
+ p00 *= invLength;
+ p01 *= invLength;
+ eigenVectors[index1] = vect.mult(p01).add(vectorS.mult(p00));
+ } else {
+ invLength = FastMath.invSqrt(p11 * p11 + p01 * p01);
+ p11 *= invLength;
+ p01 *= invLength;
+ eigenVectors[index1] = vect.mult(p11).add(vectorS.mult(p01));
+ }
+ } else {
+ if (row == 0) {
+ eigenVectors[index1].set(vectorS);
+ } else {
+ eigenVectors[index1].set(vect);
+ }
+ }
+
+ eigenVectors[index3].cross(eigenVectors[index1], eigenVectors[index2]);
+ }
+
+ /**
+ * Check the rank of the given Matrix to determine if it is positive. While
+ * doing so, store the max magnitude entry in the given float store and the
+ * max row of the matrix in the Vector store.
+ *
+ * @param matrix
+ * the Matrix3f to analyze.
+ * @param maxMagnitudeStore
+ * a float array in which to store (in the 0th position) the max
+ * magnitude entry of the matrix.
+ * @param maxRowStore
+ * a Vector3f to store the values of the row of the matrix
+ * containing the max magnitude entry.
+ * @return true if the given matrix has a non 0 rank.
+ */
+ private boolean positiveRank(Matrix3f matrix, float[] maxMagnitudeStore, Vector3f maxRowStore) {
+ // Locate the maximum-magnitude entry of the matrix.
+ maxMagnitudeStore[0] = -1f;
+ int iRow, iCol, iMaxRow = -1;
+ for (iRow = 0; iRow < 3; iRow++) {
+ for (iCol = iRow; iCol < 3; iCol++) {
+ float fAbs = FastMath.abs(matrix.get(iRow, iCol));
+ if (fAbs > maxMagnitudeStore[0]) {
+ maxMagnitudeStore[0] = fAbs;
+ iMaxRow = iRow;
+ }
+ }
+ }
+
+ // Return the row containing the maximum, to be used for eigenvector
+ // construction.
+ maxRowStore.set(matrix.getRow(iMaxRow));
+
+ return maxMagnitudeStore[0] >= FastMath.ZERO_TOLERANCE;
+ }
+
+ /**
+ * Generate the base eigen values of the given matrix using double precision
+ * math.
+ *
+ * @param mat
+ * the Matrix3f to analyze.
+ * @param rootsStore
+ * a double array to store the results in. Must be at least
+ * length 3.
+ */
+ private void computeRoots(Matrix3f mat, double[] rootsStore) {
+ // Convert the unique matrix entries to double precision.
+ double a = mat.m00, b = mat.m01, c = mat.m02,
+ d = mat.m11, e = mat.m12,
+ f = mat.m22;
+
+ // The characteristic equation is x^3 - c2*x^2 + c1*x - c0 = 0. The
+ // eigenvalues are the roots to this equation, all guaranteed to be
+ // real-valued, because the matrix is symmetric.
+ double char0 = a * d * f + 2.0 * b * c * e - a
+ * e * e - d * c * c - f * b * b;
+
+ double char1 = a * d - b * b + a * f - c * c
+ + d * f - e * e;
+
+ double char2 = a + d + f;
+
+ // Construct the parameters used in classifying the roots of the
+ // equation and in solving the equation for the roots in closed form.
+ double char2Div3 = char2 * ONE_THIRD_DOUBLE;
+ double abcDiv3 = (char1 - char2 * char2Div3) * ONE_THIRD_DOUBLE;
+ if (abcDiv3 > 0.0) {
+ abcDiv3 = 0.0;
+ }
+
+ double mbDiv2 = 0.5 * (char0 + char2Div3 * (2.0 * char2Div3 * char2Div3 - char1));
+
+ double q = mbDiv2 * mbDiv2 + abcDiv3 * abcDiv3 * abcDiv3;
+ if (q > 0.0) {
+ q = 0.0;
+ }
+
+ // Compute the eigenvalues by solving for the roots of the polynomial.
+ double magnitude = Math.sqrt(-abcDiv3);
+ double angle = Math.atan2(Math.sqrt(-q), mbDiv2) * ONE_THIRD_DOUBLE;
+ double cos = Math.cos(angle);
+ double sin = Math.sin(angle);
+ double root0 = char2Div3 + 2.0 * magnitude * cos;
+ double root1 = char2Div3 - magnitude
+ * (cos + ROOT_THREE_DOUBLE * sin);
+ double root2 = char2Div3 - magnitude
+ * (cos - ROOT_THREE_DOUBLE * sin);
+
+ // Sort in increasing order.
+ if (root1 >= root0) {
+ rootsStore[0] = root0;
+ rootsStore[1] = root1;
+ } else {
+ rootsStore[0] = root1;
+ rootsStore[1] = root0;
+ }
+
+ if (root2 >= rootsStore[1]) {
+ rootsStore[2] = root2;
+ } else {
+ rootsStore[2] = rootsStore[1];
+ if (root2 >= rootsStore[0]) {
+ rootsStore[1] = root2;
+ } else {
+ rootsStore[1] = rootsStore[0];
+ rootsStore[0] = root2;
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ Matrix3f mat = new Matrix3f(2, 1, 1, 1, 2, 1, 1, 1, 2);
+ Eigen3f eigenSystem = new Eigen3f(mat);
+
+ logger.info("eigenvalues = ");
+ for (int i = 0; i < 3; i++)
+ logger.log(Level.INFO, "{0} ", eigenSystem.getEigenValue(i));
+
+ logger.info("eigenvectors = ");
+ for (int i = 0; i < 3; i++) {
+ Vector3f vector = eigenSystem.getEigenVector(i);
+ logger.info(vector.toString());
+ mat.setColumn(i, vector);
+ }
+ logger.info(mat.toString());
+
+ // eigenvalues =
+ // 1.000000 1.000000 4.000000
+ // eigenvectors =
+ // 0.411953 0.704955 0.577350
+ // 0.404533 -0.709239 0.577350
+ // -0.816485 0.004284 0.577350
+ }
+
+ public float getEigenValue(int i) {
+ return eigenValues[i];
+ }
+
+ public Vector3f getEigenVector(int i) {
+ return eigenVectors[i];
+ }
+
+ public float[] getEigenValues() {
+ return eigenValues;
+ }
+
+ public Vector3f[] getEigenVectors() {
+ return eigenVectors;
+ }
+}
diff --git a/engine/src/core/com/jme3/math/FastMath.java b/engine/src/core/com/jme3/math/FastMath.java
new file mode 100644
index 0000000..6043d57
--- /dev/null
+++ b/engine/src/core/com/jme3/math/FastMath.java
@@ -0,0 +1,987 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import java.util.Random;
+
+/**
+ * <code>FastMath</code> provides 'fast' math approximations and float equivalents of Math
+ * functions. These are all used as static values and functions.
+ *
+ * @author Various
+ * @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $
+ */
+final public class FastMath {
+
+ private FastMath() {
+ }
+ /** A "close to zero" double epsilon value for use*/
+ public static final double DBL_EPSILON = 2.220446049250313E-16d;
+ /** A "close to zero" float epsilon value for use*/
+ public static final float FLT_EPSILON = 1.1920928955078125E-7f;
+ /** A "close to zero" float epsilon value for use*/
+ public static final float ZERO_TOLERANCE = 0.0001f;
+ public static final float ONE_THIRD = 1f / 3f;
+ /** The value PI as a float. (180 degrees) */
+ public static final float PI = (float) Math.PI;
+ /** The value 2PI as a float. (360 degrees) */
+ public static final float TWO_PI = 2.0f * PI;
+ /** The value PI/2 as a float. (90 degrees) */
+ public static final float HALF_PI = 0.5f * PI;
+ /** The value PI/4 as a float. (45 degrees) */
+ public static final float QUARTER_PI = 0.25f * PI;
+ /** The value 1/PI as a float. */
+ public static final float INV_PI = 1.0f / PI;
+ /** The value 1/(2PI) as a float. */
+ public static final float INV_TWO_PI = 1.0f / TWO_PI;
+ /** A value to multiply a degree value by, to convert it to radians. */
+ public static final float DEG_TO_RAD = PI / 180.0f;
+ /** A value to multiply a radian value by, to convert it to degrees. */
+ public static final float RAD_TO_DEG = 180.0f / PI;
+ /** A precreated random object for random numbers. */
+ public static final Random rand = new Random(System.currentTimeMillis());
+
+ /**
+ * Returns true if the number is a power of 2 (2,4,8,16...)
+ *
+ * A good implementation found on the Java boards. note: a number is a power
+ * of two if and only if it is the smallest number with that number of
+ * significant bits. Therefore, if you subtract 1, you know that the new
+ * number will have fewer bits, so ANDing the original number with anything
+ * less than it will give 0.
+ *
+ * @param number
+ * The number to test.
+ * @return True if it is a power of two.
+ */
+ public static boolean isPowerOfTwo(int number) {
+ return (number > 0) && (number & (number - 1)) == 0;
+ }
+
+ public static int nearestPowerOfTwo(int number) {
+ return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2)));
+ }
+
+ /**
+ * Linear interpolation from startValue to endValue by the given percent.
+ * Basically: ((1 - percent) * startValue) + (percent * endValue)
+ *
+ * @param scale
+ * scale value to use. if 1, use endValue, if 0, use startValue.
+ * @param startValue
+ * Begining value. 0% of f
+ * @param endValue
+ * ending value. 100% of f
+ * @return The interpolated value between startValue and endValue.
+ */
+ public static float interpolateLinear(float scale, float startValue, float endValue) {
+ if (startValue == endValue) {
+ return startValue;
+ }
+ if (scale <= 0f) {
+ return startValue;
+ }
+ if (scale >= 1f) {
+ return endValue;
+ }
+ return ((1f - scale) * startValue) + (scale * endValue);
+ }
+
+ /**
+ * Linear interpolation from startValue to endValue by the given percent.
+ * Basically: ((1 - percent) * startValue) + (percent * endValue)
+ *
+ * @param scale
+ * scale value to use. if 1, use endValue, if 0, use startValue.
+ * @param startValue
+ * Begining value. 0% of f
+ * @param endValue
+ * ending value. 100% of f
+ * @param store a vector3f to store the result
+ * @return The interpolated value between startValue and endValue.
+ */
+ public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.x = interpolateLinear(scale, startValue.x, endValue.x);
+ store.y = interpolateLinear(scale, startValue.y, endValue.y);
+ store.z = interpolateLinear(scale, startValue.z, endValue.z);
+ return store;
+ }
+
+ /**
+ * Linear interpolation from startValue to endValue by the given percent.
+ * Basically: ((1 - percent) * startValue) + (percent * endValue)
+ *
+ * @param scale
+ * scale value to use. if 1, use endValue, if 0, use startValue.
+ * @param startValue
+ * Begining value. 0% of f
+ * @param endValue
+ * ending value. 100% of f
+ * @return The interpolated value between startValue and endValue.
+ */
+ public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
+ return interpolateLinear(scale, startValue, endValue, null);
+ }
+
+ /**
+ * Linear extrapolation from startValue to endValue by the given scale.
+ * if scale is between 0 and 1 this method returns the same result as interpolateLinear
+ * if the scale is over 1 the value is linearly extrapolated.
+ * Note that the end value is the value for a scale of 1.
+ * @param scale the scale for extrapolation
+ * @param startValue the starting value (scale = 0)
+ * @param endValue the end value (scale = 1)
+ * @return an extrapolation for the given parameters
+ */
+ public static float extrapolateLinear(float scale, float startValue, float endValue) {
+// if (scale <= 0f) {
+// return startValue;
+// }
+ return ((1f - scale) * startValue) + (scale * endValue);
+ }
+
+ /**
+ * Linear extrapolation from startValue to endValue by the given scale.
+ * if scale is between 0 and 1 this method returns the same result as interpolateLinear
+ * if the scale is over 1 the value is linearly extrapolated.
+ * Note that the end value is the value for a scale of 1.
+ * @param scale the scale for extrapolation
+ * @param startValue the starting value (scale = 0)
+ * @param endValue the end value (scale = 1)
+ * @param store an initialized vector to store the return value
+ * @return an extrapolation for the given parameters
+ */
+ public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+// if (scale <= 1f) {
+// return interpolateLinear(scale, startValue, endValue, store);
+// }
+ store.x = extrapolateLinear(scale, startValue.x, endValue.x);
+ store.y = extrapolateLinear(scale, startValue.y, endValue.y);
+ store.z = extrapolateLinear(scale, startValue.z, endValue.z);
+ return store;
+ }
+
+ /**
+ * Linear extrapolation from startValue to endValue by the given scale.
+ * if scale is between 0 and 1 this method returns the same result as interpolateLinear
+ * if the scale is over 1 the value is linearly extrapolated.
+ * Note that the end value is the value for a scale of 1.
+ * @param scale the scale for extrapolation
+ * @param startValue the starting value (scale = 0)
+ * @param endValue the end value (scale = 1)
+ * @return an extrapolation for the given parameters
+ */
+ public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
+ return extrapolateLinear(scale, startValue, endValue, null);
+ }
+
+ /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
+ * here is the interpolation matrix
+ * m = [ 0.0 1.0 0.0 0.0 ]
+ * [-T 0.0 T 0.0 ]
+ * [ 2T T-3 3-2T -T ]
+ * [-T 2-T T-2 T ]
+ * where T is the curve tension
+ * the result is a value between p1 and p2, t=0 for p1, t=1 for p2
+ * @param u value from 0 to 1
+ * @param T The tension of the curve
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return catmull-Rom interpolation
+ */
+ public static float interpolateCatmullRom(float u, float T, float p0, float p1, float p2, float p3) {
+ float c1, c2, c3, c4;
+ c1 = p1;
+ c2 = -1.0f * T * p0 + T * p2;
+ c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3;
+ c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3;
+
+ return (float) (((c4 * u + c3) * u + c2) * u + c1);
+ }
+
+ /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
+ * here is the interpolation matrix
+ * m = [ 0.0 1.0 0.0 0.0 ]
+ * [-T 0.0 T 0.0 ]
+ * [ 2T T-3 3-2T -T ]
+ * [-T 2-T T-2 T ]
+ * where T is the tension of the curve
+ * the result is a value between p1 and p2, t=0 for p1, t=1 for p2
+ * @param u value from 0 to 1
+ * @param T The tension of the curve
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @param store a Vector3f to store the result
+ * @return catmull-Rom interpolation
+ */
+ public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.x = interpolateCatmullRom(u, T, p0.x, p1.x, p2.x, p3.x);
+ store.y = interpolateCatmullRom(u, T, p0.y, p1.y, p2.y, p3.y);
+ store.z = interpolateCatmullRom(u, T, p0.z, p1.z, p2.z, p3.z);
+ return store;
+ }
+
+ /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
+ * here is the interpolation matrix
+ * m = [ 0.0 1.0 0.0 0.0 ]
+ * [-T 0.0 T 0.0 ]
+ * [ 2T T-3 3-2T -T ]
+ * [-T 2-T T-2 T ]
+ * where T is the tension of the curve
+ * the result is a value between p1 and p2, t=0 for p1, t=1 for p2
+ * @param u value from 0 to 1
+ * @param T The tension of the curve
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return catmull-Rom interpolation
+ */
+ public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
+ return interpolateCatmullRom(u, T, p0, p1, p2, p3, null);
+ }
+
+ /**Interpolate a spline between at least 4 control points following the Bezier equation.
+ * here is the interpolation matrix
+ * m = [ -1.0 3.0 -3.0 1.0 ]
+ * [ 3.0 -6.0 3.0 0.0 ]
+ * [ -3.0 3.0 0.0 0.0 ]
+ * [ 1.0 0.0 0.0 0.0 ]
+ * where T is the curve tension
+ * the result is a value between p1 and p3, t=0 for p1, t=1 for p3
+ * @param u value from 0 to 1
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return Bezier interpolation
+ */
+ public static float interpolateBezier(float u, float p0, float p1, float p2, float p3) {
+ float oneMinusU = 1.0f - u;
+ float oneMinusU2 = oneMinusU * oneMinusU;
+ float u2 = u * u;
+ return p0 * oneMinusU2 * oneMinusU
+ + 3.0f * p1 * u * oneMinusU2
+ + 3.0f * p2 * u2 * oneMinusU
+ + p3 * u2 * u;
+ }
+
+ /**Interpolate a spline between at least 4 control points following the Bezier equation.
+ * here is the interpolation matrix
+ * m = [ -1.0 3.0 -3.0 1.0 ]
+ * [ 3.0 -6.0 3.0 0.0 ]
+ * [ -3.0 3.0 0.0 0.0 ]
+ * [ 1.0 0.0 0.0 0.0 ]
+ * where T is the tension of the curve
+ * the result is a value between p1 and p3, t=0 for p1, t=1 for p3
+ * @param u value from 0 to 1
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @param store a Vector3f to store the result
+ * @return Bezier interpolation
+ */
+ public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.x = interpolateBezier(u, p0.x, p1.x, p2.x, p3.x);
+ store.y = interpolateBezier(u, p0.y, p1.y, p2.y, p3.y);
+ store.z = interpolateBezier(u, p0.z, p1.z, p2.z, p3.z);
+ return store;
+ }
+
+ /**Interpolate a spline between at least 4 control points following the Bezier equation.
+ * here is the interpolation matrix
+ * m = [ -1.0 3.0 -3.0 1.0 ]
+ * [ 3.0 -6.0 3.0 0.0 ]
+ * [ -3.0 3.0 0.0 0.0 ]
+ * [ 1.0 0.0 0.0 0.0 ]
+ * where T is the tension of the curve
+ * the result is a value between p1 and p3, t=0 for p1, t=1 for p3
+ * @param u value from 0 to 1
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return Bezier interpolation
+ */
+ public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
+ return interpolateBezier(u, p0, p1, p2, p3, null);
+ }
+
+ /**
+ * Compute the lenght on a catmull rom spline between control point 1 and 2
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @param startRange the starting range on the segment (use 0)
+ * @param endRange the end range on the segment (use 1)
+ * @param curveTension the curve tension
+ * @return the length of the segment
+ */
+ public static float getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange, float curveTension) {
+
+ float epsilon = 0.001f;
+ float middleValue = (startRange + endRange) * 0.5f;
+ Vector3f start = p1.clone();
+ if (startRange != 0) {
+ FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3, start);
+ }
+ Vector3f end = p2.clone();
+ if (endRange != 1) {
+ FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3, end);
+ }
+ Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3);
+ float l = end.subtract(start).length();
+ float l1 = middle.subtract(start).length();
+ float l2 = end.subtract(middle).length();
+ float len = l1 + l2;
+ if (l + epsilon < len) {
+ l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue, curveTension);
+ l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange, curveTension);
+ }
+ l = l1 + l2;
+ return l;
+ }
+
+ /**
+ * Compute the lenght on a bezier spline between control point 1 and 2
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return the length of the segment
+ */
+ public static float getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
+ float delta = 0.02f, t = 0.0f, result = 0.0f;
+ Vector3f v1 = p0.clone(), v2 = new Vector3f();
+ while (t <= 1.0f) {
+ FastMath.interpolateBezier(t, p0, p1, p2, p3, v2);
+ result += v1.subtractLocal(v2).length();
+ v1.set(v2);
+ t += delta;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the arc cosine of an angle given in radians.<br>
+ * Special cases:
+ * <ul><li>If fValue is smaller than -1, then the result is PI.
+ * <li>If the argument is greater than 1, then the result is 0.</ul>
+ * @param fValue The angle, in radians.
+ * @return fValue's acos
+ * @see java.lang.Math#acos(double)
+ */
+ public static float acos(float fValue) {
+ if (-1.0f < fValue) {
+ if (fValue < 1.0f) {
+ return (float) Math.acos(fValue);
+ }
+
+ return 0.0f;
+ }
+
+ return PI;
+ }
+
+ /**
+ * Returns the arc sine of an angle given in radians.<br>
+ * Special cases:
+ * <ul><li>If fValue is smaller than -1, then the result is -HALF_PI.
+ * <li>If the argument is greater than 1, then the result is HALF_PI.</ul>
+ * @param fValue The angle, in radians.
+ * @return fValue's asin
+ * @see java.lang.Math#asin(double)
+ */
+ public static float asin(float fValue) {
+ if (-1.0f < fValue) {
+ if (fValue < 1.0f) {
+ return (float) Math.asin(fValue);
+ }
+
+ return HALF_PI;
+ }
+
+ return -HALF_PI;
+ }
+
+ /**
+ * Returns the arc tangent of an angle given in radians.<br>
+ * @param fValue The angle, in radians.
+ * @return fValue's atan
+ * @see java.lang.Math#atan(double)
+ */
+ public static float atan(float fValue) {
+ return (float) Math.atan(fValue);
+ }
+
+ /**
+ * A direct call to Math.atan2.
+ * @param fY
+ * @param fX
+ * @return Math.atan2(fY,fX)
+ * @see java.lang.Math#atan2(double, double)
+ */
+ public static float atan2(float fY, float fX) {
+ return (float) Math.atan2(fY, fX);
+ }
+
+ /**
+ * Rounds a fValue up. A call to Math.ceil
+ * @param fValue The value.
+ * @return The fValue rounded up
+ * @see java.lang.Math#ceil(double)
+ */
+ public static float ceil(float fValue) {
+ return (float) Math.ceil(fValue);
+ }
+
+ /**
+ * Fast Trig functions for x86. This forces the trig functiosn to stay
+ * within the safe area on the x86 processor (-45 degrees to +45 degrees)
+ * The results may be very slightly off from what the Math and StrictMath
+ * trig functions give due to rounding in the angle reduction but it will be
+ * very very close.
+ *
+ * note: code from wiki posting on java.net by jeffpk
+ */
+ public static float reduceSinAngle(float radians) {
+ radians %= TWO_PI; // put us in -2PI to +2PI space
+ if (Math.abs(radians) > PI) { // put us in -PI to +PI space
+ radians = radians - (TWO_PI);
+ }
+ if (Math.abs(radians) > HALF_PI) {// put us in -PI/2 to +PI/2 space
+ radians = PI - radians;
+ }
+
+ return radians;
+ }
+
+ /**
+ * Returns sine of a value.
+ *
+ * note: code from wiki posting on java.net by jeffpk
+ *
+ * @param fValue
+ * The value to sine, in radians.
+ * @return The sine of fValue.
+ * @see java.lang.Math#sin(double)
+ */
+ public static float sin2(float fValue) {
+ fValue = reduceSinAngle(fValue); // limits angle to between -PI/2 and +PI/2
+ if (Math.abs(fValue) <= Math.PI / 4) {
+ return (float) Math.sin(fValue);
+ }
+
+ return (float) Math.cos(Math.PI / 2 - fValue);
+ }
+
+ /**
+ * Returns cos of a value.
+ *
+ * @param fValue
+ * The value to cosine, in radians.
+ * @return The cosine of fValue.
+ * @see java.lang.Math#cos(double)
+ */
+ public static float cos2(float fValue) {
+ return sin2(fValue + HALF_PI);
+ }
+
+ public static float cos(float v) {
+ return (float) Math.cos(v);
+ }
+
+ public static float sin(float v) {
+ return (float) Math.sin(v);
+ }
+
+ /**
+ * Returns E^fValue
+ * @param fValue Value to raise to a power.
+ * @return The value E^fValue
+ * @see java.lang.Math#exp(double)
+ */
+ public static float exp(float fValue) {
+ return (float) Math.exp(fValue);
+ }
+
+ /**
+ * Returns Absolute value of a float.
+ * @param fValue The value to abs.
+ * @return The abs of the value.
+ * @see java.lang.Math#abs(float)
+ */
+ public static float abs(float fValue) {
+ if (fValue < 0) {
+ return -fValue;
+ }
+ return fValue;
+ }
+
+ /**
+ * Returns a number rounded down.
+ * @param fValue The value to round
+ * @return The given number rounded down
+ * @see java.lang.Math#floor(double)
+ */
+ public static float floor(float fValue) {
+ return (float) Math.floor(fValue);
+ }
+
+ /**
+ * Returns 1/sqrt(fValue)
+ * @param fValue The value to process.
+ * @return 1/sqrt(fValue)
+ * @see java.lang.Math#sqrt(double)
+ */
+ public static float invSqrt(float fValue) {
+ return (float) (1.0f / Math.sqrt(fValue));
+ }
+
+ public static float fastInvSqrt(float x) {
+ float xhalf = 0.5f * x;
+ int i = Float.floatToIntBits(x); // get bits for floating value
+ i = 0x5f375a86 - (i >> 1); // gives initial guess y0
+ x = Float.intBitsToFloat(i); // convert bits back to float
+ x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
+ return x;
+ }
+
+ /**
+ * Returns the log base E of a value.
+ * @param fValue The value to log.
+ * @return The log of fValue base E
+ * @see java.lang.Math#log(double)
+ */
+ public static float log(float fValue) {
+ return (float) Math.log(fValue);
+ }
+
+ /**
+ * Returns the logarithm of value with given base, calculated as log(value)/log(base),
+ * so that pow(base, return)==value (contributed by vear)
+ * @param value The value to log.
+ * @param base Base of logarithm.
+ * @return The logarithm of value with given base
+ */
+ public static float log(float value, float base) {
+ return (float) (Math.log(value) / Math.log(base));
+ }
+
+ /**
+ * Returns a number raised to an exponent power. fBase^fExponent
+ * @param fBase The base value (IE 2)
+ * @param fExponent The exponent value (IE 3)
+ * @return base raised to exponent (IE 8)
+ * @see java.lang.Math#pow(double, double)
+ */
+ public static float pow(float fBase, float fExponent) {
+ return (float) Math.pow(fBase, fExponent);
+ }
+
+ /**
+ * Returns the value squared. fValue ^ 2
+ * @param fValue The vaule to square.
+ * @return The square of the given value.
+ */
+ public static float sqr(float fValue) {
+ return fValue * fValue;
+ }
+
+ /**
+ * Returns the square root of a given value.
+ * @param fValue The value to sqrt.
+ * @return The square root of the given value.
+ * @see java.lang.Math#sqrt(double)
+ */
+ public static float sqrt(float fValue) {
+ return (float) Math.sqrt(fValue);
+ }
+
+ /**
+ * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an approximate value
+ * is returned. Otherwise, a direct value is used.
+ * @param fValue The value to tangent, in radians.
+ * @return The tangent of fValue.
+ * @see java.lang.Math#tan(double)
+ */
+ public static float tan(float fValue) {
+ return (float) Math.tan(fValue);
+ }
+
+ /**
+ * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise
+ * @param iValue The integer to examine.
+ * @return The integer's sign.
+ */
+ public static int sign(int iValue) {
+ if (iValue > 0) {
+ return 1;
+ }
+ if (iValue < 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise
+ * @param fValue The float to examine.
+ * @return The float's sign.
+ */
+ public static float sign(float fValue) {
+ return Math.signum(fValue);
+ }
+
+ /**
+ * Given 3 points in a 2d plane, this function computes if the points going from A-B-C
+ * are moving counter clock wise.
+ * @param p0 Point 0.
+ * @param p1 Point 1.
+ * @param p2 Point 2.
+ * @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0 and p1.
+ */
+ public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) {
+ float dx1, dx2, dy1, dy2;
+ dx1 = p1.x - p0.x;
+ dy1 = p1.y - p0.y;
+ dx2 = p2.x - p0.x;
+ dy2 = p2.y - p0.y;
+ if (dx1 * dy2 > dy1 * dx2) {
+ return 1;
+ }
+ if (dx1 * dy2 < dy1 * dx2) {
+ return -1;
+ }
+ if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) {
+ return -1;
+ }
+ if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Test if a point is inside a triangle. 1 if the point is on the ccw side,
+ * -1 if the point is on the cw side, and 0 if it is on neither.
+ * @param t0 First point of the triangle.
+ * @param t1 Second point of the triangle.
+ * @param t2 Third point of the triangle.
+ * @param p The point to test.
+ * @return Value 1 or -1 if inside triangle, 0 otherwise.
+ */
+ public static int pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) {
+ int val1 = counterClockwise(t0, t1, p);
+ if (val1 == 0) {
+ return 1;
+ }
+ int val2 = counterClockwise(t1, t2, p);
+ if (val2 == 0) {
+ return 1;
+ }
+ if (val2 != val1) {
+ return 0;
+ }
+ int val3 = counterClockwise(t2, t0, p);
+ if (val3 == 0) {
+ return 1;
+ }
+ if (val3 != val1) {
+ return 0;
+ }
+ return val3;
+ }
+
+ /**
+ * A method that computes normal for a triangle defined by three vertices.
+ * @param v1 first vertex
+ * @param v2 second vertex
+ * @param v3 third vertex
+ * @return a normal for the face
+ */
+ public static Vector3f computeNormal(Vector3f v1, Vector3f v2, Vector3f v3) {
+ Vector3f a1 = v1.subtract(v2);
+ Vector3f a2 = v3.subtract(v2);
+ return a2.crossLocal(a1).normalizeLocal();
+ }
+
+ /**
+ * Returns the determinant of a 4x4 matrix.
+ */
+ public static float determinant(double m00, double m01, double m02,
+ double m03, double m10, double m11, double m12, double m13,
+ double m20, double m21, double m22, double m23, double m30,
+ double m31, double m32, double m33) {
+
+ double det01 = m20 * m31 - m21 * m30;
+ double det02 = m20 * m32 - m22 * m30;
+ double det03 = m20 * m33 - m23 * m30;
+ double det12 = m21 * m32 - m22 * m31;
+ double det13 = m21 * m33 - m23 * m31;
+ double det23 = m22 * m33 - m23 * m32;
+ return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12) - m01
+ * (m10 * det23 - m12 * det03 + m13 * det02) + m02
+ * (m10 * det13 - m11 * det03 + m13 * det01) - m03
+ * (m10 * det12 - m11 * det02 + m12 * det01));
+ }
+
+ /**
+ * Returns a random float between 0 and 1.
+ *
+ * @return A random float between <tt>0.0f</tt> (inclusive) to
+ * <tt>1.0f</tt> (exclusive).
+ */
+ public static float nextRandomFloat() {
+ return rand.nextFloat();
+ }
+
+ /**
+ * Returns a random float between min and max.
+ *
+ * @return A random int between <tt>min</tt> (inclusive) to
+ * <tt>max</tt> (inclusive).
+ */
+ public static int nextRandomInt(int min, int max) {
+ return (int) (nextRandomFloat() * (max - min + 1)) + min;
+ }
+
+ public static int nextRandomInt() {
+ return rand.nextInt();
+ }
+
+ /**
+ * Converts a point from Spherical coordinates to Cartesian (using positive
+ * Y as up) and stores the results in the store var.
+ */
+ public static Vector3f sphericalToCartesian(Vector3f sphereCoords,
+ Vector3f store) {
+ store.y = sphereCoords.x * FastMath.sin(sphereCoords.z);
+ float a = sphereCoords.x * FastMath.cos(sphereCoords.z);
+ store.x = a * FastMath.cos(sphereCoords.y);
+ store.z = a * FastMath.sin(sphereCoords.y);
+
+ return store;
+ }
+
+ /**
+ * Converts a point from Cartesian coordinates (using positive Y as up) to
+ * Spherical and stores the results in the store var. (Radius, Azimuth,
+ * Polar)
+ */
+ public static Vector3f cartesianToSpherical(Vector3f cartCoords,
+ Vector3f store) {
+ float x = cartCoords.x;
+ if (x == 0) {
+ x = FastMath.FLT_EPSILON;
+ }
+ store.x = FastMath.sqrt((x * x)
+ + (cartCoords.y * cartCoords.y)
+ + (cartCoords.z * cartCoords.z));
+ store.y = FastMath.atan(cartCoords.z / x);
+ if (x < 0) {
+ store.y += FastMath.PI;
+ }
+ store.z = FastMath.asin(cartCoords.y / store.x);
+ return store;
+ }
+
+ /**
+ * Converts a point from Spherical coordinates to Cartesian (using positive
+ * Z as up) and stores the results in the store var.
+ */
+ public static Vector3f sphericalToCartesianZ(Vector3f sphereCoords,
+ Vector3f store) {
+ store.z = sphereCoords.x * FastMath.sin(sphereCoords.z);
+ float a = sphereCoords.x * FastMath.cos(sphereCoords.z);
+ store.x = a * FastMath.cos(sphereCoords.y);
+ store.y = a * FastMath.sin(sphereCoords.y);
+
+ return store;
+ }
+
+ /**
+ * Converts a point from Cartesian coordinates (using positive Z as up) to
+ * Spherical and stores the results in the store var. (Radius, Azimuth,
+ * Polar)
+ */
+ public static Vector3f cartesianZToSpherical(Vector3f cartCoords,
+ Vector3f store) {
+ float x = cartCoords.x;
+ if (x == 0) {
+ x = FastMath.FLT_EPSILON;
+ }
+ store.x = FastMath.sqrt((x * x)
+ + (cartCoords.y * cartCoords.y)
+ + (cartCoords.z * cartCoords.z));
+ store.z = FastMath.atan(cartCoords.z / x);
+ if (x < 0) {
+ store.z += FastMath.PI;
+ }
+ store.y = FastMath.asin(cartCoords.y / store.x);
+ return store;
+ }
+
+ /**
+ * Takes an value and expresses it in terms of min to max.
+ *
+ * @param val -
+ * the angle to normalize (in radians)
+ * @return the normalized angle (also in radians)
+ */
+ public static float normalize(float val, float min, float max) {
+ if (Float.isInfinite(val) || Float.isNaN(val)) {
+ return 0f;
+ }
+ float range = max - min;
+ while (val > max) {
+ val -= range;
+ }
+ while (val < min) {
+ val += range;
+ }
+ return val;
+ }
+
+ /**
+ * @param x
+ * the value whose sign is to be adjusted.
+ * @param y
+ * the value whose sign is to be used.
+ * @return x with its sign changed to match the sign of y.
+ */
+ public static float copysign(float x, float y) {
+ if (y >= 0 && x <= -0) {
+ return -x;
+ } else if (y < 0 && x >= 0) {
+ return -x;
+ } else {
+ return x;
+ }
+ }
+
+ /**
+ * Take a float input and clamp it between min and max.
+ *
+ * @param input
+ * @param min
+ * @param max
+ * @return clamped input
+ */
+ public static float clamp(float input, float min, float max) {
+ return (input < min) ? min : (input > max) ? max : input;
+ }
+
+ /**
+ * Clamps the given float to be between 0 and 1.
+ *
+ * @param input
+ * @return input clamped between 0 and 1.
+ */
+ public static float saturate(float input) {
+ return clamp(input, 0f, 1f);
+ }
+
+ /**
+ * Converts a single precision (32 bit) floating point value
+ * into half precision (16 bit).
+ *
+ * <p>Source: <a href="http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf">
+ * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf</a><br><strong>broken link</strong>
+ *
+ * @param half The half floating point value as a short.
+ * @return floating point value of the half.
+ */
+ public static float convertHalfToFloat(short half) {
+ switch ((int) half) {
+ case 0x0000:
+ return 0f;
+ case 0x8000:
+ return -0f;
+ case 0x7c00:
+ return Float.POSITIVE_INFINITY;
+ case 0xfc00:
+ return Float.NEGATIVE_INFINITY;
+ // TODO: Support for NaN?
+ default:
+ return Float.intBitsToFloat(((half & 0x8000) << 16)
+ | (((half & 0x7c00) + 0x1C000) << 13)
+ | ((half & 0x03FF) << 13));
+ }
+ }
+
+ public static short convertFloatToHalf(float flt) {
+ if (Float.isNaN(flt)) {
+ throw new UnsupportedOperationException("NaN to half conversion not supported!");
+ } else if (flt == Float.POSITIVE_INFINITY) {
+ return (short) 0x7c00;
+ } else if (flt == Float.NEGATIVE_INFINITY) {
+ return (short) 0xfc00;
+ } else if (flt == 0f) {
+ return (short) 0x0000;
+ } else if (flt == -0f) {
+ return (short) 0x8000;
+ } else if (flt > 65504f) {
+ // max value supported by half float
+ return 0x7bff;
+ } else if (flt < -65504f) {
+ return (short) (0x7bff | 0x8000);
+ } else if (flt > 0f && flt < 5.96046E-8f) {
+ return 0x0001;
+ } else if (flt < 0f && flt > -5.96046E-8f) {
+ return (short) 0x8001;
+ }
+
+ int f = Float.floatToIntBits(flt);
+ return (short) (((f >> 16) & 0x8000)
+ | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
+ | ((f >> 13) & 0x03ff));
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Line.java b/engine/src/core/com/jme3/math/Line.java
new file mode 100644
index 0000000..a0fa79b
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Line.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * <code>Line</code> defines a line. Where a line is defined as infinite along
+ * two points. The two points of the line are defined as the origin and direction.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Line implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f origin;
+ private Vector3f direction;
+
+ /**
+ * Constructor instantiates a new <code>Line</code> object. The origin and
+ * direction are set to defaults (0,0,0).
+ *
+ */
+ public Line() {
+ origin = new Vector3f();
+ direction = new Vector3f();
+ }
+
+ /**
+ * Constructor instantiates a new <code>Line</code> object. The origin
+ * and direction are set via the parameters.
+ * @param origin the origin of the line.
+ * @param direction the direction of the line.
+ */
+ public Line(Vector3f origin, Vector3f direction) {
+ this.origin = origin;
+ this.direction = direction;
+ }
+
+ /**
+ *
+ * <code>getOrigin</code> returns the origin of the line.
+ * @return the origin of the line.
+ */
+ public Vector3f getOrigin() {
+ return origin;
+ }
+
+ /**
+ *
+ * <code>setOrigin</code> sets the origin of the line.
+ * @param origin the origin of the line.
+ */
+ public void setOrigin(Vector3f origin) {
+ this.origin = origin;
+ }
+
+ /**
+ *
+ * <code>getDirection</code> returns the direction of the line.
+ * @return the direction of the line.
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ *
+ * <code>setDirection</code> sets the direction of the line.
+ * @param direction the direction of the line.
+ */
+ public void setDirection(Vector3f direction) {
+ this.direction = direction;
+ }
+
+ public float distanceSquared(Vector3f point) {
+ TempVars vars = TempVars.get();
+
+ Vector3f compVec1 = vars.vect1;
+ Vector3f compVec2 = vars.vect2;
+
+ point.subtract(origin, compVec1);
+ float lineParameter = direction.dot(compVec1);
+ origin.add(direction.mult(lineParameter, compVec2), compVec2);
+ compVec2.subtract(point, compVec1);
+ float len = compVec1.lengthSquared();
+ vars.release();
+ return len;
+ }
+
+ public float distance(Vector3f point) {
+ return FastMath.sqrt(distanceSquared(point));
+ }
+
+ public void orthogonalLineFit(FloatBuffer points) {
+ if (points == null) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ Vector3f compVec1 = vars.vect1;
+ Vector3f compVec2 = vars.vect2;
+ Matrix3f compMat1 = vars.tempMat3;
+ Eigen3f compEigen1 = vars.eigen;
+
+ points.rewind();
+
+ // compute average of points
+ int length = points.remaining() / 3;
+
+ BufferUtils.populateFromBuffer(origin, points, 0);
+ for (int i = 1; i < length; i++) {
+ BufferUtils.populateFromBuffer(compVec1, points, i);
+ origin.addLocal(compVec1);
+ }
+
+ origin.multLocal(1f / (float) length);
+
+ // compute sums of products
+ float sumXX = 0.0f, sumXY = 0.0f, sumXZ = 0.0f;
+ float sumYY = 0.0f, sumYZ = 0.0f, sumZZ = 0.0f;
+
+ points.rewind();
+ for (int i = 0; i < length; i++) {
+ BufferUtils.populateFromBuffer(compVec1, points, i);
+ compVec1.subtract(origin, compVec2);
+ sumXX += compVec2.x * compVec2.x;
+ sumXY += compVec2.x * compVec2.y;
+ sumXZ += compVec2.x * compVec2.z;
+ sumYY += compVec2.y * compVec2.y;
+ sumYZ += compVec2.y * compVec2.z;
+ sumZZ += compVec2.z * compVec2.z;
+ }
+
+ //find the smallest eigen vector for the direction vector
+ compMat1.m00 = sumYY + sumZZ;
+ compMat1.m01 = -sumXY;
+ compMat1.m02 = -sumXZ;
+ compMat1.m10 = -sumXY;
+ compMat1.m11 = sumXX + sumZZ;
+ compMat1.m12 = -sumYZ;
+ compMat1.m20 = -sumXZ;
+ compMat1.m21 = -sumYZ;
+ compMat1.m22 = sumXX + sumYY;
+
+ compEigen1.calculateEigen(compMat1);
+ direction = compEigen1.getEigenVector(0);
+
+ vars.release();
+ }
+
+ /**
+ *
+ * <code>random</code> determines a random point along the line.
+ * @return a random point on the line.
+ */
+ public Vector3f random() {
+ return random(null);
+ }
+
+ /**
+ * <code>random</code> determines a random point along the line.
+ *
+ * @param result Vector to store result in
+ * @return a random point on the line.
+ */
+ public Vector3f random(Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+ float rand = (float) Math.random();
+
+ result.x = (origin.x * (1 - rand)) + (direction.x * rand);
+ result.y = (origin.y * (1 - rand)) + (direction.y * rand);
+ result.z = (origin.z * (1 - rand)) + (direction.z * rand);
+
+ return result;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(origin, "origin", Vector3f.ZERO);
+ capsule.write(direction, "direction", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
+ direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Line clone() {
+ try {
+ Line line = (Line) super.clone();
+ line.direction = direction.clone();
+ line.origin = origin.clone();
+ return line;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/LineSegment.java b/engine/src/core/com/jme3/math/LineSegment.java
new file mode 100644
index 0000000..c86619c
--- /dev/null
+++ b/engine/src/core/com/jme3/math/LineSegment.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * <p>LineSegment represents a segment in the space. This is a portion of a Line
+ * that has a limited start and end points.</p>
+ * <p>A LineSegment is defined by an origin, a direction and an extent (or length).
+ * Direction should be a normalized vector. It is not internally normalized.</p>
+ * <p>This class provides methods to calculate distances between LineSegments, Rays and Vectors.
+ * It is also possible to retrieve both end points of the segment {@link LineSegment#getPositiveEnd(Vector3f)}
+ * and {@link LineSegment#getNegativeEnd(Vector3f)}. There are also methods to check whether
+ * a point is within the segment bounds.</p>
+ *
+ * @see Ray
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class LineSegment implements Cloneable, Savable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f origin;
+ private Vector3f direction;
+ private float extent;
+
+ public LineSegment() {
+ origin = new Vector3f();
+ direction = new Vector3f();
+ }
+
+ public LineSegment(LineSegment ls) {
+ this.origin = new Vector3f(ls.getOrigin());
+ this.direction = new Vector3f(ls.getDirection());
+ this.extent = ls.getExtent();
+ }
+
+ /**
+ * <p>Creates a new LineSegment with the given origin, direction and extent.</p>
+ * <p>Note that the origin is not one of the ends of the LineSegment, but its center.</p>
+ */
+ public LineSegment(Vector3f origin, Vector3f direction, float extent) {
+ this.origin = origin;
+ this.direction = direction;
+ this.extent = extent;
+ }
+
+ /**
+ * <p>Creates a new LineSegment with a given origin and end. This constructor will calculate the
+ * center, the direction and the extent.</p>
+ */
+ public LineSegment(Vector3f start, Vector3f end) {
+ this.origin = new Vector3f(0.5f * (start.x + end.x), 0.5f * (start.y + end.y), 0.5f * (start.z + end.z));
+ this.direction = end.subtract(start);
+ this.extent = direction.length() * 0.5f;
+ direction.normalizeLocal();
+ }
+
+ public void set(LineSegment ls) {
+ this.origin = new Vector3f(ls.getOrigin());
+ this.direction = new Vector3f(ls.getDirection());
+ this.extent = ls.getExtent();
+ }
+
+ public float distance(Vector3f point) {
+ return FastMath.sqrt(distanceSquared(point));
+ }
+
+ public float distance(LineSegment ls) {
+ return FastMath.sqrt(distanceSquared(ls));
+ }
+
+ public float distance(Ray r) {
+ return FastMath.sqrt(distanceSquared(r));
+ }
+
+ public float distanceSquared(Vector3f point) {
+ TempVars vars = TempVars.get();
+ Vector3f compVec1 = vars.vect1;
+
+ point.subtract(origin, compVec1);
+ float segmentParameter = direction.dot(compVec1);
+
+ if (-extent < segmentParameter) {
+ if (segmentParameter < extent) {
+ origin.add(direction.mult(segmentParameter, compVec1),
+ compVec1);
+ } else {
+ origin.add(direction.mult(extent, compVec1), compVec1);
+ }
+ } else {
+ origin.subtract(direction.mult(extent, compVec1), compVec1);
+ }
+
+ compVec1.subtractLocal(point);
+ float len = compVec1.lengthSquared();
+ vars.release();
+ return len;
+ }
+
+ public float distanceSquared(LineSegment test) {
+ TempVars vars = TempVars.get();
+ Vector3f compVec1 = vars.vect1;
+
+ origin.subtract(test.getOrigin(), compVec1);
+ float negativeDirectionDot = -(direction.dot(test.getDirection()));
+ float diffThisDot = compVec1.dot(direction);
+ float diffTestDot = -(compVec1.dot(test.getDirection()));
+ float lengthOfDiff = compVec1.lengthSquared();
+ vars.release();
+ float determinant = FastMath.abs(1.0f - negativeDirectionDot
+ * negativeDirectionDot);
+ float s0, s1, squareDistance, extentDeterminant0, extentDeterminant1, tempS0, tempS1;
+
+ if (determinant >= FastMath.FLT_EPSILON) {
+ // segments are not parallel
+ s0 = negativeDirectionDot * diffTestDot - diffThisDot;
+ s1 = negativeDirectionDot * diffThisDot - diffTestDot;
+ extentDeterminant0 = extent * determinant;
+ extentDeterminant1 = test.getExtent() * determinant;
+
+ if (s0 >= -extentDeterminant0) {
+ if (s0 <= extentDeterminant0) {
+ if (s1 >= -extentDeterminant1) {
+ if (s1 <= extentDeterminant1) // region 0 (interior)
+ {
+ // minimum at two interior points of 3D lines
+ float inverseDeterminant = ((float) 1.0)
+ / determinant;
+ s0 *= inverseDeterminant;
+ s1 *= inverseDeterminant;
+ squareDistance = s0
+ * (s0 + negativeDirectionDot * s1 + (2.0f) * diffThisDot)
+ + s1
+ * (negativeDirectionDot * s0 + s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else // region 3 (side)
+ {
+ s1 = test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0)
+ + s1 * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0)
+ + s1 * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ }
+ }
+ } else // region 7 (side)
+ {
+ s1 = -test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ }
+ }
+ } else {
+ if (s1 >= -extentDeterminant1) {
+ if (s1 <= extentDeterminant1) // region 1 (side)
+ {
+ s0 = extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ } else // region 2 (corner)
+ {
+ s1 = test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0)
+ + s1 * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1
+ * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1
+ * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ } else // region 8 (corner)
+ {
+ s1 = -test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 > test.getExtent()) {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 >= -test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ }
+ } else {
+ if (s1 >= -extentDeterminant1) {
+ if (s1 <= extentDeterminant1) // region 5 (side)
+ {
+ s0 = -extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ } else // region 4 (corner)
+ {
+ s1 = test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 > extent) {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 >= -extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = -extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ } else // region 6 (corner)
+ {
+ s1 = -test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 > extent) {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot) + lengthOfDiff;
+ } else if (tempS0 >= -extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot) + lengthOfDiff;
+ } else {
+ s0 = -extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ }
+ } else {
+ // The segments are parallel. The average b0 term is designed to
+ // ensure symmetry of the function. That is, dist(seg0,seg1) and
+ // dist(seg1,seg0) should produce the same number.get
+ float extentSum = extent + test.getExtent();
+ float sign = (negativeDirectionDot > 0.0f ? -1.0f : 1.0f);
+ float averageB0 = (0.5f) * (diffThisDot - sign * diffTestDot);
+ float lambda = -averageB0;
+ if (lambda < -extentSum) {
+ lambda = -extentSum;
+ } else if (lambda > extentSum) {
+ lambda = extentSum;
+ }
+
+ squareDistance = lambda * (lambda + (2.0f) * averageB0)
+ + lengthOfDiff;
+ }
+
+ return FastMath.abs(squareDistance);
+ }
+
+ public float distanceSquared(Ray r) {
+ Vector3f kDiff = r.getOrigin().subtract(origin);
+ float fA01 = -r.getDirection().dot(direction);
+ float fB0 = kDiff.dot(r.getDirection());
+ float fB1 = -kDiff.dot(direction);
+ float fC = kDiff.lengthSquared();
+ float fDet = FastMath.abs(1.0f - fA01 * fA01);
+ float fS0, fS1, fSqrDist, fExtDet;
+
+ if (fDet >= FastMath.FLT_EPSILON) {
+ // The ray and segment are not parallel.
+ fS0 = fA01 * fB1 - fB0;
+ fS1 = fA01 * fB0 - fB1;
+ fExtDet = extent * fDet;
+
+ if (fS0 >= (float) 0.0) {
+ if (fS1 >= -fExtDet) {
+ if (fS1 <= fExtDet) // region 0
+ {
+ // minimum at interior points of ray and segment
+ float fInvDet = ((float) 1.0) / fDet;
+ fS0 *= fInvDet;
+ fS1 *= fInvDet;
+ fSqrDist = fS0
+ * (fS0 + fA01 * fS1 + ((float) 2.0) * fB0)
+ + fS1
+ * (fA01 * fS0 + fS1 + ((float) 2.0) * fB1) + fC;
+ } else // region 1
+ {
+ fS1 = extent;
+ fS0 = -(fA01 * fS1 + fB0);
+ if (fS0 > (float) 0.0) {
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ } else // region 5
+ {
+ fS1 = -extent;
+ fS0 = -(fA01 * fS1 + fB0);
+ if (fS0 > (float) 0.0) {
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ } else {
+ if (fS1 <= -fExtDet) // region 4
+ {
+ fS0 = -(-fA01 * extent + fB0);
+ if (fS0 > (float) 0.0) {
+ fS1 = -extent;
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fS1 = -fB1;
+ if (fS1 < -extent) {
+ fS1 = -extent;
+ } else if (fS1 > extent) {
+ fS1 = extent;
+ }
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ } else if (fS1 <= fExtDet) // region 3
+ {
+ fS0 = (float) 0.0;
+ fS1 = -fB1;
+ if (fS1 < -extent) {
+ fS1 = -extent;
+ } else if (fS1 > extent) {
+ fS1 = extent;
+ }
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else // region 2
+ {
+ fS0 = -(fA01 * extent + fB0);
+ if (fS0 > (float) 0.0) {
+ fS1 = extent;
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fS1 = -fB1;
+ if (fS1 < -extent) {
+ fS1 = -extent;
+ } else if (fS1 > extent) {
+ fS1 = extent;
+ }
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ }
+ } else {
+ // ray and segment are parallel
+ if (fA01 > (float) 0.0) {
+ // opposite direction vectors
+ fS1 = -extent;
+ } else {
+ // same direction vectors
+ fS1 = extent;
+ }
+
+ fS0 = -(fA01 * fS1 + fB0);
+ if (fS0 > (float) 0.0) {
+ fSqrDist = -fS0 * fS0 + fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ return FastMath.abs(fSqrDist);
+ }
+
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ public void setDirection(Vector3f direction) {
+ this.direction = direction;
+ }
+
+ public float getExtent() {
+ return extent;
+ }
+
+ public void setExtent(float extent) {
+ this.extent = extent;
+ }
+
+ public Vector3f getOrigin() {
+ return origin;
+ }
+
+ public void setOrigin(Vector3f origin) {
+ this.origin = origin;
+ }
+
+ // P+e*D
+ public Vector3f getPositiveEnd(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ return origin.add((direction.mult(extent, store)), store);
+ }
+
+ // P-e*D
+ public Vector3f getNegativeEnd(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ return origin.subtract((direction.mult(extent, store)), store);
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(origin, "origin", Vector3f.ZERO);
+ capsule.write(direction, "direction", Vector3f.ZERO);
+ capsule.write(extent, "extent", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
+ direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
+ extent = capsule.readFloat("extent", 0);
+ }
+
+ @Override
+ public LineSegment clone() {
+ try {
+ LineSegment segment = (LineSegment) super.clone();
+ segment.direction = direction.clone();
+ segment.origin = origin.clone();
+ return segment;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * <p>Evaluates whether a given point is contained within the axis aligned bounding box
+ * that contains this LineSegment.</p><p>This function is float error aware.</p>
+ */
+ public boolean isPointInsideBounds(Vector3f point) {
+ return isPointInsideBounds(point, Float.MIN_VALUE);
+ }
+
+ /**
+ * <p>Evaluates whether a given point is contained within the axis aligned bounding box
+ * that contains this LineSegment.</p><p>This function accepts an error parameter, which
+ * is added to the extent of the bounding box.</p>
+ */
+ public boolean isPointInsideBounds(Vector3f point, float error) {
+
+ if (FastMath.abs(point.x - origin.x) > FastMath.abs(direction.x * extent) + error) {
+ return false;
+ }
+ if (FastMath.abs(point.y - origin.y) > FastMath.abs(direction.y * extent) + error) {
+ return false;
+ }
+ if (FastMath.abs(point.z - origin.z) > FastMath.abs(direction.z * extent) + error) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Matrix3f.java b/engine/src/core/com/jme3/math/Matrix3f.java
new file mode 100644
index 0000000..96bf1b3
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Matrix3f.java
@@ -0,0 +1,1387 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+/**
+ * <code>Matrix3f</code> defines a 3x3 matrix. Matrix data is maintained
+ * internally and is accessible via the get and set methods. Convenience methods
+ * are used for matrix operations as well as generating a matrix from a given
+ * set of values.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Matrix3f implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Matrix3f.class.getName());
+ protected float m00, m01, m02;
+ protected float m10, m11, m12;
+ protected float m20, m21, m22;
+ public static final Matrix3f ZERO = new Matrix3f(0, 0, 0, 0, 0, 0, 0, 0, 0);
+ public static final Matrix3f IDENTITY = new Matrix3f();
+
+ /**
+ * Constructor instantiates a new <code>Matrix3f</code> object. The
+ * initial values for the matrix is that of the identity matrix.
+ *
+ */
+ public Matrix3f() {
+ loadIdentity();
+ }
+
+ /**
+ * constructs a matrix with the given values.
+ *
+ * @param m00
+ * 0x0 in the matrix.
+ * @param m01
+ * 0x1 in the matrix.
+ * @param m02
+ * 0x2 in the matrix.
+ * @param m10
+ * 1x0 in the matrix.
+ * @param m11
+ * 1x1 in the matrix.
+ * @param m12
+ * 1x2 in the matrix.
+ * @param m20
+ * 2x0 in the matrix.
+ * @param m21
+ * 2x1 in the matrix.
+ * @param m22
+ * 2x2 in the matrix.
+ */
+ public Matrix3f(float m00, float m01, float m02, float m10, float m11,
+ float m12, float m20, float m21, float m22) {
+
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ }
+
+ /**
+ * Copy constructor that creates a new <code>Matrix3f</code> object that
+ * is the same as the provided matrix.
+ *
+ * @param mat
+ * the matrix to copy.
+ */
+ public Matrix3f(Matrix3f mat) {
+ set(mat);
+ }
+
+ /**
+ * Takes the absolute value of all matrix fields locally.
+ */
+ public void absoluteLocal() {
+ m00 = FastMath.abs(m00);
+ m01 = FastMath.abs(m01);
+ m02 = FastMath.abs(m02);
+ m10 = FastMath.abs(m10);
+ m11 = FastMath.abs(m11);
+ m12 = FastMath.abs(m12);
+ m20 = FastMath.abs(m20);
+ m21 = FastMath.abs(m21);
+ m22 = FastMath.abs(m22);
+ }
+
+ /**
+ * <code>copy</code> transfers the contents of a given matrix to this
+ * matrix. If a null matrix is supplied, this matrix is set to the identity
+ * matrix.
+ *
+ * @param matrix
+ * the matrix to copy.
+ * @return this
+ */
+ public Matrix3f set(Matrix3f matrix) {
+ if (null == matrix) {
+ loadIdentity();
+ } else {
+ m00 = matrix.m00;
+ m01 = matrix.m01;
+ m02 = matrix.m02;
+ m10 = matrix.m10;
+ m11 = matrix.m11;
+ m12 = matrix.m12;
+ m20 = matrix.m20;
+ m21 = matrix.m21;
+ m22 = matrix.m22;
+ }
+ return this;
+ }
+
+ /**
+ * <code>get</code> retrieves a value from the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> is
+ * thrown.
+ *
+ * @param i
+ * the row index.
+ * @param j
+ * the colum index.
+ * @return the value at (i, j).
+ */
+ @SuppressWarnings("fallthrough")
+ public float get(int i, int j) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ return m00;
+ case 1:
+ return m01;
+ case 2:
+ return m02;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ return m10;
+ case 1:
+ return m11;
+ case 2:
+ return m12;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ return m20;
+ case 1:
+ return m21;
+ case 2:
+ return m22;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ * <code>get(float[])</code> returns the matrix in row-major or column-major order.
+ *
+ * @param data
+ * The array to return the data into. This array can be 9 or 16 floats in size.
+ * Only the upper 3x3 are assigned to in the case of a 16 element array.
+ * @param rowMajor
+ * True for row major storage in the array (translation in elements 3, 7, 11 for a 4x4),
+ * false for column major (translation in elements 12, 13, 14 for a 4x4).
+ */
+ public void get(float[] data, boolean rowMajor) {
+ if (data.length == 9) {
+ if (rowMajor) {
+ data[0] = m00;
+ data[1] = m01;
+ data[2] = m02;
+ data[3] = m10;
+ data[4] = m11;
+ data[5] = m12;
+ data[6] = m20;
+ data[7] = m21;
+ data[8] = m22;
+ } else {
+ data[0] = m00;
+ data[1] = m10;
+ data[2] = m20;
+ data[3] = m01;
+ data[4] = m11;
+ data[5] = m21;
+ data[6] = m02;
+ data[7] = m12;
+ data[8] = m22;
+ }
+ } else if (data.length == 16) {
+ if (rowMajor) {
+ data[0] = m00;
+ data[1] = m01;
+ data[2] = m02;
+ data[4] = m10;
+ data[5] = m11;
+ data[6] = m12;
+ data[8] = m20;
+ data[9] = m21;
+ data[10] = m22;
+ } else {
+ data[0] = m00;
+ data[1] = m10;
+ data[2] = m20;
+ data[4] = m01;
+ data[5] = m11;
+ data[6] = m21;
+ data[8] = m02;
+ data[9] = m12;
+ data[10] = m22;
+ }
+ } else {
+ throw new IndexOutOfBoundsException("Array size must be 9 or 16 in Matrix3f.get().");
+ }
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three columns specified by the
+ * parameter. This column is returned as a <code>Vector3f</code> object.
+ *
+ * @param i
+ * the column to retrieve. Must be between 0 and 2.
+ * @return the column specified by the index.
+ */
+ public Vector3f getColumn(int i) {
+ return getColumn(i, null);
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three columns specified by the
+ * parameter. This column is returned as a <code>Vector3f</code> object.
+ *
+ * @param i
+ * the column to retrieve. Must be between 0 and 2.
+ * @param store
+ * the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the column specified by the index.
+ */
+ public Vector3f getColumn(int i, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ switch (i) {
+ case 0:
+ store.x = m00;
+ store.y = m10;
+ store.z = m20;
+ break;
+ case 1:
+ store.x = m01;
+ store.y = m11;
+ store.z = m21;
+ break;
+ case 2:
+ store.x = m02;
+ store.y = m12;
+ store.z = m22;
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ return store;
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three rows as specified by the
+ * parameter. This row is returned as a <code>Vector3f</code> object.
+ *
+ * @param i
+ * the row to retrieve. Must be between 0 and 2.
+ * @return the row specified by the index.
+ */
+ public Vector3f getRow(int i) {
+ return getRow(i, null);
+ }
+
+ /**
+ * <code>getRow</code> returns one of three rows as specified by the
+ * parameter. This row is returned as a <code>Vector3f</code> object.
+ *
+ * @param i
+ * the row to retrieve. Must be between 0 and 2.
+ * @param store
+ * the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the row specified by the index.
+ */
+ public Vector3f getRow(int i, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ switch (i) {
+ case 0:
+ store.x = m00;
+ store.y = m01;
+ store.z = m02;
+ break;
+ case 1:
+ store.x = m10;
+ store.y = m11;
+ store.z = m12;
+ break;
+ case 2:
+ store.x = m20;
+ store.y = m21;
+ store.z = m22;
+ break;
+ default:
+ logger.warning("Invalid row index.");
+ throw new IllegalArgumentException("Invalid row index. " + i);
+ }
+ return store;
+ }
+
+ /**
+ * <code>toFloatBuffer</code> returns a FloatBuffer object that contains
+ * the matrix data.
+ *
+ * @return matrix data as a FloatBuffer.
+ */
+ public FloatBuffer toFloatBuffer() {
+ FloatBuffer fb = BufferUtils.createFloatBuffer(9);
+
+ fb.put(m00).put(m01).put(m02);
+ fb.put(m10).put(m11).put(m12);
+ fb.put(m20).put(m21).put(m22);
+ fb.rewind();
+ return fb;
+ }
+
+ /**
+ * <code>fillFloatBuffer</code> fills a FloatBuffer object with the matrix
+ * data.
+ *
+ * @param fb
+ * the buffer to fill, starting at current position. Must have
+ * room for 9 more floats.
+ * @return matrix data as a FloatBuffer. (position is advanced by 9 and any
+ * limit set is not changed).
+ */
+ public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) {
+// if (columnMajor){
+// fb.put(m00).put(m10).put(m20);
+// fb.put(m01).put(m11).put(m21);
+// fb.put(m02).put(m12).put(m22);
+// }else{
+// fb.put(m00).put(m01).put(m02);
+// fb.put(m10).put(m11).put(m12);
+// fb.put(m20).put(m21).put(m22);
+// }
+
+ TempVars vars = TempVars.get();
+
+
+ fillFloatArray(vars.matrixWrite, columnMajor);
+ fb.put(vars.matrixWrite, 0, 9);
+
+ vars.release();
+
+ return fb;
+ }
+
+ public void fillFloatArray(float[] f, boolean columnMajor) {
+ if (columnMajor) {
+ f[ 0] = m00;
+ f[ 1] = m10;
+ f[ 2] = m20;
+ f[ 3] = m01;
+ f[ 4] = m11;
+ f[ 5] = m21;
+ f[ 6] = m02;
+ f[ 7] = m12;
+ f[ 8] = m22;
+ } else {
+ f[ 0] = m00;
+ f[ 1] = m01;
+ f[ 2] = m02;
+ f[ 3] = m10;
+ f[ 4] = m11;
+ f[ 5] = m12;
+ f[ 6] = m20;
+ f[ 7] = m21;
+ f[ 8] = m22;
+ }
+ }
+
+ /**
+ *
+ * <code>setColumn</code> sets a particular column of this matrix to that
+ * represented by the provided vector.
+ *
+ * @param i
+ * the column to set.
+ * @param column
+ * the data to set.
+ * @return this
+ */
+ public Matrix3f setColumn(int i, Vector3f column) {
+
+ if (column == null) {
+ logger.warning("Column is null. Ignoring.");
+ return this;
+ }
+ switch (i) {
+ case 0:
+ m00 = column.x;
+ m10 = column.y;
+ m20 = column.z;
+ break;
+ case 1:
+ m01 = column.x;
+ m11 = column.y;
+ m21 = column.z;
+ break;
+ case 2:
+ m02 = column.x;
+ m12 = column.y;
+ m22 = column.z;
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ return this;
+ }
+
+ /**
+ *
+ * <code>setRow</code> sets a particular row of this matrix to that
+ * represented by the provided vector.
+ *
+ * @param i
+ * the row to set.
+ * @param row
+ * the data to set.
+ * @return this
+ */
+ public Matrix3f setRow(int i, Vector3f row) {
+
+ if (row == null) {
+ logger.warning("Row is null. Ignoring.");
+ return this;
+ }
+ switch (i) {
+ case 0:
+ m00 = row.x;
+ m01 = row.y;
+ m02 = row.z;
+ break;
+ case 1:
+ m10 = row.x;
+ m11 = row.y;
+ m12 = row.z;
+ break;
+ case 2:
+ m20 = row.x;
+ m21 = row.y;
+ m22 = row.z;
+ break;
+ default:
+ logger.warning("Invalid row index.");
+ throw new IllegalArgumentException("Invalid row index. " + i);
+ }
+ return this;
+ }
+
+ /**
+ * <code>set</code> places a given value into the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> is
+ * thrown.
+ *
+ * @param i
+ * the row index.
+ * @param j
+ * the colum index.
+ * @param value
+ * the value for (i, j).
+ * @return this
+ */
+ @SuppressWarnings("fallthrough")
+ public Matrix3f set(int i, int j, float value) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ m00 = value;
+ return this;
+ case 1:
+ m01 = value;
+ return this;
+ case 2:
+ m02 = value;
+ return this;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ m10 = value;
+ return this;
+ case 1:
+ m11 = value;
+ return this;
+ case 2:
+ m12 = value;
+ return this;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ m20 = value;
+ return this;
+ case 1:
+ m21 = value;
+ return this;
+ case 2:
+ m22 = value;
+ return this;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ *
+ * <code>set</code> sets the values of the matrix to those supplied by the
+ * 3x3 two dimenion array.
+ *
+ * @param matrix
+ * the new values of the matrix.
+ * @throws JmeException
+ * if the array is not of size 9.
+ * @return this
+ */
+ public Matrix3f set(float[][] matrix) {
+ if (matrix.length != 3 || matrix[0].length != 3) {
+ throw new IllegalArgumentException(
+ "Array must be of size 9.");
+ }
+
+ m00 = matrix[0][0];
+ m01 = matrix[0][1];
+ m02 = matrix[0][2];
+ m10 = matrix[1][0];
+ m11 = matrix[1][1];
+ m12 = matrix[1][2];
+ m20 = matrix[2][0];
+ m21 = matrix[2][1];
+ m22 = matrix[2][2];
+
+ return this;
+ }
+
+ /**
+ * Recreate Matrix using the provided axis.
+ *
+ * @param uAxis
+ * Vector3f
+ * @param vAxis
+ * Vector3f
+ * @param wAxis
+ * Vector3f
+ */
+ public void fromAxes(Vector3f uAxis, Vector3f vAxis, Vector3f wAxis) {
+ m00 = uAxis.x;
+ m10 = uAxis.y;
+ m20 = uAxis.z;
+
+ m01 = vAxis.x;
+ m11 = vAxis.y;
+ m21 = vAxis.z;
+
+ m02 = wAxis.x;
+ m12 = wAxis.y;
+ m22 = wAxis.z;
+ }
+
+ /**
+ * <code>set</code> sets the values of this matrix from an array of
+ * values assuming that the data is rowMajor order;
+ *
+ * @param matrix
+ * the matrix to set the value to.
+ * @return this
+ */
+ public Matrix3f set(float[] matrix) {
+ return set(matrix, true);
+ }
+
+ /**
+ * <code>set</code> sets the values of this matrix from an array of
+ * values;
+ *
+ * @param matrix
+ * the matrix to set the value to.
+ * @param rowMajor
+ * whether the incoming data is in row or column major order.
+ * @return this
+ */
+ public Matrix3f set(float[] matrix, boolean rowMajor) {
+ if (matrix.length != 9) {
+ throw new IllegalArgumentException(
+ "Array must be of size 9.");
+ }
+
+ if (rowMajor) {
+ m00 = matrix[0];
+ m01 = matrix[1];
+ m02 = matrix[2];
+ m10 = matrix[3];
+ m11 = matrix[4];
+ m12 = matrix[5];
+ m20 = matrix[6];
+ m21 = matrix[7];
+ m22 = matrix[8];
+ } else {
+ m00 = matrix[0];
+ m01 = matrix[3];
+ m02 = matrix[6];
+ m10 = matrix[1];
+ m11 = matrix[4];
+ m12 = matrix[7];
+ m20 = matrix[2];
+ m21 = matrix[5];
+ m22 = matrix[8];
+ }
+ return this;
+ }
+
+ /**
+ *
+ * <code>set</code> defines the values of the matrix based on a supplied
+ * <code>Quaternion</code>. It should be noted that all previous values
+ * will be overridden.
+ *
+ * @param quaternion
+ * the quaternion to create a rotational matrix from.
+ * @return this
+ */
+ public Matrix3f set(Quaternion quaternion) {
+ return quaternion.toRotationMatrix(this);
+ }
+
+ /**
+ * <code>loadIdentity</code> sets this matrix to the identity matrix.
+ * Where all values are zero except those along the diagonal which are one.
+ *
+ */
+ public void loadIdentity() {
+ m01 = m02 = m10 = m12 = m20 = m21 = 0;
+ m00 = m11 = m22 = 1;
+ }
+
+ /**
+ * @return true if this matrix is identity
+ */
+ public boolean isIdentity() {
+ return (m00 == 1 && m01 == 0 && m02 == 0)
+ && (m10 == 0 && m11 == 1 && m12 == 0)
+ && (m20 == 0 && m21 == 0 && m22 == 1);
+ }
+
+ /**
+ * <code>fromAngleAxis</code> sets this matrix4f to the values specified
+ * by an angle and an axis of rotation. This method creates an object, so
+ * use fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle
+ * the angle to rotate (in radians).
+ * @param axis
+ * the axis of rotation.
+ */
+ public void fromAngleAxis(float angle, Vector3f axis) {
+ Vector3f normAxis = axis.normalize();
+ fromAngleNormalAxis(angle, normAxis);
+ }
+
+ /**
+ * <code>fromAngleNormalAxis</code> sets this matrix4f to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle
+ * the angle to rotate (in radians).
+ * @param axis
+ * the axis of rotation (already normalized).
+ */
+ public void fromAngleNormalAxis(float angle, Vector3f axis) {
+ float fCos = FastMath.cos(angle);
+ float fSin = FastMath.sin(angle);
+ float fOneMinusCos = ((float) 1.0) - fCos;
+ float fX2 = axis.x * axis.x;
+ float fY2 = axis.y * axis.y;
+ float fZ2 = axis.z * axis.z;
+ float fXYM = axis.x * axis.y * fOneMinusCos;
+ float fXZM = axis.x * axis.z * fOneMinusCos;
+ float fYZM = axis.y * axis.z * fOneMinusCos;
+ float fXSin = axis.x * fSin;
+ float fYSin = axis.y * fSin;
+ float fZSin = axis.z * fSin;
+
+ m00 = fX2 * fOneMinusCos + fCos;
+ m01 = fXYM - fZSin;
+ m02 = fXZM + fYSin;
+ m10 = fXYM + fZSin;
+ m11 = fY2 * fOneMinusCos + fCos;
+ m12 = fYZM - fXSin;
+ m20 = fXZM - fYSin;
+ m21 = fYZM + fXSin;
+ m22 = fZ2 * fOneMinusCos + fCos;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix by a given matrix. The result
+ * matrix is returned as a new object. If the given matrix is null, a null
+ * matrix is returned.
+ *
+ * @param mat
+ * the matrix to multiply this matrix by.
+ * @return the result matrix.
+ */
+ public Matrix3f mult(Matrix3f mat) {
+ return mult(mat, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix by a given matrix. The result
+ * matrix is returned as a new object.
+ *
+ * @param mat
+ * the matrix to multiply this matrix by.
+ * @param product
+ * the matrix to store the result in. if null, a new matrix3f is
+ * created. It is safe for mat and product to be the same object.
+ * @return a matrix3f object containing the result of this operation
+ */
+ public Matrix3f mult(Matrix3f mat, Matrix3f product) {
+
+ float temp00, temp01, temp02;
+ float temp10, temp11, temp12;
+ float temp20, temp21, temp22;
+
+ if (product == null) {
+ product = new Matrix3f();
+ }
+ temp00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20;
+ temp01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21;
+ temp02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22;
+ temp10 = m10 * mat.m00 + m11 * mat.m10 + m12 * mat.m20;
+ temp11 = m10 * mat.m01 + m11 * mat.m11 + m12 * mat.m21;
+ temp12 = m10 * mat.m02 + m11 * mat.m12 + m12 * mat.m22;
+ temp20 = m20 * mat.m00 + m21 * mat.m10 + m22 * mat.m20;
+ temp21 = m20 * mat.m01 + m21 * mat.m11 + m22 * mat.m21;
+ temp22 = m20 * mat.m02 + m21 * mat.m12 + m22 * mat.m22;
+
+ product.m00 = temp00;
+ product.m01 = temp01;
+ product.m02 = temp02;
+ product.m10 = temp10;
+ product.m11 = temp11;
+ product.m12 = temp12;
+ product.m20 = temp20;
+ product.m21 = temp21;
+ product.m22 = temp22;
+
+ return product;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix by a given
+ * <code>Vector3f</code> object. The result vector is returned. If the
+ * given vector is null, null will be returned.
+ *
+ * @param vec
+ * the vector to multiply this matrix by.
+ * @return the result vector.
+ */
+ public Vector3f mult(Vector3f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * Multiplies this 3x3 matrix by the 1x3 Vector vec and stores the result in
+ * product.
+ *
+ * @param vec
+ * The Vector3f to multiply.
+ * @param product
+ * The Vector3f to store the result, it is safe for this to be
+ * the same as vec.
+ * @return The given product vector.
+ */
+ public Vector3f mult(Vector3f vec, Vector3f product) {
+
+ if (null == product) {
+ product = new Vector3f();
+ }
+
+ float x = vec.x;
+ float y = vec.y;
+ float z = vec.z;
+
+ product.x = m00 * x + m01 * y + m02 * z;
+ product.y = m10 * x + m11 * y + m12 * z;
+ product.z = m20 * x + m21 * y + m22 * z;
+ return product;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this matrix internally by
+ * a given float scale factor.
+ *
+ * @param scale
+ * the value to scale by.
+ * @return this Matrix3f
+ */
+ public Matrix3f multLocal(float scale) {
+ m00 *= scale;
+ m01 *= scale;
+ m02 *= scale;
+ m10 *= scale;
+ m11 *= scale;
+ m12 *= scale;
+ m20 *= scale;
+ m21 *= scale;
+ m22 *= scale;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this matrix by a given
+ * <code>Vector3f</code> object. The result vector is stored inside the
+ * passed vector, then returned . If the given vector is null, null will be
+ * returned.
+ *
+ * @param vec
+ * the vector to multiply this matrix by.
+ * @return The passed vector after multiplication
+ */
+ public Vector3f multLocal(Vector3f vec) {
+ if (vec == null) {
+ return null;
+ }
+ float x = vec.x;
+ float y = vec.y;
+ vec.x = m00 * x + m01 * y + m02 * vec.z;
+ vec.y = m10 * x + m11 * y + m12 * vec.z;
+ vec.z = m20 * x + m21 * y + m22 * vec.z;
+ return vec;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix by a given matrix. The result
+ * matrix is saved in the current matrix. If the given matrix is null,
+ * nothing happens. The current matrix is returned. This is equivalent to
+ * this*=mat
+ *
+ * @param mat
+ * the matrix to multiply this matrix by.
+ * @return This matrix, after the multiplication
+ */
+ public Matrix3f multLocal(Matrix3f mat) {
+ return mult(mat, this);
+ }
+
+ /**
+ * Transposes this matrix in place. Returns this matrix for chaining
+ *
+ * @return This matrix after transpose
+ */
+ public Matrix3f transposeLocal() {
+// float[] tmp = new float[9];
+// get(tmp, false);
+// set(tmp, true);
+
+ float tmp = m01;
+ m01 = m10;
+ m10 = tmp;
+
+ tmp = m02;
+ m02 = m20;
+ m20 = tmp;
+
+ tmp = m12;
+ m12 = m21;
+ m21 = tmp;
+
+ return this;
+ }
+
+ /**
+ * Inverts this matrix as a new Matrix3f.
+ *
+ * @return The new inverse matrix
+ */
+ public Matrix3f invert() {
+ return invert(null);
+ }
+
+ /**
+ * Inverts this matrix and stores it in the given store.
+ *
+ * @return The store
+ */
+ public Matrix3f invert(Matrix3f store) {
+ if (store == null) {
+ store = new Matrix3f();
+ }
+
+ float det = determinant();
+ if (FastMath.abs(det) <= FastMath.FLT_EPSILON) {
+ return store.zero();
+ }
+
+ store.m00 = m11 * m22 - m12 * m21;
+ store.m01 = m02 * m21 - m01 * m22;
+ store.m02 = m01 * m12 - m02 * m11;
+ store.m10 = m12 * m20 - m10 * m22;
+ store.m11 = m00 * m22 - m02 * m20;
+ store.m12 = m02 * m10 - m00 * m12;
+ store.m20 = m10 * m21 - m11 * m20;
+ store.m21 = m01 * m20 - m00 * m21;
+ store.m22 = m00 * m11 - m01 * m10;
+
+ store.multLocal(1f / det);
+ return store;
+ }
+
+ /**
+ * Inverts this matrix locally.
+ *
+ * @return this
+ */
+ public Matrix3f invertLocal() {
+ float det = determinant();
+ if (FastMath.abs(det) <= 0f) {
+ return zero();
+ }
+
+ float f00 = m11 * m22 - m12 * m21;
+ float f01 = m02 * m21 - m01 * m22;
+ float f02 = m01 * m12 - m02 * m11;
+ float f10 = m12 * m20 - m10 * m22;
+ float f11 = m00 * m22 - m02 * m20;
+ float f12 = m02 * m10 - m00 * m12;
+ float f20 = m10 * m21 - m11 * m20;
+ float f21 = m01 * m20 - m00 * m21;
+ float f22 = m00 * m11 - m01 * m10;
+
+ m00 = f00;
+ m01 = f01;
+ m02 = f02;
+ m10 = f10;
+ m11 = f11;
+ m12 = f12;
+ m20 = f20;
+ m21 = f21;
+ m22 = f22;
+
+ multLocal(1f / det);
+ return this;
+ }
+
+ /**
+ * Returns a new matrix representing the adjoint of this matrix.
+ *
+ * @return The adjoint matrix
+ */
+ public Matrix3f adjoint() {
+ return adjoint(null);
+ }
+
+ /**
+ * Places the adjoint of this matrix in store (creates store if null.)
+ *
+ * @param store
+ * The matrix to store the result in. If null, a new matrix is created.
+ * @return store
+ */
+ public Matrix3f adjoint(Matrix3f store) {
+ if (store == null) {
+ store = new Matrix3f();
+ }
+
+ store.m00 = m11 * m22 - m12 * m21;
+ store.m01 = m02 * m21 - m01 * m22;
+ store.m02 = m01 * m12 - m02 * m11;
+ store.m10 = m12 * m20 - m10 * m22;
+ store.m11 = m00 * m22 - m02 * m20;
+ store.m12 = m02 * m10 - m00 * m12;
+ store.m20 = m10 * m21 - m11 * m20;
+ store.m21 = m01 * m20 - m00 * m21;
+ store.m22 = m00 * m11 - m01 * m10;
+
+ return store;
+ }
+
+ /**
+ * <code>determinant</code> generates the determinant of this matrix.
+ *
+ * @return the determinant
+ */
+ public float determinant() {
+ float fCo00 = m11 * m22 - m12 * m21;
+ float fCo10 = m12 * m20 - m10 * m22;
+ float fCo20 = m10 * m21 - m11 * m20;
+ float fDet = m00 * fCo00 + m01 * fCo10 + m02 * fCo20;
+ return fDet;
+ }
+
+ /**
+ * Sets all of the values in this matrix to zero.
+ *
+ * @return this matrix
+ */
+ public Matrix3f zero() {
+ m00 = m01 = m02 = m10 = m11 = m12 = m20 = m21 = m22 = 0.0f;
+ return this;
+ }
+
+ /**
+ * <code>transpose</code> <b>locally</b> transposes this Matrix.
+ * This is inconsistent with general value vs local semantics, but is
+ * preserved for backwards compatibility. Use transposeNew() to transpose
+ * to a new object (value).
+ *
+ * @return this object for chaining.
+ */
+ public Matrix3f transpose() {
+ return transposeLocal();
+ }
+
+ /**
+ * <code>transposeNew</code> returns a transposed version of this matrix.
+ *
+ * @return The new Matrix3f object.
+ */
+ public Matrix3f transposeNew() {
+ Matrix3f ret = new Matrix3f(m00, m10, m20, m01, m11, m21, m02, m12, m22);
+ return ret;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this object.
+ * It is in a format of a 3x3 matrix. For example, an identity matrix would
+ * be represented by the following string. com.jme.math.Matrix3f <br>[<br>
+ * 1.0 0.0 0.0 <br>
+ * 0.0 1.0 0.0 <br>
+ * 0.0 0.0 1.0 <br>]<br>
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("Matrix3f\n[\n");
+ result.append(" ");
+ result.append(m00);
+ result.append(" ");
+ result.append(m01);
+ result.append(" ");
+ result.append(m02);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m10);
+ result.append(" ");
+ result.append(m11);
+ result.append(" ");
+ result.append(m12);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m20);
+ result.append(" ");
+ result.append(m21);
+ result.append(" ");
+ result.append(m22);
+ result.append(" \n]");
+ return result.toString();
+ }
+
+ /**
+ *
+ * <code>hashCode</code> returns the hash code value as an integer and is
+ * supported for the benefit of hashing based collection classes such as
+ * Hashtable, HashMap, HashSet etc.
+ *
+ * @return the hashcode for this instance of Matrix4f.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + Float.floatToIntBits(m00);
+ hash = 37 * hash + Float.floatToIntBits(m01);
+ hash = 37 * hash + Float.floatToIntBits(m02);
+
+ hash = 37 * hash + Float.floatToIntBits(m10);
+ hash = 37 * hash + Float.floatToIntBits(m11);
+ hash = 37 * hash + Float.floatToIntBits(m12);
+
+ hash = 37 * hash + Float.floatToIntBits(m20);
+ hash = 37 * hash + Float.floatToIntBits(m21);
+ hash = 37 * hash + Float.floatToIntBits(m22);
+
+ return hash;
+ }
+
+ /**
+ * are these two matrices the same? they are is they both have the same mXX values.
+ *
+ * @param o
+ * the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Matrix3f) || o == null) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Matrix3f comp = (Matrix3f) o;
+ if (Float.compare(m00, comp.m00) != 0) {
+ return false;
+ }
+ if (Float.compare(m01, comp.m01) != 0) {
+ return false;
+ }
+ if (Float.compare(m02, comp.m02) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m10, comp.m10) != 0) {
+ return false;
+ }
+ if (Float.compare(m11, comp.m11) != 0) {
+ return false;
+ }
+ if (Float.compare(m12, comp.m12) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m20, comp.m20) != 0) {
+ return false;
+ }
+ if (Float.compare(m21, comp.m21) != 0) {
+ return false;
+ }
+ if (Float.compare(m22, comp.m22) != 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule cap = e.getCapsule(this);
+ cap.write(m00, "m00", 1);
+ cap.write(m01, "m01", 0);
+ cap.write(m02, "m02", 0);
+ cap.write(m10, "m10", 0);
+ cap.write(m11, "m11", 1);
+ cap.write(m12, "m12", 0);
+ cap.write(m20, "m20", 0);
+ cap.write(m21, "m21", 0);
+ cap.write(m22, "m22", 1);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule cap = e.getCapsule(this);
+ m00 = cap.readFloat("m00", 1);
+ m01 = cap.readFloat("m01", 0);
+ m02 = cap.readFloat("m02", 0);
+ m10 = cap.readFloat("m10", 0);
+ m11 = cap.readFloat("m11", 1);
+ m12 = cap.readFloat("m12", 0);
+ m20 = cap.readFloat("m20", 0);
+ m21 = cap.readFloat("m21", 0);
+ m22 = cap.readFloat("m22", 1);
+ }
+
+ /**
+ * A function for creating a rotation matrix that rotates a vector called
+ * "start" into another vector called "end".
+ *
+ * @param start
+ * normalized non-zero starting vector
+ * @param end
+ * normalized non-zero ending vector
+ * @see "Tomas M�ller, John Hughes \"Efficiently Building a Matrix to Rotate \
+ * One Vector to Another\" Journal of Graphics Tools, 4(4):1-4, 1999"
+ */
+ public void fromStartEndVectors(Vector3f start, Vector3f end) {
+ Vector3f v = new Vector3f();
+ float e, h, f;
+
+ start.cross(end, v);
+ e = start.dot(end);
+ f = (e < 0) ? -e : e;
+
+ // if "from" and "to" vectors are nearly parallel
+ if (f > 1.0f - FastMath.ZERO_TOLERANCE) {
+ Vector3f u = new Vector3f();
+ Vector3f x = new Vector3f();
+ float c1, c2, c3; /* coefficients for later use */
+ int i, j;
+
+ x.x = (start.x > 0.0) ? start.x : -start.x;
+ x.y = (start.y > 0.0) ? start.y : -start.y;
+ x.z = (start.z > 0.0) ? start.z : -start.z;
+
+ if (x.x < x.y) {
+ if (x.x < x.z) {
+ x.x = 1.0f;
+ x.y = x.z = 0.0f;
+ } else {
+ x.z = 1.0f;
+ x.x = x.y = 0.0f;
+ }
+ } else {
+ if (x.y < x.z) {
+ x.y = 1.0f;
+ x.x = x.z = 0.0f;
+ } else {
+ x.z = 1.0f;
+ x.x = x.y = 0.0f;
+ }
+ }
+
+ u.x = x.x - start.x;
+ u.y = x.y - start.y;
+ u.z = x.z - start.z;
+ v.x = x.x - end.x;
+ v.y = x.y - end.y;
+ v.z = x.z - end.z;
+
+ c1 = 2.0f / u.dot(u);
+ c2 = 2.0f / v.dot(v);
+ c3 = c1 * c2 * u.dot(v);
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ float val = -c1 * u.get(i) * u.get(j) - c2 * v.get(i)
+ * v.get(j) + c3 * v.get(i) * u.get(j);
+ set(i, j, val);
+ }
+ float val = get(i, i);
+ set(i, i, val + 1.0f);
+ }
+ } else {
+ // the most common case, unless "start"="end", or "start"=-"end"
+ float hvx, hvz, hvxy, hvxz, hvyz;
+ h = 1.0f / (1.0f + e);
+ hvx = h * v.x;
+ hvz = h * v.z;
+ hvxy = hvx * v.y;
+ hvxz = hvx * v.z;
+ hvyz = hvz * v.y;
+ set(0, 0, e + hvx * v.x);
+ set(0, 1, hvxy - v.z);
+ set(0, 2, hvxz + v.y);
+
+ set(1, 0, hvxy + v.z);
+ set(1, 1, e + h * v.y * v.y);
+ set(1, 2, hvyz - v.x);
+
+ set(2, 0, hvxz - v.y);
+ set(2, 1, hvyz + v.x);
+ set(2, 2, e + hvz * v.z);
+ }
+ }
+
+ /**
+ * <code>scale</code> scales the operation performed by this matrix on a
+ * per-component basis.
+ *
+ * @param scale
+ * The scale applied to each of the X, Y and Z output values.
+ */
+ public void scale(Vector3f scale) {
+ m00 *= scale.x;
+ m10 *= scale.x;
+ m20 *= scale.x;
+ m01 *= scale.y;
+ m11 *= scale.y;
+ m21 *= scale.y;
+ m02 *= scale.z;
+ m12 *= scale.z;
+ m22 *= scale.z;
+ }
+
+ static boolean equalIdentity(Matrix3f mat) {
+ if (Math.abs(mat.m00 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m11 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m22 - 1) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m01) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m02) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m10) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m12) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m20) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m21) > 1e-4) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public Matrix3f clone() {
+ try {
+ return (Matrix3f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Matrix4f.java b/engine/src/core/com/jme3/math/Matrix4f.java
new file mode 100644
index 0000000..8521eab
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Matrix4f.java
@@ -0,0 +1,2305 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+/**
+ * <code>Matrix4f</code> defines and maintains a 4x4 matrix in row major order.
+ * This matrix is intended for use in a translation and rotational capacity.
+ * It provides convenience methods for creating the matrix from a multitude
+ * of sources.
+ *
+ * Matrices are stored assuming column vectors on the right, with the translation
+ * in the rightmost column. Element numbering is row,column, so m03 is the zeroth
+ * row, third column, which is the "x" translation part. This means that the implicit
+ * storage order is column major. However, the get() and set() functions on float
+ * arrays default to row major order!
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Matrix4f implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Matrix4f.class.getName());
+ public float m00, m01, m02, m03;
+ public float m10, m11, m12, m13;
+ public float m20, m21, m22, m23;
+ public float m30, m31, m32, m33;
+ public static final Matrix4f ZERO = new Matrix4f(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ public static final Matrix4f IDENTITY = new Matrix4f();
+
+ /**
+ * Constructor instantiates a new <code>Matrix</code> that is set to the
+ * identity matrix.
+ *
+ */
+ public Matrix4f() {
+ loadIdentity();
+ }
+
+ /**
+ * constructs a matrix with the given values.
+ */
+ public Matrix4f(float m00, float m01, float m02, float m03,
+ float m10, float m11, float m12, float m13,
+ float m20, float m21, float m22, float m23,
+ float m30, float m31, float m32, float m33) {
+
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m03 = m03;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m13 = m13;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ this.m23 = m23;
+ this.m30 = m30;
+ this.m31 = m31;
+ this.m32 = m32;
+ this.m33 = m33;
+ }
+
+ /**
+ * Create a new Matrix4f, given data in column-major format.
+ *
+ * @param array
+ * An array of 16 floats in column-major format (translation in elements 12, 13 and 14).
+ */
+ public Matrix4f(float[] array) {
+ set(array, false);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Matrix</code> that is set to the
+ * provided matrix. This constructor copies a given Matrix. If the provided
+ * matrix is null, the constructor sets the matrix to the identity.
+ *
+ * @param mat
+ * the matrix to copy.
+ */
+ public Matrix4f(Matrix4f mat) {
+ copy(mat);
+ }
+
+ /**
+ * <code>copy</code> transfers the contents of a given matrix to this
+ * matrix. If a null matrix is supplied, this matrix is set to the identity
+ * matrix.
+ *
+ * @param matrix
+ * the matrix to copy.
+ */
+ public void copy(Matrix4f matrix) {
+ if (null == matrix) {
+ loadIdentity();
+ } else {
+ m00 = matrix.m00;
+ m01 = matrix.m01;
+ m02 = matrix.m02;
+ m03 = matrix.m03;
+ m10 = matrix.m10;
+ m11 = matrix.m11;
+ m12 = matrix.m12;
+ m13 = matrix.m13;
+ m20 = matrix.m20;
+ m21 = matrix.m21;
+ m22 = matrix.m22;
+ m23 = matrix.m23;
+ m30 = matrix.m30;
+ m31 = matrix.m31;
+ m32 = matrix.m32;
+ m33 = matrix.m33;
+ }
+ }
+
+ public void fromFrame(Vector3f location, Vector3f direction, Vector3f up, Vector3f left) {
+ loadIdentity();
+
+ TempVars vars = TempVars.get();
+
+ Vector3f f = vars.vect1.set(direction);
+ Vector3f s = vars.vect2.set(f).crossLocal(up);
+ Vector3f u = vars.vect3.set(s).crossLocal(f);
+// s.normalizeLocal();
+// u.normalizeLocal();
+
+ m00 = s.x;
+ m01 = s.y;
+ m02 = s.z;
+
+ m10 = u.x;
+ m11 = u.y;
+ m12 = u.z;
+
+ m20 = -f.x;
+ m21 = -f.y;
+ m22 = -f.z;
+
+// m00 = -left.x;
+// m10 = -left.y;
+// m20 = -left.z;
+//
+// m01 = up.x;
+// m11 = up.y;
+// m21 = up.z;
+//
+// m02 = -direction.x;
+// m12 = -direction.y;
+// m22 = -direction.z;
+//
+
+ Matrix4f transMatrix = vars.tempMat4;
+ transMatrix.loadIdentity();
+ transMatrix.m03 = -location.x;
+ transMatrix.m13 = -location.y;
+ transMatrix.m23 = -location.z;
+ this.multLocal(transMatrix);
+
+ vars.release();
+
+// transMatrix.multLocal(this);
+
+// set(transMatrix);
+ }
+
+ /**
+ * <code>get</code> retrieves the values of this object into
+ * a float array in row-major order.
+ *
+ * @param matrix
+ * the matrix to set the values into.
+ */
+ public void get(float[] matrix) {
+ get(matrix, true);
+ }
+
+ /**
+ * <code>set</code> retrieves the values of this object into
+ * a float array.
+ *
+ * @param matrix
+ * the matrix to set the values into.
+ * @param rowMajor
+ * whether the outgoing data is in row or column major order.
+ */
+ public void get(float[] matrix, boolean rowMajor) {
+ if (matrix.length != 16) {
+ throw new IllegalArgumentException(
+ "Array must be of size 16.");
+ }
+
+ if (rowMajor) {
+ matrix[0] = m00;
+ matrix[1] = m01;
+ matrix[2] = m02;
+ matrix[3] = m03;
+ matrix[4] = m10;
+ matrix[5] = m11;
+ matrix[6] = m12;
+ matrix[7] = m13;
+ matrix[8] = m20;
+ matrix[9] = m21;
+ matrix[10] = m22;
+ matrix[11] = m23;
+ matrix[12] = m30;
+ matrix[13] = m31;
+ matrix[14] = m32;
+ matrix[15] = m33;
+ } else {
+ matrix[0] = m00;
+ matrix[4] = m01;
+ matrix[8] = m02;
+ matrix[12] = m03;
+ matrix[1] = m10;
+ matrix[5] = m11;
+ matrix[9] = m12;
+ matrix[13] = m13;
+ matrix[2] = m20;
+ matrix[6] = m21;
+ matrix[10] = m22;
+ matrix[14] = m23;
+ matrix[3] = m30;
+ matrix[7] = m31;
+ matrix[11] = m32;
+ matrix[15] = m33;
+ }
+ }
+
+ /**
+ * <code>get</code> retrieves a value from the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> is
+ * thrown.
+ *
+ * @param i
+ * the row index.
+ * @param j
+ * the colum index.
+ * @return the value at (i, j).
+ */
+ @SuppressWarnings("fallthrough")
+ public float get(int i, int j) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ return m00;
+ case 1:
+ return m01;
+ case 2:
+ return m02;
+ case 3:
+ return m03;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ return m10;
+ case 1:
+ return m11;
+ case 2:
+ return m12;
+ case 3:
+ return m13;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ return m20;
+ case 1:
+ return m21;
+ case 2:
+ return m22;
+ case 3:
+ return m23;
+ }
+ case 3:
+ switch (j) {
+ case 0:
+ return m30;
+ case 1:
+ return m31;
+ case 2:
+ return m32;
+ case 3:
+ return m33;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three columns specified by the
+ * parameter. This column is returned as a float array of length 4.
+ *
+ * @param i
+ * the column to retrieve. Must be between 0 and 3.
+ * @return the column specified by the index.
+ */
+ public float[] getColumn(int i) {
+ return getColumn(i, null);
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three columns specified by the
+ * parameter. This column is returned as a float[4].
+ *
+ * @param i
+ * the column to retrieve. Must be between 0 and 3.
+ * @param store
+ * the float array to store the result in. if null, a new one
+ * is created.
+ * @return the column specified by the index.
+ */
+ public float[] getColumn(int i, float[] store) {
+ if (store == null) {
+ store = new float[4];
+ }
+ switch (i) {
+ case 0:
+ store[0] = m00;
+ store[1] = m10;
+ store[2] = m20;
+ store[3] = m30;
+ break;
+ case 1:
+ store[0] = m01;
+ store[1] = m11;
+ store[2] = m21;
+ store[3] = m31;
+ break;
+ case 2:
+ store[0] = m02;
+ store[1] = m12;
+ store[2] = m22;
+ store[3] = m32;
+ break;
+ case 3:
+ store[0] = m03;
+ store[1] = m13;
+ store[2] = m23;
+ store[3] = m33;
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ return store;
+ }
+
+ /**
+ *
+ * <code>setColumn</code> sets a particular column of this matrix to that
+ * represented by the provided vector.
+ *
+ * @param i
+ * the column to set.
+ * @param column
+ * the data to set.
+ */
+ public void setColumn(int i, float[] column) {
+
+ if (column == null) {
+ logger.warning("Column is null. Ignoring.");
+ return;
+ }
+ switch (i) {
+ case 0:
+ m00 = column[0];
+ m10 = column[1];
+ m20 = column[2];
+ m30 = column[3];
+ break;
+ case 1:
+ m01 = column[0];
+ m11 = column[1];
+ m21 = column[2];
+ m31 = column[3];
+ break;
+ case 2:
+ m02 = column[0];
+ m12 = column[1];
+ m22 = column[2];
+ m32 = column[3];
+ break;
+ case 3:
+ m03 = column[0];
+ m13 = column[1];
+ m23 = column[2];
+ m33 = column[3];
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ }
+
+ /**
+ * <code>set</code> places a given value into the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> is
+ * thrown.
+ *
+ * @param i
+ * the row index.
+ * @param j
+ * the colum index.
+ * @param value
+ * the value for (i, j).
+ */
+ @SuppressWarnings("fallthrough")
+ public void set(int i, int j, float value) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ m00 = value;
+ return;
+ case 1:
+ m01 = value;
+ return;
+ case 2:
+ m02 = value;
+ return;
+ case 3:
+ m03 = value;
+ return;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ m10 = value;
+ return;
+ case 1:
+ m11 = value;
+ return;
+ case 2:
+ m12 = value;
+ return;
+ case 3:
+ m13 = value;
+ return;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ m20 = value;
+ return;
+ case 1:
+ m21 = value;
+ return;
+ case 2:
+ m22 = value;
+ return;
+ case 3:
+ m23 = value;
+ return;
+ }
+ case 3:
+ switch (j) {
+ case 0:
+ m30 = value;
+ return;
+ case 1:
+ m31 = value;
+ return;
+ case 2:
+ m32 = value;
+ return;
+ case 3:
+ m33 = value;
+ return;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ * <code>set</code> sets the values of this matrix from an array of
+ * values.
+ *
+ * @param matrix
+ * the matrix to set the value to.
+ * @throws JmeException
+ * if the array is not of size 16.
+ */
+ public void set(float[][] matrix) {
+ if (matrix.length != 4 || matrix[0].length != 4) {
+ throw new IllegalArgumentException(
+ "Array must be of size 16.");
+ }
+
+ m00 = matrix[0][0];
+ m01 = matrix[0][1];
+ m02 = matrix[0][2];
+ m03 = matrix[0][3];
+ m10 = matrix[1][0];
+ m11 = matrix[1][1];
+ m12 = matrix[1][2];
+ m13 = matrix[1][3];
+ m20 = matrix[2][0];
+ m21 = matrix[2][1];
+ m22 = matrix[2][2];
+ m23 = matrix[2][3];
+ m30 = matrix[3][0];
+ m31 = matrix[3][1];
+ m32 = matrix[3][2];
+ m33 = matrix[3][3];
+ }
+
+ /**
+ * <code>set</code> sets the values of this matrix from another matrix.
+ *
+ * @param matrix
+ * the matrix to read the value from.
+ */
+ public Matrix4f set(Matrix4f matrix) {
+ m00 = matrix.m00;
+ m01 = matrix.m01;
+ m02 = matrix.m02;
+ m03 = matrix.m03;
+ m10 = matrix.m10;
+ m11 = matrix.m11;
+ m12 = matrix.m12;
+ m13 = matrix.m13;
+ m20 = matrix.m20;
+ m21 = matrix.m21;
+ m22 = matrix.m22;
+ m23 = matrix.m23;
+ m30 = matrix.m30;
+ m31 = matrix.m31;
+ m32 = matrix.m32;
+ m33 = matrix.m33;
+ return this;
+ }
+
+ /**
+ * <code>set</code> sets the values of this matrix from an array of
+ * values assuming that the data is rowMajor order;
+ *
+ * @param matrix
+ * the matrix to set the value to.
+ */
+ public void set(float[] matrix) {
+ set(matrix, true);
+ }
+
+ /**
+ * <code>set</code> sets the values of this matrix from an array of
+ * values;
+ *
+ * @param matrix
+ * the matrix to set the value to.
+ * @param rowMajor
+ * whether the incoming data is in row or column major order.
+ */
+ public void set(float[] matrix, boolean rowMajor) {
+ if (matrix.length != 16) {
+ throw new IllegalArgumentException(
+ "Array must be of size 16.");
+ }
+
+ if (rowMajor) {
+ m00 = matrix[0];
+ m01 = matrix[1];
+ m02 = matrix[2];
+ m03 = matrix[3];
+ m10 = matrix[4];
+ m11 = matrix[5];
+ m12 = matrix[6];
+ m13 = matrix[7];
+ m20 = matrix[8];
+ m21 = matrix[9];
+ m22 = matrix[10];
+ m23 = matrix[11];
+ m30 = matrix[12];
+ m31 = matrix[13];
+ m32 = matrix[14];
+ m33 = matrix[15];
+ } else {
+ m00 = matrix[0];
+ m01 = matrix[4];
+ m02 = matrix[8];
+ m03 = matrix[12];
+ m10 = matrix[1];
+ m11 = matrix[5];
+ m12 = matrix[9];
+ m13 = matrix[13];
+ m20 = matrix[2];
+ m21 = matrix[6];
+ m22 = matrix[10];
+ m23 = matrix[14];
+ m30 = matrix[3];
+ m31 = matrix[7];
+ m32 = matrix[11];
+ m33 = matrix[15];
+ }
+ }
+
+ public Matrix4f transpose() {
+ float[] tmp = new float[16];
+ get(tmp, true);
+ Matrix4f mat = new Matrix4f(tmp);
+ return mat;
+ }
+
+ /**
+ * <code>transpose</code> locally transposes this Matrix.
+ *
+ * @return this object for chaining.
+ */
+ public Matrix4f transposeLocal() {
+ float tmp = m01;
+ m01 = m10;
+ m10 = tmp;
+
+ tmp = m02;
+ m02 = m20;
+ m20 = tmp;
+
+ tmp = m03;
+ m03 = m30;
+ m30 = tmp;
+
+ tmp = m12;
+ m12 = m21;
+ m21 = tmp;
+
+ tmp = m13;
+ m13 = m31;
+ m31 = tmp;
+
+ tmp = m23;
+ m23 = m32;
+ m32 = tmp;
+
+ return this;
+ }
+
+ /**
+ * <code>toFloatBuffer</code> returns a FloatBuffer object that contains
+ * the matrix data.
+ *
+ * @return matrix data as a FloatBuffer.
+ */
+ public FloatBuffer toFloatBuffer() {
+ return toFloatBuffer(false);
+ }
+
+ /**
+ * <code>toFloatBuffer</code> returns a FloatBuffer object that contains the
+ * matrix data.
+ *
+ * @param columnMajor
+ * if true, this buffer should be filled with column major data,
+ * otherwise it will be filled row major.
+ * @return matrix data as a FloatBuffer. The position is set to 0 for
+ * convenience.
+ */
+ public FloatBuffer toFloatBuffer(boolean columnMajor) {
+ FloatBuffer fb = BufferUtils.createFloatBuffer(16);
+ fillFloatBuffer(fb, columnMajor);
+ fb.rewind();
+ return fb;
+ }
+
+ /**
+ * <code>fillFloatBuffer</code> fills a FloatBuffer object with
+ * the matrix data.
+ * @param fb the buffer to fill, must be correct size
+ * @return matrix data as a FloatBuffer.
+ */
+ public FloatBuffer fillFloatBuffer(FloatBuffer fb) {
+ return fillFloatBuffer(fb, false);
+ }
+
+ /**
+ * <code>fillFloatBuffer</code> fills a FloatBuffer object with the matrix
+ * data.
+ *
+ * @param fb
+ * the buffer to fill, starting at current position. Must have
+ * room for 16 more floats.
+ * @param columnMajor
+ * if true, this buffer should be filled with column major data,
+ * otherwise it will be filled row major.
+ * @return matrix data as a FloatBuffer. (position is advanced by 16 and any
+ * limit set is not changed).
+ */
+ public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) {
+// if (columnMajor) {
+// fb.put(m00).put(m10).put(m20).put(m30);
+// fb.put(m01).put(m11).put(m21).put(m31);
+// fb.put(m02).put(m12).put(m22).put(m32);
+// fb.put(m03).put(m13).put(m23).put(m33);
+// } else {
+// fb.put(m00).put(m01).put(m02).put(m03);
+// fb.put(m10).put(m11).put(m12).put(m13);
+// fb.put(m20).put(m21).put(m22).put(m23);
+// fb.put(m30).put(m31).put(m32).put(m33);
+// }
+
+ TempVars vars = TempVars.get();
+
+
+ fillFloatArray(vars.matrixWrite, columnMajor);
+ fb.put(vars.matrixWrite, 0, 16);
+
+ vars.release();
+
+ return fb;
+ }
+
+ public void fillFloatArray(float[] f, boolean columnMajor) {
+ if (columnMajor) {
+ f[ 0] = m00;
+ f[ 1] = m10;
+ f[ 2] = m20;
+ f[ 3] = m30;
+ f[ 4] = m01;
+ f[ 5] = m11;
+ f[ 6] = m21;
+ f[ 7] = m31;
+ f[ 8] = m02;
+ f[ 9] = m12;
+ f[10] = m22;
+ f[11] = m32;
+ f[12] = m03;
+ f[13] = m13;
+ f[14] = m23;
+ f[15] = m33;
+ } else {
+ f[ 0] = m00;
+ f[ 1] = m01;
+ f[ 2] = m02;
+ f[ 3] = m03;
+ f[ 4] = m10;
+ f[ 5] = m11;
+ f[ 6] = m12;
+ f[ 7] = m13;
+ f[ 8] = m20;
+ f[ 9] = m21;
+ f[10] = m22;
+ f[11] = m23;
+ f[12] = m30;
+ f[13] = m31;
+ f[14] = m32;
+ f[15] = m33;
+ }
+ }
+
+ /**
+ * <code>readFloatBuffer</code> reads value for this matrix from a FloatBuffer.
+ * @param fb the buffer to read from, must be correct size
+ * @return this data as a FloatBuffer.
+ */
+ public Matrix4f readFloatBuffer(FloatBuffer fb) {
+ return readFloatBuffer(fb, false);
+ }
+
+ /**
+ * <code>readFloatBuffer</code> reads value for this matrix from a FloatBuffer.
+ * @param fb the buffer to read from, must be correct size
+ * @param columnMajor if true, this buffer should be filled with column
+ * major data, otherwise it will be filled row major.
+ * @return this data as a FloatBuffer.
+ */
+ public Matrix4f readFloatBuffer(FloatBuffer fb, boolean columnMajor) {
+
+ if (columnMajor) {
+ m00 = fb.get();
+ m10 = fb.get();
+ m20 = fb.get();
+ m30 = fb.get();
+ m01 = fb.get();
+ m11 = fb.get();
+ m21 = fb.get();
+ m31 = fb.get();
+ m02 = fb.get();
+ m12 = fb.get();
+ m22 = fb.get();
+ m32 = fb.get();
+ m03 = fb.get();
+ m13 = fb.get();
+ m23 = fb.get();
+ m33 = fb.get();
+ } else {
+ m00 = fb.get();
+ m01 = fb.get();
+ m02 = fb.get();
+ m03 = fb.get();
+ m10 = fb.get();
+ m11 = fb.get();
+ m12 = fb.get();
+ m13 = fb.get();
+ m20 = fb.get();
+ m21 = fb.get();
+ m22 = fb.get();
+ m23 = fb.get();
+ m30 = fb.get();
+ m31 = fb.get();
+ m32 = fb.get();
+ m33 = fb.get();
+ }
+ return this;
+ }
+
+ /**
+ * <code>loadIdentity</code> sets this matrix to the identity matrix,
+ * namely all zeros with ones along the diagonal.
+ *
+ */
+ public void loadIdentity() {
+ m01 = m02 = m03 = 0.0f;
+ m10 = m12 = m13 = 0.0f;
+ m20 = m21 = m23 = 0.0f;
+ m30 = m31 = m32 = 0.0f;
+ m00 = m11 = m22 = m33 = 1.0f;
+ }
+
+ public void fromFrustum(float near, float far, float left, float right, float top, float bottom, boolean parallel) {
+ loadIdentity();
+ if (parallel) {
+ // scale
+ m00 = 2.0f / (right - left);
+ //m11 = 2.0f / (bottom - top);
+ m11 = 2.0f / (top - bottom);
+ m22 = -2.0f / (far - near);
+ m33 = 1f;
+
+ // translation
+ m03 = -(right + left) / (right - left);
+ //m31 = -(bottom + top) / (bottom - top);
+ m13 = -(top + bottom) / (top - bottom);
+ m23 = -(far + near) / (far - near);
+ } else {
+ m00 = (2.0f * near) / (right - left);
+ m11 = (2.0f * near) / (top - bottom);
+ m32 = -1.0f;
+ m33 = -0.0f;
+
+ // A
+ m02 = (right + left) / (right - left);
+
+ // B
+ m12 = (top + bottom) / (top - bottom);
+
+ // C
+ m22 = -(far + near) / (far - near);
+
+ // D
+ m23 = -(2.0f * far * near) / (far - near);
+ }
+ }
+
+ /**
+ * <code>fromAngleAxis</code> sets this matrix4f to the values specified
+ * by an angle and an axis of rotation. This method creates an object, so
+ * use fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle
+ * the angle to rotate (in radians).
+ * @param axis
+ * the axis of rotation.
+ */
+ public void fromAngleAxis(float angle, Vector3f axis) {
+ Vector3f normAxis = axis.normalize();
+ fromAngleNormalAxis(angle, normAxis);
+ }
+
+ /**
+ * <code>fromAngleNormalAxis</code> sets this matrix4f to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle
+ * the angle to rotate (in radians).
+ * @param axis
+ * the axis of rotation (already normalized).
+ */
+ public void fromAngleNormalAxis(float angle, Vector3f axis) {
+ zero();
+ m33 = 1;
+
+ float fCos = FastMath.cos(angle);
+ float fSin = FastMath.sin(angle);
+ float fOneMinusCos = ((float) 1.0) - fCos;
+ float fX2 = axis.x * axis.x;
+ float fY2 = axis.y * axis.y;
+ float fZ2 = axis.z * axis.z;
+ float fXYM = axis.x * axis.y * fOneMinusCos;
+ float fXZM = axis.x * axis.z * fOneMinusCos;
+ float fYZM = axis.y * axis.z * fOneMinusCos;
+ float fXSin = axis.x * fSin;
+ float fYSin = axis.y * fSin;
+ float fZSin = axis.z * fSin;
+
+ m00 = fX2 * fOneMinusCos + fCos;
+ m01 = fXYM - fZSin;
+ m02 = fXZM + fYSin;
+ m10 = fXYM + fZSin;
+ m11 = fY2 * fOneMinusCos + fCos;
+ m12 = fYZM - fXSin;
+ m20 = fXZM - fYSin;
+ m21 = fYZM + fXSin;
+ m22 = fZ2 * fOneMinusCos + fCos;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix by a scalar.
+ *
+ * @param scalar
+ * the scalar to multiply this matrix by.
+ */
+ public void multLocal(float scalar) {
+ m00 *= scalar;
+ m01 *= scalar;
+ m02 *= scalar;
+ m03 *= scalar;
+ m10 *= scalar;
+ m11 *= scalar;
+ m12 *= scalar;
+ m13 *= scalar;
+ m20 *= scalar;
+ m21 *= scalar;
+ m22 *= scalar;
+ m23 *= scalar;
+ m30 *= scalar;
+ m31 *= scalar;
+ m32 *= scalar;
+ m33 *= scalar;
+ }
+
+ public Matrix4f mult(float scalar) {
+ Matrix4f out = new Matrix4f();
+ out.set(this);
+ out.multLocal(scalar);
+ return out;
+ }
+
+ public Matrix4f mult(float scalar, Matrix4f store) {
+ store.set(this);
+ store.multLocal(scalar);
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix with another matrix. The
+ * result matrix will then be returned. This matrix will be on the left hand
+ * side, while the parameter matrix will be on the right.
+ *
+ * @param in2
+ * the matrix to multiply this matrix by.
+ * @return the resultant matrix
+ */
+ public Matrix4f mult(Matrix4f in2) {
+ return mult(in2, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix with another matrix. The
+ * result matrix will then be returned. This matrix will be on the left hand
+ * side, while the parameter matrix will be on the right.
+ *
+ * @param in2
+ * the matrix to multiply this matrix by.
+ * @param store
+ * where to store the result. It is safe for in2 and store to be
+ * the same object.
+ * @return the resultant matrix
+ */
+ public Matrix4f mult(Matrix4f in2, Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ }
+
+ float temp00, temp01, temp02, temp03;
+ float temp10, temp11, temp12, temp13;
+ float temp20, temp21, temp22, temp23;
+ float temp30, temp31, temp32, temp33;
+
+ temp00 = m00 * in2.m00
+ + m01 * in2.m10
+ + m02 * in2.m20
+ + m03 * in2.m30;
+ temp01 = m00 * in2.m01
+ + m01 * in2.m11
+ + m02 * in2.m21
+ + m03 * in2.m31;
+ temp02 = m00 * in2.m02
+ + m01 * in2.m12
+ + m02 * in2.m22
+ + m03 * in2.m32;
+ temp03 = m00 * in2.m03
+ + m01 * in2.m13
+ + m02 * in2.m23
+ + m03 * in2.m33;
+
+ temp10 = m10 * in2.m00
+ + m11 * in2.m10
+ + m12 * in2.m20
+ + m13 * in2.m30;
+ temp11 = m10 * in2.m01
+ + m11 * in2.m11
+ + m12 * in2.m21
+ + m13 * in2.m31;
+ temp12 = m10 * in2.m02
+ + m11 * in2.m12
+ + m12 * in2.m22
+ + m13 * in2.m32;
+ temp13 = m10 * in2.m03
+ + m11 * in2.m13
+ + m12 * in2.m23
+ + m13 * in2.m33;
+
+ temp20 = m20 * in2.m00
+ + m21 * in2.m10
+ + m22 * in2.m20
+ + m23 * in2.m30;
+ temp21 = m20 * in2.m01
+ + m21 * in2.m11
+ + m22 * in2.m21
+ + m23 * in2.m31;
+ temp22 = m20 * in2.m02
+ + m21 * in2.m12
+ + m22 * in2.m22
+ + m23 * in2.m32;
+ temp23 = m20 * in2.m03
+ + m21 * in2.m13
+ + m22 * in2.m23
+ + m23 * in2.m33;
+
+ temp30 = m30 * in2.m00
+ + m31 * in2.m10
+ + m32 * in2.m20
+ + m33 * in2.m30;
+ temp31 = m30 * in2.m01
+ + m31 * in2.m11
+ + m32 * in2.m21
+ + m33 * in2.m31;
+ temp32 = m30 * in2.m02
+ + m31 * in2.m12
+ + m32 * in2.m22
+ + m33 * in2.m32;
+ temp33 = m30 * in2.m03
+ + m31 * in2.m13
+ + m32 * in2.m23
+ + m33 * in2.m33;
+
+ store.m00 = temp00;
+ store.m01 = temp01;
+ store.m02 = temp02;
+ store.m03 = temp03;
+ store.m10 = temp10;
+ store.m11 = temp11;
+ store.m12 = temp12;
+ store.m13 = temp13;
+ store.m20 = temp20;
+ store.m21 = temp21;
+ store.m22 = temp22;
+ store.m23 = temp23;
+ store.m30 = temp30;
+ store.m31 = temp31;
+ store.m32 = temp32;
+ store.m33 = temp33;
+
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix with another matrix. The
+ * results are stored internally and a handle to this matrix will
+ * then be returned. This matrix will be on the left hand
+ * side, while the parameter matrix will be on the right.
+ *
+ * @param in2
+ * the matrix to multiply this matrix by.
+ * @return the resultant matrix
+ */
+ public Matrix4f multLocal(Matrix4f in2) {
+ return mult(in2, this);
+ }
+
+ /**
+ * <code>mult</code> multiplies a vector about a rotation matrix. The
+ * resulting vector is returned as a new Vector3f.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @return the rotated vector.
+ */
+ public Vector3f mult(Vector3f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies a vector about a rotation matrix and adds
+ * translation. The resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f mult(Vector3f vec, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m01 * vy + m02 * vz + m03;
+ store.y = m10 * vx + m11 * vy + m12 * vz + m13;
+ store.z = m20 * vx + m21 * vy + m22 * vz + m23;
+
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies a <code>Vector4f</code> about a rotation
+ * matrix. The resulting vector is returned as a new <code>Vector4f</code>.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @return the rotated vector.
+ */
+ public Vector4f mult(Vector4f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies a <code>Vector4f</code> about a rotation
+ * matrix. The resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector4f mult(Vector4f vec, Vector4f store) {
+ if (null == vec) {
+ logger.info("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Vector4f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z, vw = vec.w;
+ store.x = m00 * vx + m01 * vy + m02 * vz + m03 * vw;
+ store.y = m10 * vx + m11 * vy + m12 * vz + m13 * vw;
+ store.z = m20 * vx + m21 * vy + m22 * vz + m23 * vw;
+ store.w = m30 * vx + m31 * vy + m32 * vz + m33 * vw;
+
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies a vector about a rotation matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ *
+ * @return the rotated vector.
+ */
+ public Vector4f multAcross(Vector4f vec) {
+ return multAcross(vec, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies a vector about a rotation matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in. created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector4f multAcross(Vector4f vec, Vector4f store) {
+ if (null == vec) {
+ logger.info("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Vector4f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z, vw = vec.w;
+ store.x = m00 * vx + m10 * vy + m20 * vz + m30 * vw;
+ store.y = m01 * vx + m11 * vy + m21 * vz + m31 * vw;
+ store.z = m02 * vx + m12 * vy + m22 * vz + m32 * vw;
+ store.z = m03 * vx + m13 * vy + m23 * vz + m33 * vw;
+
+ return store;
+ }
+
+ /**
+ * <code>multNormal</code> multiplies a vector about a rotation matrix, but
+ * does not add translation. The resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f multNormal(Vector3f vec, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m01 * vy + m02 * vz;
+ store.y = m10 * vx + m11 * vy + m12 * vz;
+ store.z = m20 * vx + m21 * vy + m22 * vz;
+
+ return store;
+ }
+
+ /**
+ * <code>multNormal</code> multiplies a vector about a rotation matrix, but
+ * does not add translation. The resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f multNormalAcross(Vector3f vec, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m10 * vy + m20 * vz;
+ store.y = m01 * vx + m11 * vy + m21 * vz;
+ store.z = m02 * vx + m12 * vy + m22 * vz;
+
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies a vector about a rotation matrix and adds
+ * translation. The w value is returned as a result of
+ * multiplying the last column of the matrix by 1.0
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in.
+ * @return the W value
+ */
+ public float multProj(Vector3f vec, Vector3f store) {
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m01 * vy + m02 * vz + m03;
+ store.y = m10 * vx + m11 * vy + m12 * vz + m13;
+ store.z = m20 * vx + m21 * vy + m22 * vz + m23;
+ return m30 * vx + m31 * vy + m32 * vz + m33;
+ }
+
+ /**
+ * <code>mult</code> multiplies a vector about a rotation matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a vector to store the result in. created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f multAcross(Vector3f vec, Vector3f store) {
+ if (null == vec) {
+ logger.info("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m10 * vy + m20 * vz + m30 * 1;
+ store.y = m01 * vx + m11 * vy + m21 * vz + m31 * 1;
+ store.z = m02 * vx + m12 * vy + m22 * vz + m32 * 1;
+
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies a quaternion about a matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @param store
+ * a quaternion to store the result in. created if null is passed.
+ * @return store = this * vec
+ */
+ public Quaternion mult(Quaternion vec, Quaternion store) {
+
+ if (null == vec) {
+ logger.warning("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Quaternion();
+ }
+
+ float x = m00 * vec.x + m10 * vec.y + m20 * vec.z + m30 * vec.w;
+ float y = m01 * vec.x + m11 * vec.y + m21 * vec.z + m31 * vec.w;
+ float z = m02 * vec.x + m12 * vec.y + m22 * vec.z + m32 * vec.w;
+ float w = m03 * vec.x + m13 * vec.y + m23 * vec.z + m33 * vec.w;
+ store.x = x;
+ store.y = y;
+ store.z = z;
+ store.w = w;
+
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies an array of 4 floats against this rotation
+ * matrix. The results are stored directly in the array. (vec4f x mat4f)
+ *
+ * @param vec4f
+ * float array (size 4) to multiply against the matrix.
+ * @return the vec4f for chaining.
+ */
+ public float[] mult(float[] vec4f) {
+ if (null == vec4f || vec4f.length != 4) {
+ logger.warning("invalid array given, must be nonnull and length 4");
+ return null;
+ }
+
+ float x = vec4f[0], y = vec4f[1], z = vec4f[2], w = vec4f[3];
+
+ vec4f[0] = m00 * x + m01 * y + m02 * z + m03 * w;
+ vec4f[1] = m10 * x + m11 * y + m12 * z + m13 * w;
+ vec4f[2] = m20 * x + m21 * y + m22 * z + m23 * w;
+ vec4f[3] = m30 * x + m31 * y + m32 * z + m33 * w;
+
+ return vec4f;
+ }
+
+ /**
+ * <code>mult</code> multiplies an array of 4 floats against this rotation
+ * matrix. The results are stored directly in the array. (vec4f x mat4f)
+ *
+ * @param vec4f
+ * float array (size 4) to multiply against the matrix.
+ * @return the vec4f for chaining.
+ */
+ public float[] multAcross(float[] vec4f) {
+ if (null == vec4f || vec4f.length != 4) {
+ logger.warning("invalid array given, must be nonnull and length 4");
+ return null;
+ }
+
+ float x = vec4f[0], y = vec4f[1], z = vec4f[2], w = vec4f[3];
+
+ vec4f[0] = m00 * x + m10 * y + m20 * z + m30 * w;
+ vec4f[1] = m01 * x + m11 * y + m21 * z + m31 * w;
+ vec4f[2] = m02 * x + m12 * y + m22 * z + m32 * w;
+ vec4f[3] = m03 * x + m13 * y + m23 * z + m33 * w;
+
+ return vec4f;
+ }
+
+ /**
+ * Inverts this matrix as a new Matrix4f.
+ *
+ * @return The new inverse matrix
+ */
+ public Matrix4f invert() {
+ return invert(null);
+ }
+
+ /**
+ * Inverts this matrix and stores it in the given store.
+ *
+ * @return The store
+ */
+ public Matrix4f invert(Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ }
+
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+ float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+ if (FastMath.abs(fDet) <= 0f) {
+ throw new ArithmeticException("This matrix cannot be inverted");
+ }
+
+ store.m00 = +m11 * fB5 - m12 * fB4 + m13 * fB3;
+ store.m10 = -m10 * fB5 + m12 * fB2 - m13 * fB1;
+ store.m20 = +m10 * fB4 - m11 * fB2 + m13 * fB0;
+ store.m30 = -m10 * fB3 + m11 * fB1 - m12 * fB0;
+ store.m01 = -m01 * fB5 + m02 * fB4 - m03 * fB3;
+ store.m11 = +m00 * fB5 - m02 * fB2 + m03 * fB1;
+ store.m21 = -m00 * fB4 + m01 * fB2 - m03 * fB0;
+ store.m31 = +m00 * fB3 - m01 * fB1 + m02 * fB0;
+ store.m02 = +m31 * fA5 - m32 * fA4 + m33 * fA3;
+ store.m12 = -m30 * fA5 + m32 * fA2 - m33 * fA1;
+ store.m22 = +m30 * fA4 - m31 * fA2 + m33 * fA0;
+ store.m32 = -m30 * fA3 + m31 * fA1 - m32 * fA0;
+ store.m03 = -m21 * fA5 + m22 * fA4 - m23 * fA3;
+ store.m13 = +m20 * fA5 - m22 * fA2 + m23 * fA1;
+ store.m23 = -m20 * fA4 + m21 * fA2 - m23 * fA0;
+ store.m33 = +m20 * fA3 - m21 * fA1 + m22 * fA0;
+
+ float fInvDet = 1.0f / fDet;
+ store.multLocal(fInvDet);
+
+ return store;
+ }
+
+ /**
+ * Inverts this matrix locally.
+ *
+ * @return this
+ */
+ public Matrix4f invertLocal() {
+
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+ float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+ if (FastMath.abs(fDet) <= 0f) {
+ return zero();
+ }
+
+ float f00 = +m11 * fB5 - m12 * fB4 + m13 * fB3;
+ float f10 = -m10 * fB5 + m12 * fB2 - m13 * fB1;
+ float f20 = +m10 * fB4 - m11 * fB2 + m13 * fB0;
+ float f30 = -m10 * fB3 + m11 * fB1 - m12 * fB0;
+ float f01 = -m01 * fB5 + m02 * fB4 - m03 * fB3;
+ float f11 = +m00 * fB5 - m02 * fB2 + m03 * fB1;
+ float f21 = -m00 * fB4 + m01 * fB2 - m03 * fB0;
+ float f31 = +m00 * fB3 - m01 * fB1 + m02 * fB0;
+ float f02 = +m31 * fA5 - m32 * fA4 + m33 * fA3;
+ float f12 = -m30 * fA5 + m32 * fA2 - m33 * fA1;
+ float f22 = +m30 * fA4 - m31 * fA2 + m33 * fA0;
+ float f32 = -m30 * fA3 + m31 * fA1 - m32 * fA0;
+ float f03 = -m21 * fA5 + m22 * fA4 - m23 * fA3;
+ float f13 = +m20 * fA5 - m22 * fA2 + m23 * fA1;
+ float f23 = -m20 * fA4 + m21 * fA2 - m23 * fA0;
+ float f33 = +m20 * fA3 - m21 * fA1 + m22 * fA0;
+
+ m00 = f00;
+ m01 = f01;
+ m02 = f02;
+ m03 = f03;
+ m10 = f10;
+ m11 = f11;
+ m12 = f12;
+ m13 = f13;
+ m20 = f20;
+ m21 = f21;
+ m22 = f22;
+ m23 = f23;
+ m30 = f30;
+ m31 = f31;
+ m32 = f32;
+ m33 = f33;
+
+ float fInvDet = 1.0f / fDet;
+ multLocal(fInvDet);
+
+ return this;
+ }
+
+ /**
+ * Returns a new matrix representing the adjoint of this matrix.
+ *
+ * @return The adjoint matrix
+ */
+ public Matrix4f adjoint() {
+ return adjoint(null);
+ }
+
+ public void setTransform(Vector3f position, Vector3f scale, Matrix3f rotMat) {
+ // Ordering:
+ // 1. Scale
+ // 2. Rotate
+ // 3. Translate
+
+ // Set up final matrix with scale, rotation and translation
+ m00 = scale.x * rotMat.m00;
+ m01 = scale.y * rotMat.m01;
+ m02 = scale.z * rotMat.m02;
+ m03 = position.x;
+ m10 = scale.x * rotMat.m10;
+ m11 = scale.y * rotMat.m11;
+ m12 = scale.z * rotMat.m12;
+ m13 = position.y;
+ m20 = scale.x * rotMat.m20;
+ m21 = scale.y * rotMat.m21;
+ m22 = scale.z * rotMat.m22;
+ m23 = position.z;
+
+ // No projection term
+ m30 = 0;
+ m31 = 0;
+ m32 = 0;
+ m33 = 1;
+ }
+
+ /**
+ * Places the adjoint of this matrix in store (creates store if null.)
+ *
+ * @param store
+ * The matrix to store the result in. If null, a new matrix is created.
+ * @return store
+ */
+ public Matrix4f adjoint(Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ }
+
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+
+ store.m00 = +m11 * fB5 - m12 * fB4 + m13 * fB3;
+ store.m10 = -m10 * fB5 + m12 * fB2 - m13 * fB1;
+ store.m20 = +m10 * fB4 - m11 * fB2 + m13 * fB0;
+ store.m30 = -m10 * fB3 + m11 * fB1 - m12 * fB0;
+ store.m01 = -m01 * fB5 + m02 * fB4 - m03 * fB3;
+ store.m11 = +m00 * fB5 - m02 * fB2 + m03 * fB1;
+ store.m21 = -m00 * fB4 + m01 * fB2 - m03 * fB0;
+ store.m31 = +m00 * fB3 - m01 * fB1 + m02 * fB0;
+ store.m02 = +m31 * fA5 - m32 * fA4 + m33 * fA3;
+ store.m12 = -m30 * fA5 + m32 * fA2 - m33 * fA1;
+ store.m22 = +m30 * fA4 - m31 * fA2 + m33 * fA0;
+ store.m32 = -m30 * fA3 + m31 * fA1 - m32 * fA0;
+ store.m03 = -m21 * fA5 + m22 * fA4 - m23 * fA3;
+ store.m13 = +m20 * fA5 - m22 * fA2 + m23 * fA1;
+ store.m23 = -m20 * fA4 + m21 * fA2 - m23 * fA0;
+ store.m33 = +m20 * fA3 - m21 * fA1 + m22 * fA0;
+
+ return store;
+ }
+
+ /**
+ * <code>determinant</code> generates the determinate of this matrix.
+ *
+ * @return the determinate
+ */
+ public float determinant() {
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+ float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+ return fDet;
+ }
+
+ /**
+ * Sets all of the values in this matrix to zero.
+ *
+ * @return this matrix
+ */
+ public Matrix4f zero() {
+ m00 = m01 = m02 = m03 = 0.0f;
+ m10 = m11 = m12 = m13 = 0.0f;
+ m20 = m21 = m22 = m23 = 0.0f;
+ m30 = m31 = m32 = m33 = 0.0f;
+ return this;
+ }
+
+ public Matrix4f add(Matrix4f mat) {
+ Matrix4f result = new Matrix4f();
+ result.m00 = this.m00 + mat.m00;
+ result.m01 = this.m01 + mat.m01;
+ result.m02 = this.m02 + mat.m02;
+ result.m03 = this.m03 + mat.m03;
+ result.m10 = this.m10 + mat.m10;
+ result.m11 = this.m11 + mat.m11;
+ result.m12 = this.m12 + mat.m12;
+ result.m13 = this.m13 + mat.m13;
+ result.m20 = this.m20 + mat.m20;
+ result.m21 = this.m21 + mat.m21;
+ result.m22 = this.m22 + mat.m22;
+ result.m23 = this.m23 + mat.m23;
+ result.m30 = this.m30 + mat.m30;
+ result.m31 = this.m31 + mat.m31;
+ result.m32 = this.m32 + mat.m32;
+ result.m33 = this.m33 + mat.m33;
+ return result;
+ }
+
+ /**
+ * <code>add</code> adds the values of a parameter matrix to this matrix.
+ *
+ * @param mat
+ * the matrix to add to this.
+ */
+ public void addLocal(Matrix4f mat) {
+ m00 += mat.m00;
+ m01 += mat.m01;
+ m02 += mat.m02;
+ m03 += mat.m03;
+ m10 += mat.m10;
+ m11 += mat.m11;
+ m12 += mat.m12;
+ m13 += mat.m13;
+ m20 += mat.m20;
+ m21 += mat.m21;
+ m22 += mat.m22;
+ m23 += mat.m23;
+ m30 += mat.m30;
+ m31 += mat.m31;
+ m32 += mat.m32;
+ m33 += mat.m33;
+ }
+
+ public Vector3f toTranslationVector() {
+ return new Vector3f(m03, m13, m23);
+ }
+
+ public void toTranslationVector(Vector3f vector) {
+ vector.set(m03, m13, m23);
+ }
+
+ public Quaternion toRotationQuat() {
+ Quaternion quat = new Quaternion();
+ quat.fromRotationMatrix(toRotationMatrix());
+ return quat;
+ }
+
+ public void toRotationQuat(Quaternion q) {
+ q.fromRotationMatrix(toRotationMatrix());
+ }
+
+ public Matrix3f toRotationMatrix() {
+ return new Matrix3f(m00, m01, m02, m10, m11, m12, m20, m21, m22);
+
+ }
+
+ public void toRotationMatrix(Matrix3f mat) {
+ mat.m00 = m00;
+ mat.m01 = m01;
+ mat.m02 = m02;
+ mat.m10 = m10;
+ mat.m11 = m11;
+ mat.m12 = m12;
+ mat.m20 = m20;
+ mat.m21 = m21;
+ mat.m22 = m22;
+
+ }
+
+ public void setScale(float x, float y, float z) {
+ m00 *= x;
+ m11 *= y;
+ m22 *= z;
+ }
+
+ public void setScale(Vector3f scale) {
+ m00 *= scale.x;
+ m11 *= scale.y;
+ m22 *= scale.z;
+ }
+
+ /**
+ * <code>setTranslation</code> will set the matrix's translation values.
+ *
+ * @param translation
+ * the new values for the translation.
+ * @throws JmeException
+ * if translation is not size 3.
+ */
+ public void setTranslation(float[] translation) {
+ if (translation.length != 3) {
+ throw new IllegalArgumentException(
+ "Translation size must be 3.");
+ }
+ m03 = translation[0];
+ m13 = translation[1];
+ m23 = translation[2];
+ }
+
+ /**
+ * <code>setTranslation</code> will set the matrix's translation values.
+ *
+ * @param x
+ * value of the translation on the x axis
+ * @param y
+ * value of the translation on the y axis
+ * @param z
+ * value of the translation on the z axis
+ */
+ public void setTranslation(float x, float y, float z) {
+ m03 = x;
+ m13 = y;
+ m23 = z;
+ }
+
+ /**
+ * <code>setTranslation</code> will set the matrix's translation values.
+ *
+ * @param translation
+ * the new values for the translation.
+ */
+ public void setTranslation(Vector3f translation) {
+ m03 = translation.x;
+ m13 = translation.y;
+ m23 = translation.z;
+ }
+
+ /**
+ * <code>setInverseTranslation</code> will set the matrix's inverse
+ * translation values.
+ *
+ * @param translation
+ * the new values for the inverse translation.
+ * @throws JmeException
+ * if translation is not size 3.
+ */
+ public void setInverseTranslation(float[] translation) {
+ if (translation.length != 3) {
+ throw new IllegalArgumentException(
+ "Translation size must be 3.");
+ }
+ m03 = -translation[0];
+ m13 = -translation[1];
+ m23 = -translation[2];
+ }
+
+ /**
+ * <code>angleRotation</code> sets this matrix to that of a rotation about
+ * three axes (x, y, z). Where each axis has a specified rotation in
+ * degrees. These rotations are expressed in a single <code>Vector3f</code>
+ * object.
+ *
+ * @param angles
+ * the angles to rotate.
+ */
+ public void angleRotation(Vector3f angles) {
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+
+ angle = (angles.z * FastMath.DEG_TO_RAD);
+ sy = FastMath.sin(angle);
+ cy = FastMath.cos(angle);
+ angle = (angles.y * FastMath.DEG_TO_RAD);
+ sp = FastMath.sin(angle);
+ cp = FastMath.cos(angle);
+ angle = (angles.x * FastMath.DEG_TO_RAD);
+ sr = FastMath.sin(angle);
+ cr = FastMath.cos(angle);
+
+ // matrix = (Z * Y) * X
+ m00 = cp * cy;
+ m10 = cp * sy;
+ m20 = -sp;
+ m01 = sr * sp * cy + cr * -sy;
+ m11 = sr * sp * sy + cr * cy;
+ m21 = sr * cp;
+ m02 = (cr * sp * cy + -sr * -sy);
+ m12 = (cr * sp * sy + -sr * cy);
+ m22 = cr * cp;
+ m03 = 0.0f;
+ m13 = 0.0f;
+ m23 = 0.0f;
+ }
+
+ /**
+ * <code>setRotationQuaternion</code> builds a rotation from a
+ * <code>Quaternion</code>.
+ *
+ * @param quat
+ * the quaternion to build the rotation from.
+ * @throws NullPointerException
+ * if quat is null.
+ */
+ public void setRotationQuaternion(Quaternion quat) {
+ quat.toRotationMatrix(this);
+ }
+
+ /**
+ * <code>setInverseRotationRadians</code> builds an inverted rotation from
+ * Euler angles that are in radians.
+ *
+ * @param angles
+ * the Euler angles in radians.
+ * @throws JmeException
+ * if angles is not size 3.
+ */
+ public void setInverseRotationRadians(float[] angles) {
+ if (angles.length != 3) {
+ throw new IllegalArgumentException(
+ "Angles must be of size 3.");
+ }
+ double cr = FastMath.cos(angles[0]);
+ double sr = FastMath.sin(angles[0]);
+ double cp = FastMath.cos(angles[1]);
+ double sp = FastMath.sin(angles[1]);
+ double cy = FastMath.cos(angles[2]);
+ double sy = FastMath.sin(angles[2]);
+
+ m00 = (float) (cp * cy);
+ m10 = (float) (cp * sy);
+ m20 = (float) (-sp);
+
+ double srsp = sr * sp;
+ double crsp = cr * sp;
+
+ m01 = (float) (srsp * cy - cr * sy);
+ m11 = (float) (srsp * sy + cr * cy);
+ m21 = (float) (sr * cp);
+
+ m02 = (float) (crsp * cy + sr * sy);
+ m12 = (float) (crsp * sy - sr * cy);
+ m22 = (float) (cr * cp);
+ }
+
+ /**
+ * <code>setInverseRotationDegrees</code> builds an inverted rotation from
+ * Euler angles that are in degrees.
+ *
+ * @param angles
+ * the Euler angles in degrees.
+ * @throws JmeException
+ * if angles is not size 3.
+ */
+ public void setInverseRotationDegrees(float[] angles) {
+ if (angles.length != 3) {
+ throw new IllegalArgumentException(
+ "Angles must be of size 3.");
+ }
+ float vec[] = new float[3];
+ vec[0] = (angles[0] * FastMath.RAD_TO_DEG);
+ vec[1] = (angles[1] * FastMath.RAD_TO_DEG);
+ vec[2] = (angles[2] * FastMath.RAD_TO_DEG);
+ setInverseRotationRadians(vec);
+ }
+
+ /**
+ *
+ * <code>inverseTranslateVect</code> translates a given Vector3f by the
+ * translation part of this matrix.
+ *
+ * @param vec
+ * the Vector3f data to be translated.
+ * @throws JmeException
+ * if the size of the Vector3f is not 3.
+ */
+ public void inverseTranslateVect(float[] vec) {
+ if (vec.length != 3) {
+ throw new IllegalArgumentException(
+ "vec must be of size 3.");
+ }
+
+ vec[0] = vec[0] - m03;
+ vec[1] = vec[1] - m13;
+ vec[2] = vec[2] - m23;
+ }
+
+ /**
+ *
+ * <code>inverseTranslateVect</code> translates a given Vector3f by the
+ * translation part of this matrix.
+ *
+ * @param data
+ * the Vector3f to be translated.
+ * @throws JmeException
+ * if the size of the Vector3f is not 3.
+ */
+ public void inverseTranslateVect(Vector3f data) {
+ data.x -= m03;
+ data.y -= m13;
+ data.z -= m23;
+ }
+
+ /**
+ *
+ * <code>inverseTranslateVect</code> translates a given Vector3f by the
+ * translation part of this matrix.
+ *
+ * @param data
+ * the Vector3f to be translated.
+ * @throws JmeException
+ * if the size of the Vector3f is not 3.
+ */
+ public void translateVect(Vector3f data) {
+ data.x += m03;
+ data.y += m13;
+ data.z += m23;
+ }
+
+ /**
+ *
+ * <code>inverseRotateVect</code> rotates a given Vector3f by the rotation
+ * part of this matrix.
+ *
+ * @param vec
+ * the Vector3f to be rotated.
+ */
+ public void inverseRotateVect(Vector3f vec) {
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+
+ vec.x = vx * m00 + vy * m10 + vz * m20;
+ vec.y = vx * m01 + vy * m11 + vz * m21;
+ vec.z = vx * m02 + vy * m12 + vz * m22;
+ }
+
+ public void rotateVect(Vector3f vec) {
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+
+ vec.x = vx * m00 + vy * m01 + vz * m02;
+ vec.y = vx * m10 + vy * m11 + vz * m12;
+ vec.z = vx * m20 + vy * m21 + vz * m22;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this object.
+ * It is in a format of a 4x4 matrix. For example, an identity matrix would
+ * be represented by the following string. com.jme.math.Matrix3f <br>[<br>
+ * 1.0 0.0 0.0 0.0 <br>
+ * 0.0 1.0 0.0 0.0 <br>
+ * 0.0 0.0 1.0 0.0 <br>
+ * 0.0 0.0 0.0 1.0 <br>]<br>
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("Matrix4f\n[\n");
+ result.append(" ");
+ result.append(m00);
+ result.append(" ");
+ result.append(m01);
+ result.append(" ");
+ result.append(m02);
+ result.append(" ");
+ result.append(m03);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m10);
+ result.append(" ");
+ result.append(m11);
+ result.append(" ");
+ result.append(m12);
+ result.append(" ");
+ result.append(m13);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m20);
+ result.append(" ");
+ result.append(m21);
+ result.append(" ");
+ result.append(m22);
+ result.append(" ");
+ result.append(m23);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m30);
+ result.append(" ");
+ result.append(m31);
+ result.append(" ");
+ result.append(m32);
+ result.append(" ");
+ result.append(m33);
+ result.append(" \n]");
+ return result.toString();
+ }
+
+ /**
+ *
+ * <code>hashCode</code> returns the hash code value as an integer and is
+ * supported for the benefit of hashing based collection classes such as
+ * Hashtable, HashMap, HashSet etc.
+ *
+ * @return the hashcode for this instance of Matrix4f.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + Float.floatToIntBits(m00);
+ hash = 37 * hash + Float.floatToIntBits(m01);
+ hash = 37 * hash + Float.floatToIntBits(m02);
+ hash = 37 * hash + Float.floatToIntBits(m03);
+
+ hash = 37 * hash + Float.floatToIntBits(m10);
+ hash = 37 * hash + Float.floatToIntBits(m11);
+ hash = 37 * hash + Float.floatToIntBits(m12);
+ hash = 37 * hash + Float.floatToIntBits(m13);
+
+ hash = 37 * hash + Float.floatToIntBits(m20);
+ hash = 37 * hash + Float.floatToIntBits(m21);
+ hash = 37 * hash + Float.floatToIntBits(m22);
+ hash = 37 * hash + Float.floatToIntBits(m23);
+
+ hash = 37 * hash + Float.floatToIntBits(m30);
+ hash = 37 * hash + Float.floatToIntBits(m31);
+ hash = 37 * hash + Float.floatToIntBits(m32);
+ hash = 37 * hash + Float.floatToIntBits(m33);
+
+ return hash;
+ }
+
+ /**
+ * are these two matrices the same? they are is they both have the same mXX values.
+ *
+ * @param o
+ * the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Matrix4f) || o == null) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Matrix4f comp = (Matrix4f) o;
+ if (Float.compare(m00, comp.m00) != 0) {
+ return false;
+ }
+ if (Float.compare(m01, comp.m01) != 0) {
+ return false;
+ }
+ if (Float.compare(m02, comp.m02) != 0) {
+ return false;
+ }
+ if (Float.compare(m03, comp.m03) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m10, comp.m10) != 0) {
+ return false;
+ }
+ if (Float.compare(m11, comp.m11) != 0) {
+ return false;
+ }
+ if (Float.compare(m12, comp.m12) != 0) {
+ return false;
+ }
+ if (Float.compare(m13, comp.m13) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m20, comp.m20) != 0) {
+ return false;
+ }
+ if (Float.compare(m21, comp.m21) != 0) {
+ return false;
+ }
+ if (Float.compare(m22, comp.m22) != 0) {
+ return false;
+ }
+ if (Float.compare(m23, comp.m23) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m30, comp.m30) != 0) {
+ return false;
+ }
+ if (Float.compare(m31, comp.m31) != 0) {
+ return false;
+ }
+ if (Float.compare(m32, comp.m32) != 0) {
+ return false;
+ }
+ if (Float.compare(m33, comp.m33) != 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule cap = e.getCapsule(this);
+ cap.write(m00, "m00", 1);
+ cap.write(m01, "m01", 0);
+ cap.write(m02, "m02", 0);
+ cap.write(m03, "m03", 0);
+ cap.write(m10, "m10", 0);
+ cap.write(m11, "m11", 1);
+ cap.write(m12, "m12", 0);
+ cap.write(m13, "m13", 0);
+ cap.write(m20, "m20", 0);
+ cap.write(m21, "m21", 0);
+ cap.write(m22, "m22", 1);
+ cap.write(m23, "m23", 0);
+ cap.write(m30, "m30", 0);
+ cap.write(m31, "m31", 0);
+ cap.write(m32, "m32", 0);
+ cap.write(m33, "m33", 1);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule cap = e.getCapsule(this);
+ m00 = cap.readFloat("m00", 1);
+ m01 = cap.readFloat("m01", 0);
+ m02 = cap.readFloat("m02", 0);
+ m03 = cap.readFloat("m03", 0);
+ m10 = cap.readFloat("m10", 0);
+ m11 = cap.readFloat("m11", 1);
+ m12 = cap.readFloat("m12", 0);
+ m13 = cap.readFloat("m13", 0);
+ m20 = cap.readFloat("m20", 0);
+ m21 = cap.readFloat("m21", 0);
+ m22 = cap.readFloat("m22", 1);
+ m23 = cap.readFloat("m23", 0);
+ m30 = cap.readFloat("m30", 0);
+ m31 = cap.readFloat("m31", 0);
+ m32 = cap.readFloat("m32", 0);
+ m33 = cap.readFloat("m33", 1);
+ }
+
+ /**
+ * @return true if this matrix is identity
+ */
+ public boolean isIdentity() {
+ return (m00 == 1 && m01 == 0 && m02 == 0 && m03 == 0)
+ && (m10 == 0 && m11 == 1 && m12 == 0 && m13 == 0)
+ && (m20 == 0 && m21 == 0 && m22 == 1 && m23 == 0)
+ && (m30 == 0 && m31 == 0 && m32 == 0 && m33 == 1);
+ }
+
+ /**
+ * Apply a scale to this matrix.
+ *
+ * @param scale
+ * the scale to apply
+ */
+ public void scale(Vector3f scale) {
+ m00 *= scale.getX();
+ m10 *= scale.getX();
+ m20 *= scale.getX();
+ m30 *= scale.getX();
+ m01 *= scale.getY();
+ m11 *= scale.getY();
+ m21 *= scale.getY();
+ m31 *= scale.getY();
+ m02 *= scale.getZ();
+ m12 *= scale.getZ();
+ m22 *= scale.getZ();
+ m32 *= scale.getZ();
+ }
+
+ static boolean equalIdentity(Matrix4f mat) {
+ if (Math.abs(mat.m00 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m11 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m22 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m33 - 1) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m01) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m02) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m03) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m10) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m12) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m13) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m20) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m21) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m23) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m30) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m31) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m32) > 1e-4) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // XXX: This tests more solid than converting the q to a matrix and multiplying... why?
+ public void multLocal(Quaternion rotation) {
+ Vector3f axis = new Vector3f();
+ float angle = rotation.toAngleAxis(axis);
+ Matrix4f matrix4f = new Matrix4f();
+ matrix4f.fromAngleAxis(angle, axis);
+ multLocal(matrix4f);
+ }
+
+ @Override
+ public Matrix4f clone() {
+ try {
+ return (Matrix4f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Plane.java b/engine/src/core/com/jme3/math/Plane.java
new file mode 100644
index 0000000..e14e645
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Plane.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * <code>Plane</code> defines a plane where Normal dot (x,y,z) = Constant.
+ * This provides methods for calculating a "distance" of a point from this
+ * plane. The distance is pseudo due to the fact that it can be negative if the
+ * point is on the non-normal side of the plane.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Plane implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger
+ .getLogger(Plane.class.getName());
+
+ public static enum Side {
+ None,
+ Positive,
+ Negative
+ }
+
+ /**
+ * Vector normal to the plane.
+ */
+ protected Vector3f normal = new Vector3f();
+
+ /**
+ * Constant of the plane. See formula in class definition.
+ */
+ protected float constant;
+
+ /**
+ * Constructor instantiates a new <code>Plane</code> object. This is the
+ * default object and contains a normal of (0,0,0) and a constant of 0.
+ */
+ public Plane() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>Plane</code> object. The normal
+ * and constant values are set at creation.
+ *
+ * @param normal
+ * the normal of the plane.
+ * @param constant
+ * the constant of the plane.
+ */
+ public Plane(Vector3f normal, float constant) {
+ if (normal == null) {
+ throw new IllegalArgumentException("normal cannot be null");
+ }
+
+ this.normal.set(normal);
+ this.constant = constant;
+ }
+
+ /**
+ * <code>setNormal</code> sets the normal of the plane.
+ *
+ * @param normal
+ * the new normal of the plane.
+ */
+ public void setNormal(Vector3f normal) {
+ if (normal == null) {
+ throw new IllegalArgumentException("normal cannot be null");
+ }
+ this.normal.set(normal);
+ }
+
+ /**
+ * <code>setNormal</code> sets the normal of the plane.
+ *
+ */
+ public void setNormal(float x, float y, float z) {
+ this.normal.set(x,y,z);
+ }
+
+ /**
+ * <code>getNormal</code> retrieves the normal of the plane.
+ *
+ * @return the normal of the plane.
+ */
+ public Vector3f getNormal() {
+ return normal;
+ }
+
+ /**
+ * <code>setConstant</code> sets the constant value that helps define the
+ * plane.
+ *
+ * @param constant
+ * the new constant value.
+ */
+ public void setConstant(float constant) {
+ this.constant = constant;
+ }
+
+ /**
+ * <code>getConstant</code> returns the constant of the plane.
+ *
+ * @return the constant of the plane.
+ */
+ public float getConstant() {
+ return constant;
+ }
+
+ public Vector3f getClosestPoint(Vector3f point, Vector3f store){
+// float t = constant - normal.dot(point);
+// return store.set(normal).multLocal(t).addLocal(point);
+ float t = (constant - normal.dot(point)) / normal.dot(normal);
+ return store.set(normal).multLocal(t).addLocal(point);
+ }
+
+ public Vector3f getClosestPoint(Vector3f point){
+ return getClosestPoint(point, new Vector3f());
+ }
+
+ public Vector3f reflect(Vector3f point, Vector3f store){
+ if (store == null)
+ store = new Vector3f();
+
+ float d = pseudoDistance(point);
+ store.set(normal).negateLocal().multLocal(d * 2f);
+ store.addLocal(point);
+ return store;
+ }
+
+ /**
+ * <code>pseudoDistance</code> calculates the distance from this plane to
+ * a provided point. If the point is on the negative side of the plane the
+ * distance returned is negative, otherwise it is positive. If the point is
+ * on the plane, it is zero.
+ *
+ * @param point
+ * the point to check.
+ * @return the signed distance from the plane to a point.
+ */
+ public float pseudoDistance(Vector3f point) {
+ return normal.dot(point) - constant;
+ }
+
+ /**
+ * <code>whichSide</code> returns the side at which a point lies on the
+ * plane. The positive values returned are: NEGATIVE_SIDE, POSITIVE_SIDE and
+ * NO_SIDE.
+ *
+ * @param point
+ * the point to check.
+ * @return the side at which the point lies.
+ */
+ public Side whichSide(Vector3f point) {
+ float dis = pseudoDistance(point);
+ if (dis < 0) {
+ return Side.Negative;
+ } else if (dis > 0) {
+ return Side.Positive;
+ } else {
+ return Side.None;
+ }
+ }
+
+ public boolean isOnPlane(Vector3f point){
+ float dist = pseudoDistance(point);
+ if (dist < FastMath.FLT_EPSILON && dist > -FastMath.FLT_EPSILON)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Initialize this plane using the three points of the given triangle.
+ *
+ * @param t
+ * the triangle
+ */
+ public void setPlanePoints(AbstractTriangle t) {
+ setPlanePoints(t.get1(), t.get2(), t.get3());
+ }
+
+ /**
+ * Initialize this plane using a point of origin and a normal.
+ *
+ * @param origin
+ * @param normal
+ */
+ public void setOriginNormal(Vector3f origin, Vector3f normal){
+ this.normal.set(normal);
+ this.constant = normal.x * origin.x + normal.y * origin.y + normal.z * origin.z;
+ }
+
+ /**
+ * Initialize the Plane using the given 3 points as coplanar.
+ *
+ * @param v1
+ * the first point
+ * @param v2
+ * the second point
+ * @param v3
+ * the third point
+ */
+ public void setPlanePoints(Vector3f v1, Vector3f v2, Vector3f v3) {
+ normal.set(v2).subtractLocal(v1);
+ normal.crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z)
+ .normalizeLocal();
+ constant = normal.dot(v1);
+ }
+
+ /**
+ * <code>toString</code> returns a string thta represents the string
+ * representation of this plane. It represents the normal as a
+ * <code>Vector3f</code> object, so the format is the following:
+ * com.jme.math.Plane [Normal: org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY,
+ * Z=ZZ.ZZZZ] - Constant: CC.CCCCC]
+ *
+ * @return the string representation of this plane.
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [Normal: " + normal + " - Constant: "
+ + constant + "]";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(normal, "normal", Vector3f.ZERO);
+ capsule.write(constant, "constant", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ normal = (Vector3f) capsule.readSavable("normal", Vector3f.ZERO.clone());
+ constant = capsule.readFloat("constant", 0);
+ }
+
+ @Override
+ public Plane clone() {
+ try {
+ Plane p = (Plane) super.clone();
+ p.normal = normal.clone();
+ return p;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Quaternion.java b/engine/src/core/com/jme3/math/Quaternion.java
new file mode 100644
index 0000000..5a5a1c9
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Quaternion.java
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.logging.Logger;
+
+/**
+ * <code>Quaternion</code> defines a single example of a more general class of
+ * hypercomplex numbers. Quaternions extends a rotation in three dimensions to a
+ * rotation in four dimensions. This avoids "gimbal lock" and allows for smooth
+ * continuous rotation.
+ *
+ * <code>Quaternion</code> is defined by four floating point numbers: {x y z
+ * w}.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Quaternion implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Quaternion.class.getName());
+ /**
+ * Represents the identity quaternion rotation (0, 0, 0, 1).
+ */
+ public static final Quaternion IDENTITY = new Quaternion();
+ public static final Quaternion DIRECTION_Z = new Quaternion();
+ public static final Quaternion ZERO = new Quaternion(0, 0, 0, 0);
+
+ static {
+ DIRECTION_Z.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
+ }
+ protected float x, y, z, w;
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> object
+ * initializing all values to zero, except w which is initialized to 1.
+ *
+ */
+ public Quaternion() {
+ x = 0;
+ y = 0;
+ z = 0;
+ w = 1;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> object from the
+ * given list of parameters.
+ *
+ * @param x
+ * the x value of the quaternion.
+ * @param y
+ * the y value of the quaternion.
+ * @param z
+ * the z value of the quaternion.
+ * @param w
+ * the w value of the quaternion.
+ */
+ public Quaternion(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public float getW() {
+ return w;
+ }
+
+ /**
+ * sets the data in a <code>Quaternion</code> object from the given list
+ * of parameters.
+ *
+ * @param x
+ * the x value of the quaternion.
+ * @param y
+ * the y value of the quaternion.
+ * @param z
+ * the z value of the quaternion.
+ * @param w
+ * the w value of the quaternion.
+ * @return this
+ */
+ public Quaternion set(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ return this;
+ }
+
+ /**
+ * Sets the data in this <code>Quaternion</code> object to be equal to the
+ * passed <code>Quaternion</code> object. The values are copied producing
+ * a new object.
+ *
+ * @param q
+ * The Quaternion to copy values from.
+ * @return this
+ */
+ public Quaternion set(Quaternion q) {
+ this.x = q.x;
+ this.y = q.y;
+ this.z = q.z;
+ this.w = q.w;
+ return this;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> object from a
+ * collection of rotation angles.
+ *
+ * @param angles
+ * the angles of rotation (x, y, z) that will define the
+ * <code>Quaternion</code>.
+ */
+ public Quaternion(float[] angles) {
+ fromAngles(angles);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> object from an
+ * interpolation between two other quaternions.
+ *
+ * @param q1
+ * the first quaternion.
+ * @param q2
+ * the second quaternion.
+ * @param interp
+ * the amount to interpolate between the two quaternions.
+ */
+ public Quaternion(Quaternion q1, Quaternion q2, float interp) {
+ slerp(q1, q2, interp);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> object from an
+ * existing quaternion, creating a copy.
+ *
+ * @param q
+ * the quaternion to copy.
+ */
+ public Quaternion(Quaternion q) {
+ this.x = q.x;
+ this.y = q.y;
+ this.z = q.z;
+ this.w = q.w;
+ }
+
+ /**
+ * Sets this Quaternion to {0, 0, 0, 1}. Same as calling set(0,0,0,1).
+ */
+ public void loadIdentity() {
+ x = y = z = 0;
+ w = 1;
+ }
+
+ /**
+ * @return true if this Quaternion is {0,0,0,1}
+ */
+ public boolean isIdentity() {
+ if (x == 0 && y == 0 && z == 0 && w == 1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * <code>fromAngles</code> builds a quaternion from the Euler rotation
+ * angles (y,r,p).
+ *
+ * @param angles
+ * the Euler angles of rotation (in radians).
+ */
+ public Quaternion fromAngles(float[] angles) {
+ if (angles.length != 3) {
+ throw new IllegalArgumentException(
+ "Angles array must have three elements");
+ }
+
+ return fromAngles(angles[0], angles[1], angles[2]);
+ }
+
+ /**
+ * <code>fromAngles</code> builds a Quaternion from the Euler rotation
+ * angles (y,r,p). Note that we are applying in order: roll, pitch, yaw but
+ * we've ordered them in x, y, and z for convenience.
+ * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm</a>
+ *
+ * @param yaw
+ * the Euler yaw of rotation (in radians). (aka Bank, often rot
+ * around x)
+ * @param roll
+ * the Euler roll of rotation (in radians). (aka Heading, often
+ * rot around y)
+ * @param pitch
+ * the Euler pitch of rotation (in radians). (aka Attitude, often
+ * rot around z)
+ */
+ public Quaternion fromAngles(float yaw, float roll, float pitch) {
+ float angle;
+ float sinRoll, sinPitch, sinYaw, cosRoll, cosPitch, cosYaw;
+ angle = pitch * 0.5f;
+ sinPitch = FastMath.sin(angle);
+ cosPitch = FastMath.cos(angle);
+ angle = roll * 0.5f;
+ sinRoll = FastMath.sin(angle);
+ cosRoll = FastMath.cos(angle);
+ angle = yaw * 0.5f;
+ sinYaw = FastMath.sin(angle);
+ cosYaw = FastMath.cos(angle);
+
+ // variables used to reduce multiplication calls.
+ float cosRollXcosPitch = cosRoll * cosPitch;
+ float sinRollXsinPitch = sinRoll * sinPitch;
+ float cosRollXsinPitch = cosRoll * sinPitch;
+ float sinRollXcosPitch = sinRoll * cosPitch;
+
+ w = (cosRollXcosPitch * cosYaw - sinRollXsinPitch * sinYaw);
+ x = (cosRollXcosPitch * sinYaw + sinRollXsinPitch * cosYaw);
+ y = (sinRollXcosPitch * cosYaw + cosRollXsinPitch * sinYaw);
+ z = (cosRollXsinPitch * cosYaw - sinRollXcosPitch * sinYaw);
+
+ normalize();
+ return this;
+ }
+
+ /**
+ * <code>toAngles</code> returns this quaternion converted to Euler
+ * rotation angles (yaw,roll,pitch).<br/>
+ * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm</a>
+ *
+ * @param angles
+ * the float[] in which the angles should be stored, or null if
+ * you want a new float[] to be created
+ * @return the float[] in which the angles are stored.
+ */
+ public float[] toAngles(float[] angles) {
+ if (angles == null) {
+ angles = new float[3];
+ } else if (angles.length != 3) {
+ throw new IllegalArgumentException("Angles array must have three elements");
+ }
+
+ float sqw = w * w;
+ float sqx = x * x;
+ float sqy = y * y;
+ float sqz = z * z;
+ float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise
+ // is correction factor
+ float test = x * y + z * w;
+ if (test > 0.499 * unit) { // singularity at north pole
+ angles[1] = 2 * FastMath.atan2(x, w);
+ angles[2] = FastMath.HALF_PI;
+ angles[0] = 0;
+ } else if (test < -0.499 * unit) { // singularity at south pole
+ angles[1] = -2 * FastMath.atan2(x, w);
+ angles[2] = -FastMath.HALF_PI;
+ angles[0] = 0;
+ } else {
+ angles[1] = FastMath.atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + sqw); // roll or heading
+ angles[2] = FastMath.asin(2 * test / unit); // pitch or attitude
+ angles[0] = FastMath.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); // yaw or bank
+ }
+ return angles;
+ }
+
+ /**
+ *
+ * <code>fromRotationMatrix</code> generates a quaternion from a supplied
+ * matrix. This matrix is assumed to be a rotational matrix.
+ *
+ * @param matrix
+ * the matrix that defines the rotation.
+ */
+ public Quaternion fromRotationMatrix(Matrix3f matrix) {
+ return fromRotationMatrix(matrix.m00, matrix.m01, matrix.m02, matrix.m10,
+ matrix.m11, matrix.m12, matrix.m20, matrix.m21, matrix.m22);
+ }
+
+ public Quaternion fromRotationMatrix(float m00, float m01, float m02,
+ float m10, float m11, float m12,
+ float m20, float m21, float m22) {
+ // Use the Graphics Gems code, from
+ // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z
+ // *NOT* the "Matrix and Quaternions FAQ", which has errors!
+
+ // the trace is the sum of the diagonal elements; see
+ // http://mathworld.wolfram.com/MatrixTrace.html
+ float t = m00 + m11 + m22;
+
+ // we protect the division by s by ensuring that s>=1
+ if (t >= 0) { // |w| >= .5
+ float s = FastMath.sqrt(t + 1); // |s|>=1 ...
+ w = 0.5f * s;
+ s = 0.5f / s; // so this division isn't bad
+ x = (m21 - m12) * s;
+ y = (m02 - m20) * s;
+ z = (m10 - m01) * s;
+ } else if ((m00 > m11) && (m00 > m22)) {
+ float s = FastMath.sqrt(1.0f + m00 - m11 - m22); // |s|>=1
+ x = s * 0.5f; // |x| >= .5
+ s = 0.5f / s;
+ y = (m10 + m01) * s;
+ z = (m02 + m20) * s;
+ w = (m21 - m12) * s;
+ } else if (m11 > m22) {
+ float s = FastMath.sqrt(1.0f + m11 - m00 - m22); // |s|>=1
+ y = s * 0.5f; // |y| >= .5
+ s = 0.5f / s;
+ x = (m10 + m01) * s;
+ z = (m21 + m12) * s;
+ w = (m02 - m20) * s;
+ } else {
+ float s = FastMath.sqrt(1.0f + m22 - m00 - m11); // |s|>=1
+ z = s * 0.5f; // |z| >= .5
+ s = 0.5f / s;
+ x = (m02 + m20) * s;
+ y = (m21 + m12) * s;
+ w = (m10 - m01) * s;
+ }
+
+ return this;
+ }
+
+ /**
+ * <code>toRotationMatrix</code> converts this quaternion to a rotational
+ * matrix. Note: the result is created from a normalized version of this quat.
+ *
+ * @return the rotation matrix representation of this quaternion.
+ */
+ public Matrix3f toRotationMatrix() {
+ Matrix3f matrix = new Matrix3f();
+ return toRotationMatrix(matrix);
+ }
+
+ /**
+ * <code>toRotationMatrix</code> converts this quaternion to a rotational
+ * matrix. The result is stored in result.
+ *
+ * @param result
+ * The Matrix3f to store the result in.
+ * @return the rotation matrix representation of this quaternion.
+ */
+ public Matrix3f toRotationMatrix(Matrix3f result) {
+
+ float norm = norm();
+ // we explicitly test norm against one here, saving a division
+ // at the cost of a test and branch. Is it worth it?
+ float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
+
+ // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
+ // will be used 2-4 times each.
+ float xs = x * s;
+ float ys = y * s;
+ float zs = z * s;
+ float xx = x * xs;
+ float xy = x * ys;
+ float xz = x * zs;
+ float xw = w * xs;
+ float yy = y * ys;
+ float yz = y * zs;
+ float yw = w * ys;
+ float zz = z * zs;
+ float zw = w * zs;
+
+ // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
+ result.m00 = 1 - (yy + zz);
+ result.m01 = (xy - zw);
+ result.m02 = (xz + yw);
+ result.m10 = (xy + zw);
+ result.m11 = 1 - (xx + zz);
+ result.m12 = (yz - xw);
+ result.m20 = (xz - yw);
+ result.m21 = (yz + xw);
+ result.m22 = 1 - (xx + yy);
+
+ return result;
+ }
+
+ /**
+ * <code>toRotationMatrix</code> converts this quaternion to a rotational
+ * matrix. The result is stored in result. 4th row and 4th column values are
+ * untouched. Note: the result is created from a normalized version of this quat.
+ *
+ * @param result
+ * The Matrix4f to store the result in.
+ * @return the rotation matrix representation of this quaternion.
+ */
+ public Matrix4f toRotationMatrix(Matrix4f result) {
+
+ float norm = norm();
+ // we explicitly test norm against one here, saving a division
+ // at the cost of a test and branch. Is it worth it?
+ float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
+
+ // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
+ // will be used 2-4 times each.
+ float xs = x * s;
+ float ys = y * s;
+ float zs = z * s;
+ float xx = x * xs;
+ float xy = x * ys;
+ float xz = x * zs;
+ float xw = w * xs;
+ float yy = y * ys;
+ float yz = y * zs;
+ float yw = w * ys;
+ float zz = z * zs;
+ float zw = w * zs;
+
+ // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
+ result.m00 = 1 - (yy + zz);
+ result.m01 = (xy - zw);
+ result.m02 = (xz + yw);
+ result.m10 = (xy + zw);
+ result.m11 = 1 - (xx + zz);
+ result.m12 = (yz - xw);
+ result.m20 = (xz - yw);
+ result.m21 = (yz + xw);
+ result.m22 = 1 - (xx + yy);
+
+ return result;
+ }
+
+ /**
+ * <code>getRotationColumn</code> returns one of three columns specified
+ * by the parameter. This column is returned as a <code>Vector3f</code>
+ * object.
+ *
+ * @param i
+ * the column to retrieve. Must be between 0 and 2.
+ * @return the column specified by the index.
+ */
+ public Vector3f getRotationColumn(int i) {
+ return getRotationColumn(i, null);
+ }
+
+ /**
+ * <code>getRotationColumn</code> returns one of three columns specified
+ * by the parameter. This column is returned as a <code>Vector3f</code>
+ * object. The value is retrieved as if this quaternion was first normalized.
+ *
+ * @param i
+ * the column to retrieve. Must be between 0 and 2.
+ * @param store
+ * the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the column specified by the index.
+ */
+ public Vector3f getRotationColumn(int i, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float norm = norm();
+ if (norm != 1.0f) {
+ norm = FastMath.invSqrt(norm);
+ }
+
+ float xx = x * x * norm;
+ float xy = x * y * norm;
+ float xz = x * z * norm;
+ float xw = x * w * norm;
+ float yy = y * y * norm;
+ float yz = y * z * norm;
+ float yw = y * w * norm;
+ float zz = z * z * norm;
+ float zw = z * w * norm;
+
+ switch (i) {
+ case 0:
+ store.x = 1 - 2 * (yy + zz);
+ store.y = 2 * (xy + zw);
+ store.z = 2 * (xz - yw);
+ break;
+ case 1:
+ store.x = 2 * (xy - zw);
+ store.y = 1 - 2 * (xx + zz);
+ store.z = 2 * (yz + xw);
+ break;
+ case 2:
+ store.x = 2 * (xz + yw);
+ store.y = 2 * (yz - xw);
+ store.z = 1 - 2 * (xx + yy);
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+
+ return store;
+ }
+
+ /**
+ * <code>fromAngleAxis</code> sets this quaternion to the values specified
+ * by an angle and an axis of rotation. This method creates an object, so
+ * use fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle
+ * the angle to rotate (in radians).
+ * @param axis
+ * the axis of rotation.
+ * @return this quaternion
+ */
+ public Quaternion fromAngleAxis(float angle, Vector3f axis) {
+ Vector3f normAxis = axis.normalize();
+ fromAngleNormalAxis(angle, normAxis);
+ return this;
+ }
+
+ /**
+ * <code>fromAngleNormalAxis</code> sets this quaternion to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle
+ * the angle to rotate (in radians).
+ * @param axis
+ * the axis of rotation (already normalized).
+ */
+ public Quaternion fromAngleNormalAxis(float angle, Vector3f axis) {
+ if (axis.x == 0 && axis.y == 0 && axis.z == 0) {
+ loadIdentity();
+ } else {
+ float halfAngle = 0.5f * angle;
+ float sin = FastMath.sin(halfAngle);
+ w = FastMath.cos(halfAngle);
+ x = sin * axis.x;
+ y = sin * axis.y;
+ z = sin * axis.z;
+ }
+ return this;
+ }
+
+ /**
+ * <code>toAngleAxis</code> sets a given angle and axis to that
+ * represented by the current quaternion. The values are stored as
+ * following: The axis is provided as a parameter and built by the method,
+ * the angle is returned as a float.
+ *
+ * @param axisStore
+ * the object we'll store the computed axis in.
+ * @return the angle of rotation in radians.
+ */
+ public float toAngleAxis(Vector3f axisStore) {
+ float sqrLength = x * x + y * y + z * z;
+ float angle;
+ if (sqrLength == 0.0f) {
+ angle = 0.0f;
+ if (axisStore != null) {
+ axisStore.x = 1.0f;
+ axisStore.y = 0.0f;
+ axisStore.z = 0.0f;
+ }
+ } else {
+ angle = (2.0f * FastMath.acos(w));
+ if (axisStore != null) {
+ float invLength = (1.0f / FastMath.sqrt(sqrLength));
+ axisStore.x = x * invLength;
+ axisStore.y = y * invLength;
+ axisStore.z = z * invLength;
+ }
+ }
+
+ return angle;
+ }
+
+ /**
+ * <code>slerp</code> sets this quaternion's value as an interpolation
+ * between two other quaternions.
+ *
+ * @param q1
+ * the first quaternion.
+ * @param q2
+ * the second quaternion.
+ * @param t
+ * the amount to interpolate between the two quaternions.
+ */
+ public Quaternion slerp(Quaternion q1, Quaternion q2, float t) {
+ // Create a local quaternion to store the interpolated quaternion
+ if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w) {
+ this.set(q1);
+ return this;
+ }
+
+ float result = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z)
+ + (q1.w * q2.w);
+
+ if (result < 0.0f) {
+ // Negate the second quaternion and the result of the dot product
+ q2.x = -q2.x;
+ q2.y = -q2.y;
+ q2.z = -q2.z;
+ q2.w = -q2.w;
+ result = -result;
+ }
+
+ // Set the first and second scale for the interpolation
+ float scale0 = 1 - t;
+ float scale1 = t;
+
+ // Check if the angle between the 2 quaternions was big enough to
+ // warrant such calculations
+ if ((1 - result) > 0.1f) {// Get the angle between the 2 quaternions,
+ // and then store the sin() of that angle
+ float theta = FastMath.acos(result);
+ float invSinTheta = 1f / FastMath.sin(theta);
+
+ // Calculate the scale for q1 and q2, according to the angle and
+ // it's sine value
+ scale0 = FastMath.sin((1 - t) * theta) * invSinTheta;
+ scale1 = FastMath.sin((t * theta)) * invSinTheta;
+ }
+
+ // Calculate the x, y, z and w values for the quaternion by using a
+ // special
+ // form of linear interpolation for quaternions.
+ this.x = (scale0 * q1.x) + (scale1 * q2.x);
+ this.y = (scale0 * q1.y) + (scale1 * q2.y);
+ this.z = (scale0 * q1.z) + (scale1 * q2.z);
+ this.w = (scale0 * q1.w) + (scale1 * q2.w);
+
+ // Return the interpolated quaternion
+ return this;
+ }
+
+ /**
+ * Sets the values of this quaternion to the slerp from itself to q2 by
+ * changeAmnt
+ *
+ * @param q2
+ * Final interpolation value
+ * @param changeAmnt
+ * The amount diffrence
+ */
+ public void slerp(Quaternion q2, float changeAmnt) {
+ if (this.x == q2.x && this.y == q2.y && this.z == q2.z
+ && this.w == q2.w) {
+ return;
+ }
+
+ float result = (this.x * q2.x) + (this.y * q2.y) + (this.z * q2.z)
+ + (this.w * q2.w);
+
+ if (result < 0.0f) {
+ // Negate the second quaternion and the result of the dot product
+ q2.x = -q2.x;
+ q2.y = -q2.y;
+ q2.z = -q2.z;
+ q2.w = -q2.w;
+ result = -result;
+ }
+
+ // Set the first and second scale for the interpolation
+ float scale0 = 1 - changeAmnt;
+ float scale1 = changeAmnt;
+
+ // Check if the angle between the 2 quaternions was big enough to
+ // warrant such calculations
+ if ((1 - result) > 0.1f) {
+ // Get the angle between the 2 quaternions, and then store the sin()
+ // of that angle
+ float theta = FastMath.acos(result);
+ float invSinTheta = 1f / FastMath.sin(theta);
+
+ // Calculate the scale for q1 and q2, according to the angle and
+ // it's sine value
+ scale0 = FastMath.sin((1 - changeAmnt) * theta) * invSinTheta;
+ scale1 = FastMath.sin((changeAmnt * theta)) * invSinTheta;
+ }
+
+ // Calculate the x, y, z and w values for the quaternion by using a
+ // special
+ // form of linear interpolation for quaternions.
+ this.x = (scale0 * this.x) + (scale1 * q2.x);
+ this.y = (scale0 * this.y) + (scale1 * q2.y);
+ this.z = (scale0 * this.z) + (scale1 * q2.z);
+ this.w = (scale0 * this.w) + (scale1 * q2.w);
+ }
+
+ /**
+ * Sets the values of this quaternion to the nlerp from itself to q2 by blend.
+ * @param q2
+ * @param blend
+ */
+ public void nlerp(Quaternion q2, float blend) {
+ float dot = dot(q2);
+ float blendI = 1.0f - blend;
+ if (dot < 0.0f) {
+ x = blendI * x - blend * q2.x;
+ y = blendI * y - blend * q2.y;
+ z = blendI * z - blend * q2.z;
+ w = blendI * w - blend * q2.w;
+ } else {
+ x = blendI * x + blend * q2.x;
+ y = blendI * y + blend * q2.y;
+ z = blendI * z + blend * q2.z;
+ w = blendI * w + blend * q2.w;
+ }
+ normalizeLocal();
+ }
+
+ /**
+ * <code>add</code> adds the values of this quaternion to those of the
+ * parameter quaternion. The result is returned as a new quaternion.
+ *
+ * @param q
+ * the quaternion to add to this.
+ * @return the new quaternion.
+ */
+ public Quaternion add(Quaternion q) {
+ return new Quaternion(x + q.x, y + q.y, z + q.z, w + q.w);
+ }
+
+ /**
+ * <code>add</code> adds the values of this quaternion to those of the
+ * parameter quaternion. The result is stored in this Quaternion.
+ *
+ * @param q
+ * the quaternion to add to this.
+ * @return This Quaternion after addition.
+ */
+ public Quaternion addLocal(Quaternion q) {
+ this.x += q.x;
+ this.y += q.y;
+ this.z += q.z;
+ this.w += q.w;
+ return this;
+ }
+
+ /**
+ * <code>subtract</code> subtracts the values of the parameter quaternion
+ * from those of this quaternion. The result is returned as a new
+ * quaternion.
+ *
+ * @param q
+ * the quaternion to subtract from this.
+ * @return the new quaternion.
+ */
+ public Quaternion subtract(Quaternion q) {
+ return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w);
+ }
+
+ /**
+ * <code>subtract</code> subtracts the values of the parameter quaternion
+ * from those of this quaternion. The result is stored in this Quaternion.
+ *
+ * @param q
+ * the quaternion to subtract from this.
+ * @return This Quaternion after subtraction.
+ */
+ public Quaternion subtractLocal(Quaternion q) {
+ this.x -= q.x;
+ this.y -= q.y;
+ this.z -= q.z;
+ this.w -= q.w;
+ return this;
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter quaternion.
+ * The result is returned as a new quaternion. It should be noted that
+ * quaternion multiplication is not commutative so q * p != p * q.
+ *
+ * @param q
+ * the quaternion to multiply this quaternion by.
+ * @return the new quaternion.
+ */
+ public Quaternion mult(Quaternion q) {
+ return mult(q, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter quaternion.
+ * The result is returned as a new quaternion. It should be noted that
+ * quaternion multiplication is not commutative so q * p != p * q.
+ *
+ * It IS safe for q and res to be the same object.
+ * It IS safe for this and res to be the same object.
+ *
+ * @param q
+ * the quaternion to multiply this quaternion by.
+ * @param res
+ * the quaternion to store the result in.
+ * @return the new quaternion.
+ */
+ public Quaternion mult(Quaternion q, Quaternion res) {
+ if (res == null) {
+ res = new Quaternion();
+ }
+ float qw = q.w, qx = q.x, qy = q.y, qz = q.z;
+ res.x = x * qw + y * qz - z * qy + w * qx;
+ res.y = -x * qz + y * qw + z * qx + w * qy;
+ res.z = x * qy - y * qx + z * qw + w * qz;
+ res.w = -x * qx - y * qy - z * qz + w * qw;
+ return res;
+ }
+
+ /**
+ * <code>apply</code> multiplies this quaternion by a parameter matrix
+ * internally.
+ *
+ * @param matrix
+ * the matrix to apply to this quaternion.
+ */
+ public void apply(Matrix3f matrix) {
+ float oldX = x, oldY = y, oldZ = z, oldW = w;
+ fromRotationMatrix(matrix);
+ float tempX = x, tempY = y, tempZ = z, tempW = w;
+
+ x = oldX * tempW + oldY * tempZ - oldZ * tempY + oldW * tempX;
+ y = -oldX * tempZ + oldY * tempW + oldZ * tempX + oldW * tempY;
+ z = oldX * tempY - oldY * tempX + oldZ * tempW + oldW * tempZ;
+ w = -oldX * tempX - oldY * tempY - oldZ * tempZ + oldW * tempW;
+ }
+
+ /**
+ *
+ * <code>fromAxes</code> creates a <code>Quaternion</code> that
+ * represents the coordinate system defined by three axes. These axes are
+ * assumed to be orthogonal and no error checking is applied. Thus, the user
+ * must insure that the three axes being provided indeed represents a proper
+ * right handed coordinate system.
+ *
+ * @param axis
+ * the array containing the three vectors representing the
+ * coordinate system.
+ */
+ public Quaternion fromAxes(Vector3f[] axis) {
+ if (axis.length != 3) {
+ throw new IllegalArgumentException(
+ "Axis array must have three elements");
+ }
+ return fromAxes(axis[0], axis[1], axis[2]);
+ }
+
+ /**
+ *
+ * <code>fromAxes</code> creates a <code>Quaternion</code> that
+ * represents the coordinate system defined by three axes. These axes are
+ * assumed to be orthogonal and no error checking is applied. Thus, the user
+ * must insure that the three axes being provided indeed represents a proper
+ * right handed coordinate system.
+ *
+ * @param xAxis vector representing the x-axis of the coordinate system.
+ * @param yAxis vector representing the y-axis of the coordinate system.
+ * @param zAxis vector representing the z-axis of the coordinate system.
+ */
+ public Quaternion fromAxes(Vector3f xAxis, Vector3f yAxis, Vector3f zAxis) {
+ return fromRotationMatrix(xAxis.x, yAxis.x, zAxis.x, xAxis.y, yAxis.y,
+ zAxis.y, xAxis.z, yAxis.z, zAxis.z);
+ }
+
+ /**
+ *
+ * <code>toAxes</code> takes in an array of three vectors. Each vector
+ * corresponds to an axis of the coordinate system defined by the quaternion
+ * rotation.
+ *
+ * @param axis
+ * the array of vectors to be filled.
+ */
+ public void toAxes(Vector3f axis[]) {
+ Matrix3f tempMat = toRotationMatrix();
+ axis[0] = tempMat.getColumn(0, axis[0]);
+ axis[1] = tempMat.getColumn(1, axis[1]);
+ axis[2] = tempMat.getColumn(2, axis[2]);
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter vector. The
+ * result is returned as a new vector.
+ *
+ * @param v
+ * the vector to multiply this quaternion by.
+ * @return the new vector.
+ */
+ public Vector3f mult(Vector3f v) {
+ return mult(v, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter vector. The
+ * result is stored in the supplied vector
+ *
+ * @param v
+ * the vector to multiply this quaternion by.
+ * @return v
+ */
+ public Vector3f multLocal(Vector3f v) {
+ float tempX, tempY;
+ tempX = w * w * v.x + 2 * y * w * v.z - 2 * z * w * v.y + x * x * v.x
+ + 2 * y * x * v.y + 2 * z * x * v.z - z * z * v.x - y * y * v.x;
+ tempY = 2 * x * y * v.x + y * y * v.y + 2 * z * y * v.z + 2 * w * z
+ * v.x - z * z * v.y + w * w * v.y - 2 * x * w * v.z - x * x
+ * v.y;
+ v.z = 2 * x * z * v.x + 2 * y * z * v.y + z * z * v.z - 2 * w * y * v.x
+ - y * y * v.z + 2 * w * x * v.y - x * x * v.z + w * w * v.z;
+ v.x = tempX;
+ v.y = tempY;
+ return v;
+ }
+
+ /**
+ * Multiplies this Quaternion by the supplied quaternion. The result is
+ * stored in this Quaternion, which is also returned for chaining. Similar
+ * to this *= q.
+ *
+ * @param q
+ * The Quaternion to multiply this one by.
+ * @return This Quaternion, after multiplication.
+ */
+ public Quaternion multLocal(Quaternion q) {
+ float x1 = x * q.w + y * q.z - z * q.y + w * q.x;
+ float y1 = -x * q.z + y * q.w + z * q.x + w * q.y;
+ float z1 = x * q.y - y * q.x + z * q.w + w * q.z;
+ w = -x * q.x - y * q.y - z * q.z + w * q.w;
+ x = x1;
+ y = y1;
+ z = z1;
+ return this;
+ }
+
+ /**
+ * Multiplies this Quaternion by the supplied quaternion. The result is
+ * stored in this Quaternion, which is also returned for chaining. Similar
+ * to this *= q.
+ *
+ * @param qx -
+ * quat x value
+ * @param qy -
+ * quat y value
+ * @param qz -
+ * quat z value
+ * @param qw -
+ * quat w value
+ *
+ * @return This Quaternion, after multiplication.
+ */
+ public Quaternion multLocal(float qx, float qy, float qz, float qw) {
+ float x1 = x * qw + y * qz - z * qy + w * qx;
+ float y1 = -x * qz + y * qw + z * qx + w * qy;
+ float z1 = x * qy - y * qx + z * qw + w * qz;
+ w = -x * qx - y * qy - z * qz + w * qw;
+ x = x1;
+ y = y1;
+ z = z1;
+ return this;
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter vector. The
+ * result is returned as a new vector.
+ *
+ * @param v
+ * the vector to multiply this quaternion by.
+ * @param store
+ * the vector to store the result in. It IS safe for v and store
+ * to be the same object.
+ * @return the result vector.
+ */
+ public Vector3f mult(Vector3f v, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ if (v.x == 0 && v.y == 0 && v.z == 0) {
+ store.set(0, 0, 0);
+ } else {
+ float vx = v.x, vy = v.y, vz = v.z;
+ store.x = w * w * vx + 2 * y * w * vz - 2 * z * w * vy + x * x
+ * vx + 2 * y * x * vy + 2 * z * x * vz - z * z * vx - y
+ * y * vx;
+ store.y = 2 * x * y * vx + y * y * vy + 2 * z * y * vz + 2 * w
+ * z * vx - z * z * vy + w * w * vy - 2 * x * w * vz - x
+ * x * vy;
+ store.z = 2 * x * z * vx + 2 * y * z * vy + z * z * vz - 2 * w
+ * y * vx - y * y * vz + 2 * w * x * vy - x * x * vz + w
+ * w * vz;
+ }
+ return store;
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter scalar. The
+ * result is returned as a new quaternion.
+ *
+ * @param scalar
+ * the quaternion to multiply this quaternion by.
+ * @return the new quaternion.
+ */
+ public Quaternion mult(float scalar) {
+ return new Quaternion(scalar * x, scalar * y, scalar * z, scalar * w);
+ }
+
+ /**
+ * <code>mult</code> multiplies this quaternion by a parameter scalar. The
+ * result is stored locally.
+ *
+ * @param scalar
+ * the quaternion to multiply this quaternion by.
+ * @return this.
+ */
+ public Quaternion multLocal(float scalar) {
+ w *= scalar;
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ /**
+ * <code>dot</code> calculates and returns the dot product of this
+ * quaternion with that of the parameter quaternion.
+ *
+ * @param q
+ * the quaternion to calculate the dot product of.
+ * @return the dot product of this and the parameter quaternion.
+ */
+ public float dot(Quaternion q) {
+ return w * q.w + x * q.x + y * q.y + z * q.z;
+ }
+
+ /**
+ * <code>norm</code> returns the norm of this quaternion. This is the dot
+ * product of this quaternion with itself.
+ *
+ * @return the norm of the quaternion.
+ */
+ public float norm() {
+ return w * w + x * x + y * y + z * z;
+ }
+
+ /**
+ * <code>normalize</code> normalizes the current <code>Quaternion</code>
+ * @deprecated The naming of this method doesn't follow convention.
+ * Please use {@link Quaternion#normalizeLocal() } instead.
+ */
+ @Deprecated
+ public void normalize() {
+ float n = FastMath.invSqrt(norm());
+ x *= n;
+ y *= n;
+ z *= n;
+ w *= n;
+ }
+
+ /**
+ * <code>normalize</code> normalizes the current <code>Quaternion</code>
+ */
+ public void normalizeLocal() {
+ float n = FastMath.invSqrt(norm());
+ x *= n;
+ y *= n;
+ z *= n;
+ w *= n;
+ }
+
+ /**
+ * <code>inverse</code> returns the inverse of this quaternion as a new
+ * quaternion. If this quaternion does not have an inverse (if its normal is
+ * 0 or less), then null is returned.
+ *
+ * @return the inverse of this quaternion or null if the inverse does not
+ * exist.
+ */
+ public Quaternion inverse() {
+ float norm = norm();
+ if (norm > 0.0) {
+ float invNorm = 1.0f / norm;
+ return new Quaternion(-x * invNorm, -y * invNorm, -z * invNorm, w
+ * invNorm);
+ }
+ // return an invalid result to flag the error
+ return null;
+ }
+
+ /**
+ * <code>inverse</code> calculates the inverse of this quaternion and
+ * returns this quaternion after it is calculated. If this quaternion does
+ * not have an inverse (if it's norma is 0 or less), nothing happens
+ *
+ * @return the inverse of this quaternion
+ */
+ public Quaternion inverseLocal() {
+ float norm = norm();
+ if (norm > 0.0) {
+ float invNorm = 1.0f / norm;
+ x *= -invNorm;
+ y *= -invNorm;
+ z *= -invNorm;
+ w *= invNorm;
+ }
+ return this;
+ }
+
+ /**
+ * <code>negate</code> inverts the values of the quaternion.
+ *
+ */
+ public void negate() {
+ x *= -1;
+ y *= -1;
+ z *= -1;
+ w *= -1;
+ }
+
+ /**
+ *
+ * <code>toString</code> creates the string representation of this
+ * <code>Quaternion</code>. The values of the quaternion are displace (x,
+ * y, z, w), in the following manner: <br>
+ * (x, y, z, w)
+ *
+ * @return the string representation of this object.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ", " + w + ")";
+ }
+
+ /**
+ * <code>equals</code> determines if two quaternions are logically equal,
+ * that is, if the values of (x, y, z, w) are the same for both quaternions.
+ *
+ * @param o
+ * the object to compare for equality
+ * @return true if they are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Quaternion)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Quaternion comp = (Quaternion) o;
+ if (Float.compare(x, comp.x) != 0) {
+ return false;
+ }
+ if (Float.compare(y, comp.y) != 0) {
+ return false;
+ }
+ if (Float.compare(z, comp.z) != 0) {
+ return false;
+ }
+ if (Float.compare(w, comp.w) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * <code>hashCode</code> returns the hash code value as an integer and is
+ * supported for the benefit of hashing based collection classes such as
+ * Hashtable, HashMap, HashSet etc.
+ *
+ * @return the hashcode for this instance of Quaternion.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + Float.floatToIntBits(x);
+ hash = 37 * hash + Float.floatToIntBits(y);
+ hash = 37 * hash + Float.floatToIntBits(z);
+ hash = 37 * hash + Float.floatToIntBits(w);
+ return hash;
+
+ }
+
+ /**
+ * <code>readExternal</code> builds a quaternion from an
+ * <code>ObjectInput</code> object. <br>
+ * NOTE: Used with serialization. Not to be called manually.
+ *
+ * @param in
+ * the ObjectInput value to read from.
+ * @throws IOException
+ * if the ObjectInput value has problems reading a float.
+ * @see java.io.Externalizable
+ */
+ public void readExternal(ObjectInput in) throws IOException {
+ x = in.readFloat();
+ y = in.readFloat();
+ z = in.readFloat();
+ w = in.readFloat();
+ }
+
+ /**
+ * <code>writeExternal</code> writes this quaternion out to a
+ * <code>ObjectOutput</code> object. NOTE: Used with serialization. Not to
+ * be called manually.
+ *
+ * @param out
+ * the object to write to.
+ * @throws IOException
+ * if writing to the ObjectOutput fails.
+ * @see java.io.Externalizable
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeFloat(x);
+ out.writeFloat(y);
+ out.writeFloat(z);
+ out.writeFloat(w);
+ }
+
+ /**
+ * <code>lookAt</code> is a convienence method for auto-setting the
+ * quaternion based on a direction and an up vector. It computes
+ * the rotation to transform the z-axis to point into 'direction'
+ * and the y-axis to 'up'.
+ *
+ * @param direction
+ * where to look at in terms of local coordinates
+ * @param up
+ * a vector indicating the local up direction.
+ * (typically {0, 1, 0} in jME.)
+ */
+ public void lookAt(Vector3f direction, Vector3f up) {
+ TempVars vars = TempVars.get();
+ vars.vect3.set(direction).normalizeLocal();
+ vars.vect1.set(up).crossLocal(direction).normalizeLocal();
+ vars.vect2.set(direction).crossLocal(vars.vect1).normalizeLocal();
+ fromAxes(vars.vect1, vars.vect2, vars.vect3);
+ vars.release();
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule cap = e.getCapsule(this);
+ cap.write(x, "x", 0);
+ cap.write(y, "y", 0);
+ cap.write(z, "z", 0);
+ cap.write(w, "w", 1);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule cap = e.getCapsule(this);
+ x = cap.readFloat("x", 0);
+ y = cap.readFloat("y", 0);
+ z = cap.readFloat("z", 0);
+ w = cap.readFloat("w", 1);
+ }
+
+ /**
+ * @return A new quaternion that describes a rotation that would point you
+ * in the exact opposite direction of this Quaternion.
+ */
+ public Quaternion opposite() {
+ return opposite(null);
+ }
+
+ /**
+ * FIXME: This seems to have singularity type issues with angle == 0, possibly others such as PI.
+ * @param store
+ * A Quaternion to store our result in. If null, a new one is
+ * created.
+ * @return The store quaternion (or a new Quaterion, if store is null) that
+ * describes a rotation that would point you in the exact opposite
+ * direction of this Quaternion.
+ */
+ public Quaternion opposite(Quaternion store) {
+ if (store == null) {
+ store = new Quaternion();
+ }
+
+ Vector3f axis = new Vector3f();
+ float angle = toAngleAxis(axis);
+
+ store.fromAngleAxis(FastMath.PI + angle, axis);
+ return store;
+ }
+
+ /**
+ * @return This Quaternion, altered to describe a rotation that would point
+ * you in the exact opposite direction of where it is pointing
+ * currently.
+ */
+ public Quaternion oppositeLocal() {
+ return opposite(this);
+ }
+
+ @Override
+ public Quaternion clone() {
+ try {
+ return (Quaternion) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Ray.java b/engine/src/core/com/jme3/math/Ray.java
new file mode 100644
index 0000000..f363613
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Ray.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * <code>Ray</code> defines a line segment which has an origin and a direction.
+ * That is, a point and an infinite ray is cast from this point. The ray is
+ * defined by the following equation: R(t) = origin + t*direction for t >= 0.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Ray implements Savable, Cloneable, Collidable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ /**
+ * The ray's begining point.
+ */
+ public Vector3f origin = new Vector3f();
+
+ /**
+ * The direction of the ray.
+ */
+ public Vector3f direction = new Vector3f(0, 0, 1);
+
+
+ public float limit = Float.POSITIVE_INFINITY;
+
+ /**
+ * Constructor instantiates a new <code>Ray</code> object. As default, the
+ * origin is (0,0,0) and the direction is (0,0,1).
+ *
+ */
+ public Ray() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>Ray</code> object. The origin and
+ * direction are given.
+ * @param origin the origin of the ray.
+ * @param direction the direction the ray travels in.
+ */
+ public Ray(Vector3f origin, Vector3f direction) {
+ setOrigin(origin);
+ setDirection(direction);
+ }
+
+ /**
+ * <code>intersect</code> determines if the Ray intersects a triangle.
+ * @param t the Triangle to test against.
+ * @return true if the ray collides.
+ */
+// public boolean intersect(Triangle t) {
+// return intersect(t.get(0), t.get(1), t.get(2));
+// }
+ /**
+ * <code>intersect</code> determines if the Ray intersects a triangle
+ * defined by the specified points.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @return true if the ray collides.
+ */
+// public boolean intersect(Vector3f v0,Vector3f v1,Vector3f v2){
+// return intersectWhere(v0, v1, v2, null);
+// }
+ /**
+ * <code>intersectWhere</code> determines if the Ray intersects a triangle. It then
+ * stores the point of intersection in the given loc vector
+ * @param t the Triangle to test against.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides)
+ * @return true if the ray collides.
+ */
+ public boolean intersectWhere(Triangle t, Vector3f loc) {
+ return intersectWhere(t.get(0), t.get(1), t.get(2), loc);
+ }
+
+ /**
+ * <code>intersectWhere</code> determines if the Ray intersects a triangle
+ * defined by the specified points and if so it stores the point of
+ * intersection in the given loc vector.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) if null, only boolean is calculated.
+ * @return true if the ray collides.
+ */
+ public boolean intersectWhere(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f loc) {
+ return intersects(v0, v1, v2, loc, false, false);
+ }
+
+ /**
+ * <code>intersectWherePlanar</code> determines if the Ray intersects a
+ * triangle and if so it stores the point of
+ * intersection in the given loc vector as t, u, v where t is the distance
+ * from the origin to the point of intersection and u,v is the intersection
+ * point in terms of the triangle plane.
+ *
+ * @param t the Triangle to test against.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) as t, u, v
+ * @return true if the ray collides.
+ */
+ public boolean intersectWherePlanar(Triangle t, Vector3f loc) {
+ return intersectWherePlanar(t.get(0), t.get(1), t.get(2), loc);
+ }
+
+ /**
+ * <code>intersectWherePlanar</code> determines if the Ray intersects a
+ * triangle defined by the specified points and if so it stores the point of
+ * intersection in the given loc vector as t, u, v where t is the distance
+ * from the origin to the point of intersection and u,v is the intersection
+ * point in terms of the triangle plane.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) as t, u, v
+ * @return true if the ray collides.
+ */
+ public boolean intersectWherePlanar(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f loc) {
+ return intersects(v0, v1, v2, loc, true, false);
+ }
+
+ /**
+ * <code>intersects</code> does the actual intersection work.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @param store
+ * storage vector - if null, no intersection is calc'd
+ * @param doPlanar
+ * true if we are calcing planar results.
+ * @param quad
+ * @return true if ray intersects triangle
+ */
+ private boolean intersects(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f store, boolean doPlanar, boolean quad) {
+ TempVars vars = TempVars.get();
+
+ Vector3f tempVa = vars.vect1,
+ tempVb = vars.vect2,
+ tempVc = vars.vect3,
+ tempVd = vars.vect4;
+
+ Vector3f diff = origin.subtract(v0, tempVa);
+ Vector3f edge1 = v1.subtract(v0, tempVb);
+ Vector3f edge2 = v2.subtract(v0, tempVc);
+ Vector3f norm = edge1.cross(edge2, tempVd);
+
+ float dirDotNorm = direction.dot(norm);
+ float sign;
+ if (dirDotNorm > FastMath.FLT_EPSILON) {
+ sign = 1;
+ } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
+ sign = -1f;
+ dirDotNorm = -dirDotNorm;
+ } else {
+ // ray and triangle/quad are parallel
+ vars.release();
+ return false;
+ }
+
+ float dirDotDiffxEdge2 = sign * direction.dot(diff.cross(edge2, edge2));
+ if (dirDotDiffxEdge2 >= 0.0f) {
+ float dirDotEdge1xDiff = sign
+ * direction.dot(edge1.crossLocal(diff));
+
+ if (dirDotEdge1xDiff >= 0.0f) {
+ if (!quad ? dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm : dirDotEdge1xDiff <= dirDotNorm) {
+ float diffDotNorm = -sign * diff.dot(norm);
+ if (diffDotNorm >= 0.0f) {
+ // this method always returns
+ vars.release();
+
+ // ray intersects triangle
+ // if storage vector is null, just return true,
+ if (store == null) {
+ return true;
+ }
+
+ // else fill in.
+ float inv = 1f / dirDotNorm;
+ float t = diffDotNorm * inv;
+ if (!doPlanar) {
+ store.set(origin).addLocal(direction.x * t,
+ direction.y * t, direction.z * t);
+ } else {
+ // these weights can be used to determine
+ // interpolated values, such as texture coord.
+ // eg. texcoord s,t at intersection point:
+ // s = w0*s0 + w1*s1 + w2*s2;
+ // t = w0*t0 + w1*t1 + w2*t2;
+ float w1 = dirDotDiffxEdge2 * inv;
+ float w2 = dirDotEdge1xDiff * inv;
+ //float w0 = 1.0f - w1 - w2;
+ store.set(t, w1, w2);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ vars.release();
+ return false;
+ }
+
+ public float intersects(Vector3f v0, Vector3f v1, Vector3f v2) {
+ float edge1X = v1.x - v0.x;
+ float edge1Y = v1.y - v0.y;
+ float edge1Z = v1.z - v0.z;
+
+ float edge2X = v2.x - v0.x;
+ float edge2Y = v2.y - v0.y;
+ float edge2Z = v2.z - v0.z;
+
+ float normX = ((edge1Y * edge2Z) - (edge1Z * edge2Y));
+ float normY = ((edge1Z * edge2X) - (edge1X * edge2Z));
+ float normZ = ((edge1X * edge2Y) - (edge1Y * edge2X));
+
+ float dirDotNorm = direction.x * normX + direction.y * normY + direction.z * normZ;
+
+ float diffX = origin.x - v0.x;
+ float diffY = origin.y - v0.y;
+ float diffZ = origin.z - v0.z;
+
+ float sign;
+ if (dirDotNorm > FastMath.FLT_EPSILON) {
+ sign = 1;
+ } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
+ sign = -1f;
+ dirDotNorm = -dirDotNorm;
+ } else {
+ // ray and triangle/quad are parallel
+ return Float.POSITIVE_INFINITY;
+ }
+
+ float diffEdge2X = ((diffY * edge2Z) - (diffZ * edge2Y));
+ float diffEdge2Y = ((diffZ * edge2X) - (diffX * edge2Z));
+ float diffEdge2Z = ((diffX * edge2Y) - (diffY * edge2X));
+
+ float dirDotDiffxEdge2 = sign * (direction.x * diffEdge2X
+ + direction.y * diffEdge2Y
+ + direction.z * diffEdge2Z);
+
+ if (dirDotDiffxEdge2 >= 0.0f) {
+ diffEdge2X = ((edge1Y * diffZ) - (edge1Z * diffY));
+ diffEdge2Y = ((edge1Z * diffX) - (edge1X * diffZ));
+ diffEdge2Z = ((edge1X * diffY) - (edge1Y * diffX));
+
+ float dirDotEdge1xDiff = sign * (direction.x * diffEdge2X
+ + direction.y * diffEdge2Y
+ + direction.z * diffEdge2Z);
+
+ if (dirDotEdge1xDiff >= 0.0f) {
+ if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) {
+ float diffDotNorm = -sign * (diffX * normX + diffY * normY + diffZ * normZ);
+ if (diffDotNorm >= 0.0f) {
+ // ray intersects triangle
+ // fill in.
+ float inv = 1f / dirDotNorm;
+ float t = diffDotNorm * inv;
+ return t;
+ }
+ }
+ }
+ }
+
+ return Float.POSITIVE_INFINITY;
+ }
+
+ /**
+ * <code>intersectWherePlanar</code> determines if the Ray intersects a
+ * quad defined by the specified points and if so it stores the point of
+ * intersection in the given loc vector as t, u, v where t is the distance
+ * from the origin to the point of intersection and u,v is the intersection
+ * point in terms of the quad plane.
+ * One edge of the quad is [v0,v1], another one [v0,v2]. The behaviour thus is like
+ * {@link #intersectWherePlanar(Vector3f, Vector3f, Vector3f, Vector3f)} except for
+ * the extended area, which is equivalent to the union of the triangles [v0,v1,v2]
+ * and [-v0+v1+v2,v1,v2].
+ *
+ * @param v0
+ * top left point of the quad.
+ * @param v1
+ * top right point of the quad.
+ * @param v2
+ * bottom left point of the quad.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) as t, u, v
+ * @return true if the ray collides with the quad.
+ */
+ public boolean intersectWherePlanarQuad(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f loc) {
+ return intersects(v0, v1, v2, loc, true, true);
+ }
+
+ /**
+ *
+ * @param p
+ * @param loc
+ * @return true if the ray collides with the given Plane
+ */
+ public boolean intersectsWherePlane(Plane p, Vector3f loc) {
+ float denominator = p.getNormal().dot(direction);
+
+ if (denominator > -FastMath.FLT_EPSILON && denominator < FastMath.FLT_EPSILON) {
+ return false; // coplanar
+ }
+ float numerator = -(p.getNormal().dot(origin) - p.getConstant());
+ float ratio = numerator / denominator;
+
+ if (ratio < FastMath.FLT_EPSILON) {
+ return false; // intersects behind origin
+ }
+ loc.set(direction).multLocal(ratio).addLocal(origin);
+
+ return true;
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) {
+ if (other instanceof BoundingVolume) {
+ BoundingVolume bv = (BoundingVolume) other;
+ return bv.collideWith(this, results);
+ } else if (other instanceof AbstractTriangle) {
+ AbstractTriangle tri = (AbstractTriangle) other;
+ float d = intersects(tri.get1(), tri.get2(), tri.get3());
+ if (Float.isInfinite(d) || Float.isNaN(d)) {
+ return 0;
+ }
+
+ Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin);
+ results.addCollision(new CollisionResult(point, d));
+ return 1;
+ } else {
+ throw new UnsupportedCollisionException();
+ }
+ }
+
+ public float distanceSquared(Vector3f point) {
+ TempVars vars = TempVars.get();
+
+ Vector3f tempVa = vars.vect1,
+ tempVb = vars.vect2;
+
+ point.subtract(origin, tempVa);
+ float rayParam = direction.dot(tempVa);
+ if (rayParam > 0) {
+ origin.add(direction.mult(rayParam, tempVb), tempVb);
+ } else {
+ tempVb.set(origin);
+ rayParam = 0.0f;
+ }
+
+ tempVb.subtract(point, tempVa);
+ float len = tempVa.lengthSquared();
+ vars.release();
+ return len;
+ }
+
+ /**
+ *
+ * <code>getOrigin</code> retrieves the origin point of the ray.
+ *
+ * @return the origin of the ray.
+ */
+ public Vector3f getOrigin() {
+ return origin;
+ }
+
+ /**
+ *
+ * <code>setOrigin</code> sets the origin of the ray.
+ * @param origin the origin of the ray.
+ */
+ public void setOrigin(Vector3f origin) {
+ this.origin.set(origin);
+ }
+
+ /**
+ * <code>getLimit</code> returns the limit of the ray, aka the length.
+ * If the limit is not infinity, then this ray is a line with length <code>
+ * limit</code>.
+ *
+ * @return the limit of the ray, aka the length.
+ */
+ public float getLimit() {
+ return limit;
+ }
+
+ /**
+ * <code>setLimit</code> sets the limit of the ray.
+ * @param limit the limit of the ray.
+ * @see Ray#getLimit()
+ */
+ public void setLimit(float limit) {
+ this.limit = limit;
+ }
+
+ /**
+ *
+ * <code>getDirection</code> retrieves the direction vector of the ray.
+ * @return the direction of the ray.
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ *
+ * <code>setDirection</code> sets the direction vector of the ray.
+ * @param direction the direction of the ray.
+ */
+ public void setDirection(Vector3f direction) {
+ assert direction.isUnitVector();
+ this.direction.set(direction);
+ }
+
+ /**
+ * Copies information from a source ray into this ray.
+ *
+ * @param source
+ * the ray to copy information from
+ */
+ public void set(Ray source) {
+ origin.set(source.getOrigin());
+ direction.set(source.getDirection());
+ }
+
+ public String toString() {
+ return getClass().getSimpleName() + " [Origin: " + origin + ", Direction: " + direction + "]";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(origin, "origin", Vector3f.ZERO);
+ capsule.write(direction, "direction", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
+ direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Ray clone() {
+ try {
+ Ray r = (Ray) super.clone();
+ r.direction = direction.clone();
+ r.origin = origin.clone();
+ return r;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Rectangle.java b/engine/src/core/com/jme3/math/Rectangle.java
new file mode 100644
index 0000000..310270c
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Rectangle.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+
+/**
+ *
+ * <code>Rectangle</code> defines a finite plane within three dimensional space
+ * that is specified via three points (A, B, C). These three points define a
+ * triangle with the forth point defining the rectangle ((B + C) - A.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+
+public final class Rectangle implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f a, b, c;
+
+ /**
+ * Constructor creates a new <code>Rectangle</code> with no defined corners.
+ * A, B, and C must be set to define a valid rectangle.
+ *
+ */
+ public Rectangle() {
+ a = new Vector3f();
+ b = new Vector3f();
+ c = new Vector3f();
+ }
+
+ /**
+ * Constructor creates a new <code>Rectangle</code> with defined A, B, and C
+ * points that define the area of the rectangle.
+ *
+ * @param a
+ * the first corner of the rectangle.
+ * @param b
+ * the second corner of the rectangle.
+ * @param c
+ * the third corner of the rectangle.
+ */
+ public Rectangle(Vector3f a, Vector3f b, Vector3f c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+
+ /**
+ * <code>getA</code> returns the first point of the rectangle.
+ *
+ * @return the first point of the rectangle.
+ */
+ public Vector3f getA() {
+ return a;
+ }
+
+ /**
+ * <code>setA</code> sets the first point of the rectangle.
+ *
+ * @param a
+ * the first point of the rectangle.
+ */
+ public void setA(Vector3f a) {
+ this.a = a;
+ }
+
+ /**
+ * <code>getB</code> returns the second point of the rectangle.
+ *
+ * @return the second point of the rectangle.
+ */
+ public Vector3f getB() {
+ return b;
+ }
+
+ /**
+ * <code>setB</code> sets the second point of the rectangle.
+ *
+ * @param b
+ * the second point of the rectangle.
+ */
+ public void setB(Vector3f b) {
+ this.b = b;
+ }
+
+ /**
+ * <code>getC</code> returns the third point of the rectangle.
+ *
+ * @return the third point of the rectangle.
+ */
+ public Vector3f getC() {
+ return c;
+ }
+
+ /**
+ * <code>setC</code> sets the third point of the rectangle.
+ *
+ * @param c
+ * the third point of the rectangle.
+ */
+ public void setC(Vector3f c) {
+ this.c = c;
+ }
+
+ /**
+ * <code>random</code> returns a random point within the plane defined by:
+ * A, B, C, and (B + C) - A.
+ *
+ * @return a random point within the rectangle.
+ */
+ public Vector3f random() {
+ return random(null);
+ }
+
+ /**
+ * <code>random</code> returns a random point within the plane defined by:
+ * A, B, C, and (B + C) - A.
+ *
+ * @param result
+ * Vector to store result in
+ * @return a random point within the rectangle.
+ */
+ public Vector3f random(Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+
+ float s = FastMath.nextRandomFloat();
+ float t = FastMath.nextRandomFloat();
+
+ float aMod = 1.0f - s - t;
+ result.set(a.mult(aMod).addLocal(b.mult(s).addLocal(c.mult(t))));
+ return result;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(a, "a", Vector3f.ZERO);
+ capsule.write(b, "b", Vector3f.ZERO);
+ capsule.write(c, "c", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ a = (Vector3f) capsule.readSavable("a", Vector3f.ZERO.clone());
+ b = (Vector3f) capsule.readSavable("b", Vector3f.ZERO.clone());
+ c = (Vector3f) capsule.readSavable("c", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Rectangle clone() {
+ try {
+ Rectangle r = (Rectangle) super.clone();
+ r.a = a.clone();
+ r.b = b.clone();
+ r.c = c.clone();
+ return r;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Ring.java b/engine/src/core/com/jme3/math/Ring.java
new file mode 100644
index 0000000..20f1ea4
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Ring.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+
+/**
+ * <code>Ring</code> defines a flat ring or disk within three dimensional
+ * space that is specified via the ring's center point, an up vector, an inner
+ * radius, and an outer radius.
+ *
+ * @author Andrzej Kapolka
+ * @author Joshua Slack
+ */
+
+public final class Ring implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f center, up;
+ private float innerRadius, outerRadius;
+ private transient static Vector3f b1 = new Vector3f(), b2 = new Vector3f();
+
+ /**
+ * Constructor creates a new <code>Ring</code> lying on the XZ plane,
+ * centered at the origin, with an inner radius of zero and an outer radius
+ * of one (a unit disk).
+ */
+ public Ring() {
+ center = new Vector3f();
+ up = Vector3f.UNIT_Y.clone();
+ innerRadius = 0f;
+ outerRadius = 1f;
+ }
+
+ /**
+ * Constructor creates a new <code>Ring</code> with defined center point,
+ * up vector, and inner and outer radii.
+ *
+ * @param center
+ * the center of the ring.
+ * @param up
+ * the unit up vector defining the ring's orientation.
+ * @param innerRadius
+ * the ring's inner radius.
+ * @param outerRadius
+ * the ring's outer radius.
+ */
+ public Ring(Vector3f center, Vector3f up, float innerRadius,
+ float outerRadius) {
+ this.center = center;
+ this.up = up;
+ this.innerRadius = innerRadius;
+ this.outerRadius = outerRadius;
+ }
+
+ /**
+ * <code>getCenter</code> returns the center of the ring.
+ *
+ * @return the center of the ring.
+ */
+ public Vector3f getCenter() {
+ return center;
+ }
+
+ /**
+ * <code>setCenter</code> sets the center of the ring.
+ *
+ * @param center
+ * the center of the ring.
+ */
+ public void setCenter(Vector3f center) {
+ this.center = center;
+ }
+
+ /**
+ * <code>getUp</code> returns the ring's up vector.
+ *
+ * @return the ring's up vector.
+ */
+ public Vector3f getUp() {
+ return up;
+ }
+
+ /**
+ * <code>setUp</code> sets the ring's up vector.
+ *
+ * @param up
+ * the ring's up vector.
+ */
+ public void setUp(Vector3f up) {
+ this.up = up;
+ }
+
+ /**
+ * <code>getInnerRadius</code> returns the ring's inner radius.
+ *
+ * @return the ring's inner radius.
+ */
+ public float getInnerRadius() {
+ return innerRadius;
+ }
+
+ /**
+ * <code>setInnerRadius</code> sets the ring's inner radius.
+ *
+ * @param innerRadius
+ * the ring's inner radius.
+ */
+ public void setInnerRadius(float innerRadius) {
+ this.innerRadius = innerRadius;
+ }
+
+ /**
+ * <code>getOuterRadius</code> returns the ring's outer radius.
+ *
+ * @return the ring's outer radius.
+ */
+ public float getOuterRadius() {
+ return outerRadius;
+ }
+
+ /**
+ * <code>setOuterRadius</code> sets the ring's outer radius.
+ *
+ * @param outerRadius
+ * the ring's outer radius.
+ */
+ public void setOuterRadius(float outerRadius) {
+ this.outerRadius = outerRadius;
+ }
+
+ /**
+ *
+ * <code>random</code> returns a random point within the ring.
+ *
+ * @return a random point within the ring.
+ */
+ public Vector3f random() {
+ return random(null);
+ }
+
+ /**
+ *
+ * <code>random</code> returns a random point within the ring.
+ *
+ * @param result Vector to store result in
+ * @return a random point within the ring.
+ */
+ public Vector3f random(Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+
+ // compute a random radius according to the ring area distribution
+ float inner2 = innerRadius * innerRadius, outer2 = outerRadius
+ * outerRadius, r = FastMath.sqrt(inner2
+ + FastMath.nextRandomFloat() * (outer2 - inner2)), theta = FastMath
+ .nextRandomFloat()
+ * FastMath.TWO_PI;
+ up.cross(Vector3f.UNIT_X, b1);
+ if (b1.lengthSquared() < FastMath.FLT_EPSILON) {
+ up.cross(Vector3f.UNIT_Y, b1);
+ }
+ b1.normalizeLocal();
+ up.cross(b1, b2);
+ result.set(b1).multLocal(r * FastMath.cos(theta)).addLocal(center);
+ result.scaleAdd(r * FastMath.sin(theta), b2, result);
+ return result;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(center, "center", Vector3f.ZERO);
+ capsule.write(up, "up", Vector3f.UNIT_Z);
+ capsule.write(innerRadius, "innerRadius", 0f);
+ capsule.write(outerRadius, "outerRadius", 1f);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ center = (Vector3f) capsule.readSavable("center",
+ Vector3f.ZERO.clone());
+ up = (Vector3f) capsule
+ .readSavable("up", Vector3f.UNIT_Z.clone());
+ innerRadius = capsule.readFloat("innerRadius", 0f);
+ outerRadius = capsule.readFloat("outerRadius", 1f);
+ }
+
+ @Override
+ public Ring clone() {
+ try {
+ Ring r = (Ring) super.clone();
+ r.center = center.clone();
+ r.up = up.clone();
+ return r;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/math/Spline.java b/engine/src/core/com/jme3/math/Spline.java
new file mode 100644
index 0000000..b28a797
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Spline.java
@@ -0,0 +1,447 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Spline implements Savable {
+
+ public enum SplineType {
+ Linear,
+ CatmullRom,
+ Bezier,
+ Nurb
+ }
+
+ private List<Vector3f> controlPoints = new ArrayList<Vector3f>();
+ private List<Float> knots; //knots of NURBS spline
+ private float[] weights; //weights of NURBS spline
+ private int basisFunctionDegree; //degree of NURBS spline basis function (computed automatically)
+ private boolean cycle;
+ private List<Float> segmentsLength;
+ private float totalLength;
+ private List<Vector3f> CRcontrolPoints;
+ private float curveTension = 0.5f;
+ private SplineType type = SplineType.CatmullRom;
+
+ public Spline() {
+ }
+
+ /**
+ * Create a spline
+ * @param splineType the type of the spline @see {SplineType}
+ * @param controlPoints an array of vector to use as control points of the spline
+ * If the type of the curve is Bezier curve the control points should be provided
+ * in the appropriate way. Each point 'p' describing control position in the scene
+ * should be surrounded by two handler points. This applies to every point except
+ * for the border points of the curve, who should have only one handle point.
+ * The pattern should be as follows:
+ * P0 - H0 : H1 - P1 - H1 : ... : Hn - Pn
+ *
+ * n is the amount of 'P' - points.
+ * @param curveTension the tension of the spline
+ * @param cycle true if the spline cycle.
+ */
+ public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) {
+ if(splineType==SplineType.Nurb) {
+ throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
+ }
+ for (int i = 0; i < controlPoints.length; i++) {
+ Vector3f vector3f = controlPoints[i];
+ this.controlPoints.add(vector3f);
+ }
+ type = splineType;
+ this.curveTension = curveTension;
+ this.cycle = cycle;
+ this.computeTotalLentgh();
+ }
+
+ /**
+ * Create a spline
+ * @param splineType the type of the spline @see {SplineType}
+ * @param controlPoints a list of vector to use as control points of the spline
+ * If the type of the curve is Bezier curve the control points should be provided
+ * in the appropriate way. Each point 'p' describing control position in the scene
+ * should be surrounded by two handler points. This applies to every point except
+ * for the border points of the curve, who should have only one handle point.
+ * The pattern should be as follows:
+ * P0 - H0 : H1 - P1 - H1 : ... : Hn - Pn
+ *
+ * n is the amount of 'P' - points.
+ * @param curveTension the tension of the spline
+ * @param cycle true if the spline cycle.
+ */
+ public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) {
+ if(splineType==SplineType.Nurb) {
+ throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
+ }
+ type = splineType;
+ this.controlPoints.addAll(controlPoints);
+ this.curveTension = curveTension;
+ this.cycle = cycle;
+ this.computeTotalLentgh();
+ }
+
+ /**
+ * Create a NURBS spline. A spline type is automatically set to SplineType.Nurb.
+ * The cycle is set to <b>false</b> by default.
+ * @param controlPoints a list of vector to use as control points of the spline
+ * @param nurbKnots the nurb's spline knots
+ */
+ public Spline(List<Vector4f> controlPoints, List<Float> nurbKnots) {
+ //input data control
+ for(int i=0;i<nurbKnots.size()-1;++i) {
+ if(nurbKnots.get(i)>nurbKnots.get(i+1)) {
+ throw new IllegalArgumentException("The knots values cannot decrease!");
+ }
+ }
+
+ //storing the data
+ type = SplineType.Nurb;
+ this.weights = new float[controlPoints.size()];
+ this.knots = nurbKnots;
+ this.basisFunctionDegree = nurbKnots.size() - weights.length;
+ for(int i=0;i<controlPoints.size();++i) {
+ Vector4f controlPoint = controlPoints.get(i);
+ this.controlPoints.add(new Vector3f(controlPoint.x, controlPoint.y, controlPoint.z));
+ this.weights[i] = controlPoint.w;
+ }
+ CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
+ this.computeTotalLentgh();
+ }
+
+ private void initCatmullRomWayPoints(List<Vector3f> list) {
+ if (CRcontrolPoints == null) {
+ CRcontrolPoints = new ArrayList<Vector3f>();
+ } else {
+ CRcontrolPoints.clear();
+ }
+ int nb = list.size() - 1;
+
+ if (cycle) {
+ CRcontrolPoints.add(list.get(list.size() - 2));
+ } else {
+ CRcontrolPoints.add(list.get(0).subtract(list.get(1).subtract(list.get(0))));
+ }
+
+ for (Iterator<Vector3f> it = list.iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ CRcontrolPoints.add(vector3f);
+ }
+ if (cycle) {
+ CRcontrolPoints.add(list.get(1));
+ } else {
+ CRcontrolPoints.add(list.get(nb).add(list.get(nb).subtract(list.get(nb - 1))));
+ }
+
+ }
+
+ /**
+ * Adds a controlPoint to the spline
+ * @param controlPoint a position in world space
+ */
+ public void addControlPoint(Vector3f controlPoint) {
+ if (controlPoints.size() > 2 && this.cycle) {
+ controlPoints.remove(controlPoints.size() - 1);
+ }
+ controlPoints.add(controlPoint);
+ if (controlPoints.size() >= 2 && this.cycle) {
+ controlPoints.add(controlPoints.get(0));
+ }
+ if (controlPoints.size() > 1) {
+ this.computeTotalLentgh();
+ }
+ }
+
+ /**
+ * remove the controlPoint from the spline
+ * @param controlPoint the controlPoint to remove
+ */
+ public void removeControlPoint(Vector3f controlPoint) {
+ controlPoints.remove(controlPoint);
+ if (controlPoints.size() > 1) {
+ this.computeTotalLentgh();
+ }
+ }
+
+ public void clearControlPoints(){
+ controlPoints.clear();
+ totalLength = 0;
+ }
+
+ /**
+ * This method computes the total length of the curve.
+ */
+ private void computeTotalLentgh() {
+ totalLength = 0;
+ float l = 0;
+ if (segmentsLength == null) {
+ segmentsLength = new ArrayList<Float>();
+ } else {
+ segmentsLength.clear();
+ }
+ if (type == SplineType.Linear) {
+ if (controlPoints.size() > 1) {
+ for (int i = 0; i < controlPoints.size() - 1; i++) {
+ l = controlPoints.get(i + 1).subtract(controlPoints.get(i)).length();
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ } else if(type == SplineType.Bezier) {
+ this.computeBezierLength();
+ } else if(type == SplineType.Nurb) {
+ this.computeNurbLength();
+ } else {
+ this.initCatmullRomWayPoints(controlPoints);
+ this.computeCatmulLength();
+ }
+ }
+
+ /**
+ * This method computes the Catmull Rom curve length.
+ */
+ private void computeCatmulLength() {
+ float l = 0;
+ if (controlPoints.size() > 1) {
+ for (int i = 0; i < controlPoints.size() - 1; i++) {
+ l = FastMath.getCatmullRomP1toP2Length(CRcontrolPoints.get(i),
+ CRcontrolPoints.get(i + 1), CRcontrolPoints.get(i + 2), CRcontrolPoints.get(i + 3), 0, 1, curveTension);
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ }
+
+ /**
+ * This method calculates the Bezier curve length.
+ */
+ private void computeBezierLength() {
+ float l = 0;
+ if (controlPoints.size() > 1) {
+ for (int i = 0; i < controlPoints.size() - 1; i+=3) {
+ l = FastMath.getBezierP1toP2Length(controlPoints.get(i),
+ controlPoints.get(i + 1), controlPoints.get(i + 2), controlPoints.get(i + 3));
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ }
+
+ /**
+ * This method calculates the NURB curve length.
+ */
+ private void computeNurbLength() {
+ //TODO: implement
+ }
+
+ /**
+ * Iterpolate a position on the spline
+ * @param value a value from 0 to 1 that represent the postion between the curent control point and the next one
+ * @param currentControlPoint the current control point
+ * @param store a vector to store the result (use null to create a new one that will be returned by the method)
+ * @return the position
+ */
+ public Vector3f interpolate(float value, int currentControlPoint, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ switch (type) {
+ case CatmullRom:
+ FastMath.interpolateCatmullRom(value, curveTension, CRcontrolPoints.get(currentControlPoint), CRcontrolPoints.get(currentControlPoint + 1), CRcontrolPoints.get(currentControlPoint + 2), CRcontrolPoints.get(currentControlPoint + 3), store);
+ break;
+ case Linear:
+ FastMath.interpolateLinear(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), store);
+ break;
+ case Bezier:
+ FastMath.interpolateBezier(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), controlPoints.get(currentControlPoint + 2), controlPoints.get(currentControlPoint + 3), store);
+ break;
+ case Nurb:
+ CurveAndSurfaceMath.interpolateNurbs(value, this, store);
+ break;
+ default:
+ break;
+ }
+ return store;
+ }
+
+ /**
+ * returns the curve tension
+ */
+ public float getCurveTension() {
+ return curveTension;
+ }
+
+ /**
+ * sets the curve tension
+ *
+ * @param curveTension the tension
+ */
+ public void setCurveTension(float curveTension) {
+ this.curveTension = curveTension;
+ if(type==SplineType.CatmullRom) {
+ this.computeTotalLentgh();
+ }
+ }
+
+ /**
+ * returns true if the spline cycle
+ */
+ public boolean isCycle() {
+ return cycle;
+ }
+
+ /**
+ * set to true to make the spline cycle
+ * @param cycle
+ */
+ public void setCycle(boolean cycle) {
+ if(type!=SplineType.Nurb) {
+ if (controlPoints.size() >= 2) {
+ if (this.cycle && !cycle) {
+ controlPoints.remove(controlPoints.size() - 1);
+ }
+ if (!this.cycle && cycle) {
+ controlPoints.add(controlPoints.get(0));
+ }
+ this.cycle = cycle;
+ this.computeTotalLentgh();
+ } else {
+ this.cycle = cycle;
+ }
+ }
+ }
+
+ /**
+ * return the total lenght of the spline
+ */
+ public float getTotalLength() {
+ return totalLength;
+ }
+
+ /**
+ * return the type of the spline
+ */
+ public SplineType getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of the spline
+ * @param type
+ */
+ public void setType(SplineType type) {
+ this.type = type;
+ this.computeTotalLentgh();
+ }
+
+ /**
+ * returns this spline control points
+ */
+ public List<Vector3f> getControlPoints() {
+ return controlPoints;
+ }
+
+ /**
+ * returns a list of float representing the segments lenght
+ */
+ public List<Float> getSegmentsLength() {
+ return segmentsLength;
+ }
+
+ //////////// NURBS getters /////////////////////
+
+ /**
+ * This method returns the minimum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
+ * type - NPE will be thrown.
+ * @return the minimum nurb curve knot value
+ */
+ public float getMinNurbKnot() {
+ return knots.get(basisFunctionDegree - 1);
+ }
+
+ /**
+ * This method returns the maximum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
+ * type - NPE will be thrown.
+ * @return the maximum nurb curve knot value
+ */
+ public float getMaxNurbKnot() {
+ return knots.get(weights.length);
+ }
+
+ /**
+ * This method returns NURBS' spline knots.
+ * @return NURBS' spline knots
+ */
+ public List<Float> getKnots() {
+ return knots;
+ }
+
+ /**
+ * This method returns NURBS' spline weights.
+ * @return NURBS' spline weights
+ */
+ public float[] getWeights() {
+ return weights;
+ }
+
+ /**
+ * This method returns NURBS' spline basis function degree.
+ * @return NURBS' spline basis function degree
+ */
+ public int getBasisFunctionDegree() {
+ return basisFunctionDegree;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null);
+ oc.write(type, "type", SplineType.CatmullRom);
+ float list[] = new float[segmentsLength.size()];
+ for (int i = 0; i < segmentsLength.size(); i++) {
+ list[i] = segmentsLength.get(i);
+ }
+ oc.write(list, "segmentsLength", null);
+
+ oc.write(totalLength, "totalLength", 0);
+ oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
+ oc.write(curveTension, "curveTension", 0.5f);
+ oc.write(cycle, "cycle", false);
+ oc.writeSavableArrayList((ArrayList<Float>)knots, "knots", null);
+ oc.write(weights, "weights", null);
+ oc.write(basisFunctionDegree, "basisFunctionDegree", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+
+ controlPoints = (ArrayList<Vector3f>) in.readSavableArrayList("wayPoints", null);
+ float list[] = in.readFloatArray("segmentsLength", null);
+ if (list != null) {
+ segmentsLength = new ArrayList<Float>();
+ for (int i = 0; i < list.length; i++) {
+ segmentsLength.add(new Float(list[i]));
+ }
+ }
+ type = in.readEnum("pathSplineType", SplineType.class, SplineType.CatmullRom);
+ totalLength = in.readFloat("totalLength", 0);
+ CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null);
+ curveTension = in.readFloat("curveTension", 0.5f);
+ cycle = in.readBoolean("cycle", false);
+ knots = in.readSavableArrayList("knots", null);
+ weights = in.readFloatArray("weights", null);
+ basisFunctionDegree = in.readInt("basisFunctionDegree", 0);
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Transform.java b/engine/src/core/com/jme3/math/Transform.java
new file mode 100644
index 0000000..7ccd847
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Transform.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+/**
+ * Started Date: Jul 16, 2004<br><br>
+ * Represents a translation, rotation and scale in one object.
+ *
+ * @author Jack Lindamood
+ * @author Joshua Slack
+ */
+public final class Transform implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ public static final Transform IDENTITY = new Transform();
+
+ private Quaternion rot = new Quaternion();
+ private Vector3f translation = new Vector3f();
+ private Vector3f scale = new Vector3f(1,1,1);
+
+ public Transform(Vector3f translation, Quaternion rot){
+ this.translation.set(translation);
+ this.rot.set(rot);
+ }
+
+ public Transform(Vector3f translation, Quaternion rot, Vector3f scale){
+ this(translation, rot);
+ this.scale.set(scale);
+ }
+
+ public Transform(Vector3f translation){
+ this(translation, Quaternion.IDENTITY);
+ }
+
+ public Transform(Quaternion rot){
+ this(Vector3f.ZERO, rot);
+ }
+
+ public Transform(){
+ this(Vector3f.ZERO, Quaternion.IDENTITY);
+ }
+
+ /**
+ * Sets this rotation to the given Quaternion value.
+ * @param rot The new rotation for this matrix.
+ * @return this
+ */
+ public Transform setRotation(Quaternion rot) {
+ this.rot.set(rot);
+ return this;
+ }
+
+ /**
+ * Sets this translation to the given value.
+ * @param trans The new translation for this matrix.
+ * @return this
+ */
+ public Transform setTranslation(Vector3f trans) {
+ this.translation.set(trans);
+ return this;
+ }
+
+ /**
+ * Return the translation vector in this matrix.
+ * @return translation vector.
+ */
+ public Vector3f getTranslation() {
+ return translation;
+ }
+
+ /**
+ * Sets this scale to the given value.
+ * @param scale The new scale for this matrix.
+ * @return this
+ */
+ public Transform setScale(Vector3f scale) {
+ this.scale.set(scale);
+ return this;
+ }
+
+ /**
+ * Sets this scale to the given value.
+ * @param scale The new scale for this matrix.
+ * @return this
+ */
+ public Transform setScale(float scale) {
+ this.scale.set(scale, scale, scale);
+ return this;
+ }
+
+ /**
+ * Return the scale vector in this matrix.
+ * @return scale vector.
+ */
+ public Vector3f getScale() {
+ return scale;
+ }
+
+ /**
+ * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to
+ * hold the value. The value, once stored, is returned.
+ * @param trans The store location for this matrix's translation.
+ * @return The value of this matrix's translation.
+ */
+ public Vector3f getTranslation(Vector3f trans) {
+ if (trans==null) trans=new Vector3f();
+ trans.set(this.translation);
+ return trans;
+ }
+
+ /**
+ * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to
+ * hold the value. The value, once stored, is returned.
+ * @param quat The store location for this matrix's rotation.
+ * @return The value of this matrix's rotation.
+ */
+ public Quaternion getRotation(Quaternion quat) {
+ if (quat==null) quat=new Quaternion();
+ quat.set(rot);
+ return quat;
+ }
+
+ /**
+ * Return the rotation quaternion in this matrix.
+ * @return rotation quaternion.
+ */
+ public Quaternion getRotation() {
+ return rot;
+ }
+
+ /**
+ * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to
+ * hold the value. The value, once stored, is returned.
+ * @param scale The store location for this matrix's scale.
+ * @return The value of this matrix's scale.
+ */
+ public Vector3f getScale(Vector3f scale) {
+ if (scale==null) scale=new Vector3f();
+ scale.set(this.scale);
+ return scale;
+ }
+
+ /**
+ * Sets this matrix to the interpolation between the first matrix and the second by delta amount.
+ * @param t1 The begining transform.
+ * @param t2 The ending transform.
+ * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.
+ */
+ public void interpolateTransforms(Transform t1, Transform t2, float delta) {
+ this.rot.slerp(t1.rot,t2.rot,delta);
+ this.translation.interpolate(t1.translation,t2.translation,delta);
+ this.scale.interpolate(t1.scale,t2.scale,delta);
+ }
+
+ /**
+ * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms.
+ * @param parent The parent matrix.
+ * @return This matrix, after combining.
+ */
+ public Transform combineWithParent(Transform parent) {
+ scale.multLocal(parent.scale);
+// rot.multLocal(parent.rot);
+ parent.rot.mult(rot, rot);
+
+ // This here, is evil code
+// parent
+// .rot
+// .multLocal(translation)
+// .multLocal(parent.scale)
+// .addLocal(parent.translation);
+
+ translation.multLocal(parent.scale);
+ parent
+ .rot
+ .multLocal(translation)
+ .addLocal(parent.translation);
+ return this;
+ }
+
+ /**
+ * Sets this matrix's translation to the given x,y,z values.
+ * @param x This matrix's new x translation.
+ * @param y This matrix's new y translation.
+ * @param z This matrix's new z translation.
+ * @return this
+ */
+ public Transform setTranslation(float x,float y, float z) {
+ translation.set(x,y,z);
+ return this;
+ }
+
+ /**
+ * Sets this matrix's scale to the given x,y,z values.
+ * @param x This matrix's new x scale.
+ * @param y This matrix's new y scale.
+ * @param z This matrix's new z scale.
+ * @return this
+ */
+ public Transform setScale(float x, float y, float z) {
+ scale.set(x,y,z);
+ return this;
+ }
+
+ public Vector3f transformVector(final Vector3f in, Vector3f store){
+ if (store == null)
+ store = new Vector3f();
+
+ // multiply with scale first, then rotate, finally translate (cf.
+ // Eberly)
+ return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
+ }
+
+ public Vector3f transformInverseVector(final Vector3f in, Vector3f store){
+ if (store == null)
+ store = new Vector3f();
+
+ // The author of this code should look above and take the inverse of that
+ // But for some reason, they didnt ..
+// in.subtract(translation, store).divideLocal(scale);
+// rot.inverse().mult(store, store);
+
+ in.subtract(translation, store);
+ rot.inverse().mult(store, store);
+ store.divideLocal(scale);
+
+ return store;
+ }
+
+ /**
+ * Loads the identity. Equal to translation=1,1,1 scale=0,0,0 rot=0,0,0,1.
+ */
+ public void loadIdentity() {
+ translation.set(0,0,0);
+ scale.set(1,1,1);
+ rot.set(0,0,0,1);
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n"
+ + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n"
+ + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]";
+ }
+
+ /**
+ * Sets this matrix to be equal to the given matrix.
+ * @param matrixQuat The matrix to be equal to.
+ * @return this
+ */
+ public Transform set(Transform matrixQuat) {
+ this.translation.set(matrixQuat.translation);
+ this.rot.set(matrixQuat.rot);
+ this.scale.set(matrixQuat.scale);
+ return this;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(rot, "rot", new Quaternion());
+ capsule.write(translation, "translation", Vector3f.ZERO);
+ capsule.write(scale, "scale", Vector3f.UNIT_XYZ);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+
+ rot = (Quaternion)capsule.readSavable("rot", new Quaternion());
+ translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO);
+ scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);
+ }
+
+ @Override
+ public Transform clone() {
+ try {
+ Transform tq = (Transform) super.clone();
+ tq.rot = rot.clone();
+ tq.scale = scale.clone();
+ tq.translation = translation.clone();
+ return tq;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Triangle.java b/engine/src/core/com/jme3/math/Triangle.java
new file mode 100644
index 0000000..6faa53e
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Triangle.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+/**
+ * <code>Triangle</code> defines an object for containing triangle information.
+ * The triangle is defined by a collection of three {@link Vector3f}
+ * objects.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Triangle extends AbstractTriangle implements Savable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f pointa = new Vector3f();
+ private Vector3f pointb = new Vector3f();
+ private Vector3f pointc = new Vector3f();
+ private transient Vector3f center;
+ private transient Vector3f normal;
+ private float projection;
+ private int index;
+
+ public Triangle() {
+ }
+
+ /**
+ * Constructor instantiates a new <Code>Triangle</code> object with the
+ * supplied vectors as the points. It is recommended that the vertices
+ * be supplied in a counter clockwise winding to support normals for a
+ * right handed coordinate system.
+ * @param p1 the first point of the triangle.
+ * @param p2 the second point of the triangle.
+ * @param p3 the third point of the triangle.
+ */
+ public Triangle(Vector3f p1, Vector3f p2, Vector3f p3) {
+ pointa.set(p1);
+ pointb.set(p2);
+ pointc.set(p3);
+ }
+
+ /**
+ *
+ * <code>get</code> retrieves a point on the triangle denoted by the index
+ * supplied.
+ * @param i the index of the point.
+ * @return the point.
+ */
+ public Vector3f get(int i) {
+ switch (i) {
+ case 0:
+ return pointa;
+ case 1:
+ return pointb;
+ case 2:
+ return pointc;
+ default:
+ return null;
+ }
+ }
+
+ public Vector3f get1() {
+ return pointa;
+ }
+
+ public Vector3f get2() {
+ return pointb;
+ }
+
+ public Vector3f get3() {
+ return pointc;
+ }
+
+ /**
+ *
+ * <code>set</code> sets one of the triangle's points to that specified as
+ * a parameter.
+ * @param i the index to place the point.
+ * @param point the point to set.
+ */
+ public void set(int i, Vector3f point) {
+ switch (i) {
+ case 0:
+ pointa.set(point);
+ break;
+ case 1:
+ pointb.set(point);
+ break;
+ case 2:
+ pointc.set(point);
+ break;
+ }
+ }
+
+ /**
+ *
+ * <code>set</code> sets one of the triangle's points to that specified as
+ * a parameter.
+ * @param i the index to place the point.
+ */
+ public void set(int i, float x, float y, float z) {
+ switch (i) {
+ case 0:
+ pointa.set(x, y, z);
+ break;
+ case 1:
+ pointb.set(x, y, z);
+ break;
+ case 2:
+ pointc.set(x, y, z);
+ break;
+ }
+ }
+
+ public void set1(Vector3f v) {
+ pointa.set(v);
+ }
+
+ public void set2(Vector3f v) {
+ pointb.set(v);
+ }
+
+ public void set3(Vector3f v) {
+ pointc.set(v);
+ }
+
+ public void set(Vector3f v1, Vector3f v2, Vector3f v3) {
+ pointa.set(v1);
+ pointb.set(v2);
+ pointc.set(v3);
+ }
+
+ /**
+ * calculateCenter finds the average point of the triangle.
+ *
+ */
+ public void calculateCenter() {
+ if (center == null) {
+ center = new Vector3f(pointa);
+ } else {
+ center.set(pointa);
+ }
+ center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);
+ }
+
+ /**
+ * calculateNormal generates the normal for this triangle
+ *
+ */
+ public void calculateNormal() {
+ if (normal == null) {
+ normal = new Vector3f(pointb);
+ } else {
+ normal.set(pointb);
+ }
+ normal.subtractLocal(pointa).crossLocal(pointc.x - pointa.x, pointc.y - pointa.y, pointc.z - pointa.z);
+ normal.normalizeLocal();
+ }
+
+ /**
+ * obtains the center point of this triangle (average of the three triangles)
+ * @return the center point.
+ */
+ public Vector3f getCenter() {
+ if (center == null) {
+ calculateCenter();
+ }
+ return center;
+ }
+
+ /**
+ * sets the center point of this triangle (average of the three triangles)
+ * @param center the center point.
+ */
+ public void setCenter(Vector3f center) {
+ this.center = center;
+ }
+
+ /**
+ * obtains the unit length normal vector of this triangle, if set or
+ * calculated
+ *
+ * @return the normal vector
+ */
+ public Vector3f getNormal() {
+ if (normal == null) {
+ calculateNormal();
+ }
+ return normal;
+ }
+
+ /**
+ * sets the normal vector of this triangle (to conform, must be unit length)
+ * @param normal the normal vector.
+ */
+ public void setNormal(Vector3f normal) {
+ this.normal = normal;
+ }
+
+ /**
+ * obtains the projection of the vertices relative to the line origin.
+ * @return the projection of the triangle.
+ */
+ public float getProjection() {
+ return this.projection;
+ }
+
+ /**
+ * sets the projection of the vertices relative to the line origin.
+ * @param projection the projection of the triangle.
+ */
+ public void setProjection(float projection) {
+ this.projection = projection;
+ }
+
+ /**
+ * obtains an index that this triangle represents if it is contained in a OBBTree.
+ * @return the index in an OBBtree
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * sets an index that this triangle represents if it is contained in a OBBTree.
+ * @param index the index in an OBBtree
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f(v2);
+ } else {
+ store.set(v2);
+ }
+
+ store.subtractLocal(v1).crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z);
+ return store.normalizeLocal();
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ e.getCapsule(this).write(pointa, "pointa", Vector3f.ZERO);
+ e.getCapsule(this).write(pointb, "pointb", Vector3f.ZERO);
+ e.getCapsule(this).write(pointc, "pointc", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ pointa = (Vector3f) e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone());
+ pointb = (Vector3f) e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone());
+ pointc = (Vector3f) e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Triangle clone() {
+ try {
+ Triangle t = (Triangle) super.clone();
+ t.pointa = pointa.clone();
+ t.pointb = pointb.clone();
+ t.pointc = pointc.clone();
+ return t;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Vector2f.java b/engine/src/core/com/jme3/math/Vector2f.java
new file mode 100644
index 0000000..c2d8c6f
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Vector2f.java
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.logging.Logger;
+
+/**
+ * <code>Vector2f</code> defines a Vector for a two float value vector.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Vector2f implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+ private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
+
+ public static final Vector2f ZERO = new Vector2f(0f, 0f);
+ public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
+
+ /**
+ * the x value of the vector.
+ */
+ public float x;
+ /**
+ * the y value of the vector.
+ */
+ public float y;
+
+ /**
+ * Creates a Vector2f with the given initial x and y values.
+ *
+ * @param x
+ * The x value of this Vector2f.
+ * @param y
+ * The y value of this Vector2f.
+ */
+ public Vector2f(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
+ */
+ public Vector2f() {
+ x = y = 0;
+ }
+
+ /**
+ * Creates a new Vector2f that contains the passed vector's information
+ *
+ * @param vector2f
+ * The vector to copy
+ */
+ public Vector2f(Vector2f vector2f) {
+ this.x = vector2f.x;
+ this.y = vector2f.y;
+ }
+
+ /**
+ * set the x and y values of the vector
+ *
+ * @param x
+ * the x value of the vector.
+ * @param y
+ * the y value of the vector.
+ * @return this vector
+ */
+ public Vector2f set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ /**
+ * set the x and y values of the vector from another vector
+ *
+ * @param vec
+ * the vector to copy from
+ * @return this vector
+ */
+ public Vector2f set(Vector2f vec) {
+ this.x = vec.x;
+ this.y = vec.y;
+ return this;
+ }
+
+ /**
+ * <code>add</code> adds a provided vector to this vector creating a
+ * resultant vector which is returned. If the provided vector is null, null
+ * is returned.
+ *
+ * @param vec
+ * the vector to add to this.
+ * @return the resultant vector.
+ */
+ public Vector2f add(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return new Vector2f(x + vec.x, y + vec.y);
+ }
+
+ /**
+ * <code>addLocal</code> adds a provided vector to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls. If the
+ * provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to add to this vector.
+ * @return this
+ */
+ public Vector2f addLocal(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x += vec.x;
+ y += vec.y;
+ return this;
+ }
+
+ /**
+ * <code>addLocal</code> adds the provided values to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param addX
+ * value to add to x
+ * @param addY
+ * value to add to y
+ * @return this
+ */
+ public Vector2f addLocal(float addX, float addY) {
+ x += addX;
+ y += addY;
+ return this;
+ }
+
+ /**
+ * <code>add</code> adds this vector by <code>vec</code> and stores the
+ * result in <code>result</code>.
+ *
+ * @param vec
+ * The vector to add.
+ * @param result
+ * The vector to store the result in.
+ * @return The result vector, after adding.
+ */
+ public Vector2f add(Vector2f vec, Vector2f result) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ if (result == null)
+ result = new Vector2f();
+ result.x = x + vec.x;
+ result.y = y + vec.y;
+ return result;
+ }
+
+ /**
+ * <code>dot</code> calculates the dot product of this vector with a
+ * provided vector. If the provided vector is null, 0 is returned.
+ *
+ * @param vec
+ * the vector to dot with this vector.
+ * @return the resultant dot product of this vector and a given vector.
+ */
+ public float dot(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, 0 returned.");
+ return 0;
+ }
+ return x * vec.x + y * vec.y;
+ }
+
+ /**
+ * <code>cross</code> calculates the cross product of this vector with a
+ * parameter vector v.
+ *
+ * @param v
+ * the vector to take the cross product of with this.
+ * @return the cross product vector.
+ */
+ public Vector3f cross(Vector2f v) {
+ return new Vector3f(0, 0, determinant(v));
+ }
+
+ public float determinant(Vector2f v) {
+ return (x * v.y) - (y * v.x);
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the
+ * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
+ *
+ * @param finalVec
+ * The final vector to interpolate towards
+ * @param changeAmnt
+ * An amount between 0.0 - 1.0 representing a percentage change
+ * from this towards finalVec
+ */
+ public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to
+ * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ *
+ * @param beginVec
+ * The begining vector (delta=0)
+ * @param finalVec
+ * The final vector to interpolate towards (delta=1)
+ * @param changeAmnt
+ * An amount between 0.0 - 1.0 representing a precentage change
+ * from beginVec towards finalVec
+ */
+ public Vector2f interpolate(Vector2f beginVec, Vector2f finalVec,
+ float changeAmnt) {
+ this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
+ return this;
+ }
+
+ /**
+ * Check a vector... if it is null or its floats are NaN or infinite, return
+ * false. Else return true.
+ *
+ * @param vector
+ * the vector to check
+ * @return true or false as stated above.
+ */
+ public static boolean isValidVector(Vector2f vector) {
+ if (vector == null) return false;
+ if (Float.isNaN(vector.x) ||
+ Float.isNaN(vector.y)) return false;
+ if (Float.isInfinite(vector.x) ||
+ Float.isInfinite(vector.y)) return false;
+ return true;
+ }
+
+ /**
+ * <code>length</code> calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * <code>lengthSquared</code> calculates the squared value of the
+ * magnitude of the vector.
+ *
+ * @return the magnitude squared of the vector.
+ */
+ public float lengthSquared() {
+ return x * x + y * y;
+ }
+
+ /**
+ * <code>distanceSquared</code> calculates the distance squared between
+ * this vector and vector v.
+ *
+ * @param v the second vector to determine the distance squared.
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(Vector2f v) {
+ double dx = x - v.x;
+ double dy = y - v.y;
+ return (float) (dx * dx + dy * dy);
+ }
+
+ /**
+ * <code>distanceSquared</code> calculates the distance squared between
+ * this vector and vector v.
+ *
+ * @param otherX The X coordinate of the v vector
+ * @param otherY The Y coordinate of the v vector
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(float otherX, float otherY) {
+ double dx = x - otherX;
+ double dy = y - otherY;
+ return (float) (dx * dx + dy * dy);
+ }
+
+ /**
+ * <code>distance</code> calculates the distance between this vector and
+ * vector v.
+ *
+ * @param v the second vector to determine the distance.
+ * @return the distance between the two vectors.
+ */
+ public float distance(Vector2f v) {
+ return FastMath.sqrt(distanceSquared(v));
+ }
+
+ /**
+ * <code>mult</code> multiplies this vector by a scalar. The resultant
+ * vector is returned.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @return the new vector.
+ */
+ public Vector2f mult(float scalar) {
+ return new Vector2f(x * scalar, y * scalar);
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @return this
+ */
+ public Vector2f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @return this
+ */
+ public Vector2f multLocal(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x *= vec.x;
+ y *= vec.y;
+ return this;
+ }
+
+ /**
+ * Multiplies this Vector2f's x and y by the scalar and stores the result in
+ * product. The result is returned for chaining. Similar to
+ * product=this*scalar;
+ *
+ * @param scalar
+ * The scalar to multiply by.
+ * @param product
+ * The vector2f to store the result in.
+ * @return product, after multiplication.
+ */
+ public Vector2f mult(float scalar, Vector2f product) {
+ if (null == product) {
+ product = new Vector2f();
+ }
+
+ product.x = x * scalar;
+ product.y = y * scalar;
+ return product;
+ }
+
+ /**
+ * <code>divide</code> divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar
+ * the value to divide this vectors attributes by.
+ * @return the result <code>Vector</code>.
+ */
+ public Vector2f divide(float scalar) {
+ return new Vector2f(x / scalar, y / scalar);
+ }
+
+ /**
+ * <code>divideLocal</code> divides this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls. Dividing
+ * by zero will result in an exception.
+ *
+ * @param scalar
+ * the value to divides this vector by.
+ * @return this
+ */
+ public Vector2f divideLocal(float scalar) {
+ x /= scalar;
+ y /= scalar;
+ return this;
+ }
+
+ /**
+ * <code>negate</code> returns the negative of this vector. All values are
+ * negated and set to a new vector.
+ *
+ * @return the negated vector.
+ */
+ public Vector2f negate() {
+ return new Vector2f(-x, -y);
+ }
+
+ /**
+ * <code>negateLocal</code> negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector2f negateLocal() {
+ x = -x;
+ y = -y;
+ return this;
+ }
+
+ /**
+ * <code>subtract</code> subtracts the values of a given vector from those
+ * of this vector creating a new vector object. If the provided vector is
+ * null, an exception is thrown.
+ *
+ * @param vec
+ * the vector to subtract from this vector.
+ * @return the result vector.
+ */
+ public Vector2f subtract(Vector2f vec) {
+ return subtract(vec, null);
+ }
+
+ /**
+ * <code>subtract</code> subtracts the values of a given vector from those
+ * of this vector storing the result in the given vector object. If the
+ * provided vector is null, an exception is thrown.
+ *
+ * @param vec
+ * the vector to subtract from this vector.
+ * @param store
+ * the vector to store the result in. It is safe for this to be
+ * the same as vec. If null, a new vector is created.
+ * @return the result vector.
+ */
+ public Vector2f subtract(Vector2f vec, Vector2f store) {
+ if (store == null)
+ store = new Vector2f();
+ store.x = x - vec.x;
+ store.y = y - vec.y;
+ return store;
+ }
+
+ /**
+ * <code>subtract</code> subtracts the given x,y values from those of this
+ * vector creating a new vector object.
+ *
+ * @param valX
+ * value to subtract from x
+ * @param valY
+ * value to subtract from y
+ * @return this
+ */
+ public Vector2f subtract(float valX, float valY) {
+ return new Vector2f(x - valX, y - valY);
+ }
+
+ /**
+ * <code>subtractLocal</code> subtracts a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to subtract
+ * @return this
+ */
+ public Vector2f subtractLocal(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ return this;
+ }
+
+ /**
+ * <code>subtractLocal</code> subtracts the provided values from this
+ * vector internally, and returns a handle to this vector for easy chaining
+ * of calls.
+ *
+ * @param valX
+ * value to subtract from x
+ * @param valY
+ * value to subtract from y
+ * @return this
+ */
+ public Vector2f subtractLocal(float valX, float valY) {
+ x -= valX;
+ y -= valY;
+ return this;
+ }
+
+ /**
+ * <code>normalize</code> returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector2f normalize() {
+ float length = length();
+ if (length != 0) {
+ return divide(length);
+ }
+
+ return divide(1);
+ }
+
+ /**
+ * <code>normalizeLocal</code> makes this vector into a unit vector of
+ * itself.
+ *
+ * @return this.
+ */
+ public Vector2f normalizeLocal() {
+ float length = length();
+ if (length != 0) {
+ return divideLocal(length);
+ }
+
+ return divideLocal(1);
+ }
+
+ /**
+ * <code>smallestAngleBetween</code> returns (in radians) the minimum
+ * angle between two vectors. It is assumed that both this vector and the
+ * given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector
+ * a unit vector to find the angle against
+ * @return the angle in radians.
+ */
+ public float smallestAngleBetween(Vector2f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = FastMath.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * <code>angleBetween</code> returns (in radians) the angle required to
+ * rotate a ray represented by this vector to lie colinear to a ray
+ * described by the given vector. It is assumed that both this vector and
+ * the given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector
+ * the "destination" unit vector
+ * @return the angle in radians.
+ */
+ public float angleBetween(Vector2f otherVector) {
+ float angle = FastMath.atan2(otherVector.y, otherVector.x)
+ - FastMath.atan2(y, x);
+ return angle;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector2f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector2f setY(float y) {
+ this.y = y;
+ return this;
+ }
+ /**
+ * <code>getAngle</code> returns (in radians) the angle represented by
+ * this Vector2f as expressed by a conversion from rectangular coordinates (<code>x</code>,&nbsp;<code>y</code>)
+ * to polar coordinates (r,&nbsp;<i>theta</i>).
+ *
+ * @return the angle in radians. [-pi, pi)
+ */
+ public float getAngle() {
+ return FastMath.atan2(y, x);
+ }
+
+ /**
+ * <code>zero</code> resets this vector's data to zero internally.
+ */
+ public Vector2f zero() {
+ x = y = 0;
+ return this;
+ }
+
+ /**
+ * <code>hashCode</code> returns a unique code for this vector object
+ * based on it's values. If two vectors are logically equivalent, they will
+ * return the same hash code value.
+ *
+ * @return the hash code value of this vector.
+ */
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(x);
+ hash += 37 * hash + Float.floatToIntBits(y);
+ return hash;
+ }
+
+ @Override
+ public Vector2f clone() {
+ try {
+ return (Vector2f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this Vector2f into the given float[] object.
+ *
+ * @param floats
+ * The float[] to take this Vector2f. If null, a new float[2] is
+ * created.
+ * @return The array, with X, Y float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[2];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ return floats;
+ }
+
+ /**
+ * are these two vectors the same? they are is they both have the same x and
+ * y values.
+ *
+ * @param o
+ * the object to compare for equality
+ * @return true if they are equal
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof Vector2f)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Vector2f comp = (Vector2f) o;
+ if (Float.compare(x, comp.x) != 0)
+ return false;
+ if (Float.compare(y, comp.y) != 0)
+ return false;
+ return true;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this vector
+ * object. The format of the string is such: com.jme.math.Vector2f
+ * [X=XX.XXXX, Y=YY.YYYY]
+ *
+ * @return the string representation of this vector.
+ */
+ public String toString() {
+ return "(" + x + ", " + y + ")";
+ }
+
+ /**
+ * Used with serialization. Not to be called manually.
+ *
+ * @param in
+ * ObjectInput
+ * @throws IOException
+ * @throws ClassNotFoundException
+ * @see java.io.Externalizable
+ */
+ public void readExternal(ObjectInput in) throws IOException,
+ ClassNotFoundException {
+ x = in.readFloat();
+ y = in.readFloat();
+ }
+
+ /**
+ * Used with serialization. Not to be called manually.
+ *
+ * @param out
+ * ObjectOutput
+ * @throws IOException
+ * @see java.io.Externalizable
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeFloat(x);
+ out.writeFloat(y);
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(x, "x", 0);
+ capsule.write(y, "y", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ x = capsule.readFloat("x", 0);
+ y = capsule.readFloat("y", 0);
+ }
+
+ public void rotateAroundOrigin(float angle, boolean cw) {
+ if (cw)
+ angle = -angle;
+ float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
+ float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
+ x = newX;
+ y = newY;
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Vector3f.java b/engine/src/core/com/jme3/math/Vector3f.java
new file mode 100644
index 0000000..9f6f851
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Vector3f.java
@@ -0,0 +1,1061 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/*
+ * -- Added *Local methods to cut down on object creation - JS
+ */
+
+/**
+ * <code>Vector3f</code> defines a Vector for a three float value tuple.
+ * <code>Vector3f</code> can represent any three dimensional value, such as a
+ * vertex, a normal, etc. Utility methods are also included to aid in
+ * mathematical calculations.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Vector3f implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Vector3f.class.getName());
+
+ public final static Vector3f ZERO = new Vector3f(0, 0, 0);
+ public final static Vector3f NAN = new Vector3f(Float.NaN, Float.NaN, Float.NaN);
+ public final static Vector3f UNIT_X = new Vector3f(1, 0, 0);
+ public final static Vector3f UNIT_Y = new Vector3f(0, 1, 0);
+ public final static Vector3f UNIT_Z = new Vector3f(0, 0, 1);
+ public final static Vector3f UNIT_XYZ = new Vector3f(1, 1, 1);
+ public final static Vector3f POSITIVE_INFINITY = new Vector3f(
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY);
+ public final static Vector3f NEGATIVE_INFINITY = new Vector3f(
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY);
+
+
+ /**
+ * the x value of the vector.
+ */
+ public float x;
+
+ /**
+ * the y value of the vector.
+ */
+ public float y;
+
+ /**
+ * the z value of the vector.
+ */
+ public float z;
+
+ /**
+ * Constructor instantiates a new <code>Vector3f</code> with default
+ * values of (0,0,0).
+ *
+ */
+ public Vector3f() {
+ x = y = z = 0;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Vector3f</code> with provides
+ * values.
+ *
+ * @param x
+ * the x value of the vector.
+ * @param y
+ * the y value of the vector.
+ * @param z
+ * the z value of the vector.
+ */
+ public Vector3f(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Vector3f</code> that is a copy
+ * of the provided vector
+ * @param copy The Vector3f to copy
+ */
+ public Vector3f(Vector3f copy) {
+ this.set(copy);
+ }
+
+ /**
+ * <code>set</code> sets the x,y,z values of the vector based on passed
+ * parameters.
+ *
+ * @param x
+ * the x value of the vector.
+ * @param y
+ * the y value of the vector.
+ * @param z
+ * the z value of the vector.
+ * @return this vector
+ */
+ public Vector3f set(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ /**
+ * <code>set</code> sets the x,y,z values of the vector by copying the
+ * supplied vector.
+ *
+ * @param vect
+ * the vector to copy.
+ * @return this vector
+ */
+ public Vector3f set(Vector3f vect) {
+ this.x = vect.x;
+ this.y = vect.y;
+ this.z = vect.z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>add</code> adds a provided vector to this vector creating a
+ * resultant vector which is returned. If the provided vector is null, null
+ * is returned.
+ *
+ * @param vec
+ * the vector to add to this.
+ * @return the resultant vector.
+ */
+ public Vector3f add(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return new Vector3f(x + vec.x, y + vec.y, z + vec.z);
+ }
+
+ /**
+ *
+ * <code>add</code> adds the values of a provided vector storing the
+ * values in the supplied vector.
+ *
+ * @param vec
+ * the vector to add to this
+ * @param result
+ * the vector to store the result in
+ * @return result returns the supplied result vector.
+ */
+ public Vector3f add(Vector3f vec, Vector3f result) {
+ result.x = x + vec.x;
+ result.y = y + vec.y;
+ result.z = z + vec.z;
+ return result;
+ }
+
+ /**
+ * <code>addLocal</code> adds a provided vector to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls. If the
+ * provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to add to this vector.
+ * @return this
+ */
+ public Vector3f addLocal(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x += vec.x;
+ y += vec.y;
+ z += vec.z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>add</code> adds the provided values to this vector, creating a
+ * new vector that is then returned.
+ *
+ * @param addX
+ * the x value to add.
+ * @param addY
+ * the y value to add.
+ * @param addZ
+ * the z value to add.
+ * @return the result vector.
+ */
+ public Vector3f add(float addX, float addY, float addZ) {
+ return new Vector3f(x + addX, y + addY, z + addZ);
+ }
+
+ /**
+ * <code>addLocal</code> adds the provided values to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param addX
+ * value to add to x
+ * @param addY
+ * value to add to y
+ * @param addZ
+ * value to add to z
+ * @return this
+ */
+ public Vector3f addLocal(float addX, float addY, float addZ) {
+ x += addX;
+ y += addY;
+ z += addZ;
+ return this;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> multiplies this vector by a scalar then adds the
+ * given Vector3f.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @param add
+ * the value to add
+ */
+ public Vector3f scaleAdd(float scalar, Vector3f add) {
+ x = x * scalar + add.x;
+ y = y * scalar + add.y;
+ z = z * scalar + add.z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> multiplies the given vector by a scalar then adds
+ * the given vector.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @param mult
+ * the value to multiply the scalar by
+ * @param add
+ * the value to add
+ */
+ public Vector3f scaleAdd(float scalar, Vector3f mult, Vector3f add) {
+ this.x = mult.x * scalar + add.x;
+ this.y = mult.y * scalar + add.y;
+ this.z = mult.z * scalar + add.z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>dot</code> calculates the dot product of this vector with a
+ * provided vector. If the provided vector is null, 0 is returned.
+ *
+ * @param vec
+ * the vector to dot with this vector.
+ * @return the resultant dot product of this vector and a given vector.
+ */
+ public float dot(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, 0 returned.");
+ return 0;
+ }
+ return x * vec.x + y * vec.y + z * vec.z;
+ }
+
+ /**
+ * <code>cross</code> calculates the cross product of this vector with a
+ * parameter vector v.
+ *
+ * @param v
+ * the vector to take the cross product of with this.
+ * @return the cross product vector.
+ */
+ public Vector3f cross(Vector3f v) {
+ return cross(v, null);
+ }
+
+ /**
+ * <code>cross</code> calculates the cross product of this vector with a
+ * parameter vector v. The result is stored in <code>result</code>
+ *
+ * @param v
+ * the vector to take the cross product of with this.
+ * @param result
+ * the vector to store the cross product result.
+ * @return result, after recieving the cross product vector.
+ */
+ public Vector3f cross(Vector3f v,Vector3f result) {
+ return cross(v.x, v.y, v.z, result);
+ }
+
+ /**
+ * <code>cross</code> calculates the cross product of this vector with a
+ * parameter vector v. The result is stored in <code>result</code>
+ *
+ * @param otherX
+ * x component of the vector to take the cross product of with this.
+ * @param otherY
+ * y component of the vector to take the cross product of with this.
+ * @param otherZ
+ * z component of the vector to take the cross product of with this.
+ * @param result
+ * the vector to store the cross product result.
+ * @return result, after recieving the cross product vector.
+ */
+ public Vector3f cross(float otherX, float otherY, float otherZ, Vector3f result) {
+ if (result == null) result = new Vector3f();
+ float resX = ((y * otherZ) - (z * otherY));
+ float resY = ((z * otherX) - (x * otherZ));
+ float resZ = ((x * otherY) - (y * otherX));
+ result.set(resX, resY, resZ);
+ return result;
+ }
+
+ /**
+ * <code>crossLocal</code> calculates the cross product of this vector
+ * with a parameter vector v.
+ *
+ * @param v
+ * the vector to take the cross product of with this.
+ * @return this.
+ */
+ public Vector3f crossLocal(Vector3f v) {
+ return crossLocal(v.x, v.y, v.z);
+ }
+
+ /**
+ * <code>crossLocal</code> calculates the cross product of this vector
+ * with a parameter vector v.
+ *
+ * @param otherX
+ * x component of the vector to take the cross product of with this.
+ * @param otherY
+ * y component of the vector to take the cross product of with this.
+ * @param otherZ
+ * z component of the vector to take the cross product of with this.
+ * @return this.
+ */
+ public Vector3f crossLocal(float otherX, float otherY, float otherZ) {
+ float tempx = ( y * otherZ ) - ( z * otherY );
+ float tempy = ( z * otherX ) - ( x * otherZ );
+ z = (x * otherY) - (y * otherX);
+ x = tempx;
+ y = tempy;
+ return this;
+ }
+
+ public Vector3f project(Vector3f other){
+ float n = this.dot(other); // A . B
+ float d = other.lengthSquared(); // |B|^2
+ return new Vector3f(other).normalizeLocal().multLocal(n/d);
+ }
+
+ /**
+ * Returns true if this vector is a unit vector (length() ~= 1),
+ * returns false otherwise.
+ *
+ * @return true if this vector is a unit vector (length() ~= 1),
+ * or false otherwise.
+ */
+ public boolean isUnitVector(){
+ float len = length();
+ return 0.99f < len && len < 1.01f;
+ }
+
+ /**
+ * <code>length</code> calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * <code>lengthSquared</code> calculates the squared value of the
+ * magnitude of the vector.
+ *
+ * @return the magnitude squared of the vector.
+ */
+ public float lengthSquared() {
+ return x * x + y * y + z * z;
+ }
+
+ /**
+ * <code>distanceSquared</code> calculates the distance squared between
+ * this vector and vector v.
+ *
+ * @param v the second vector to determine the distance squared.
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(Vector3f v) {
+ double dx = x - v.x;
+ double dy = y - v.y;
+ double dz = z - v.z;
+ return (float) (dx * dx + dy * dy + dz * dz);
+ }
+
+ /**
+ * <code>distance</code> calculates the distance between this vector and
+ * vector v.
+ *
+ * @param v the second vector to determine the distance.
+ * @return the distance between the two vectors.
+ */
+ public float distance(Vector3f v) {
+ return FastMath.sqrt(distanceSquared(v));
+ }
+
+ /**
+ *
+ * <code>mult</code> multiplies this vector by a scalar. The resultant
+ * vector is returned.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @return the new vector.
+ */
+ public Vector3f mult(float scalar) {
+ return new Vector3f(x * scalar, y * scalar, z * scalar);
+ }
+
+ /**
+ *
+ * <code>mult</code> multiplies this vector by a scalar. The resultant
+ * vector is supplied as the second parameter and returned.
+ *
+ * @param scalar the scalar to multiply this vector by.
+ * @param product the product to store the result in.
+ * @return product
+ */
+ public Vector3f mult(float scalar, Vector3f product) {
+ if (null == product) {
+ product = new Vector3f();
+ }
+
+ product.x = x * scalar;
+ product.y = y * scalar;
+ product.z = z * scalar;
+ return product;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @return this
+ */
+ public Vector3f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @return this
+ */
+ public Vector3f multLocal(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x *= vec.x;
+ y *= vec.y;
+ z *= vec.z;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this vector by 3 scalars
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @return this
+ */
+ public Vector3f multLocal(float x, float y, float z) {
+ this.x *= x;
+ this.y *= y;
+ this.z *= z;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @return this
+ */
+ public Vector3f mult(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return mult(vec, null);
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @param store result vector (null to create a new vector)
+ * @return this
+ */
+ public Vector3f mult(Vector3f vec, Vector3f store) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ if (store == null) store = new Vector3f();
+ return store.set(x * vec.x, y * vec.y, z * vec.z);
+ }
+
+
+ /**
+ * <code>divide</code> divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar
+ * the value to divide this vectors attributes by.
+ * @return the result <code>Vector</code>.
+ */
+ public Vector3f divide(float scalar) {
+ scalar = 1f/scalar;
+ return new Vector3f(x * scalar, y * scalar, z * scalar);
+ }
+
+ /**
+ * <code>divideLocal</code> divides this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls. Dividing
+ * by zero will result in an exception.
+ *
+ * @param scalar
+ * the value to divides this vector by.
+ * @return this
+ */
+ public Vector3f divideLocal(float scalar) {
+ scalar = 1f/scalar;
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+
+ /**
+ * <code>divide</code> divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar
+ * the value to divide this vectors attributes by.
+ * @return the result <code>Vector</code>.
+ */
+ public Vector3f divide(Vector3f scalar) {
+ return new Vector3f(x / scalar.x, y / scalar.y, z / scalar.z);
+ }
+
+ /**
+ * <code>divideLocal</code> divides this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls. Dividing
+ * by zero will result in an exception.
+ *
+ * @param scalar
+ * the value to divides this vector by.
+ * @return this
+ */
+ public Vector3f divideLocal(Vector3f scalar) {
+ x /= scalar.x;
+ y /= scalar.y;
+ z /= scalar.z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>negate</code> returns the negative of this vector. All values are
+ * negated and set to a new vector.
+ *
+ * @return the negated vector.
+ */
+ public Vector3f negate() {
+ return new Vector3f(-x, -y, -z);
+ }
+
+ /**
+ *
+ * <code>negateLocal</code> negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector3f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>subtract</code> subtracts the values of a given vector from those
+ * of this vector creating a new vector object. If the provided vector is
+ * null, null is returned.
+ *
+ * @param vec
+ * the vector to subtract from this vector.
+ * @return the result vector.
+ */
+ public Vector3f subtract(Vector3f vec) {
+ return new Vector3f(x - vec.x, y - vec.y, z - vec.z);
+ }
+
+ /**
+ * <code>subtractLocal</code> subtracts a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to subtract
+ * @return this
+ */
+ public Vector3f subtractLocal(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ z -= vec.z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>subtract</code>
+ *
+ * @param vec
+ * the vector to subtract from this
+ * @param result
+ * the vector to store the result in
+ * @return result
+ */
+ public Vector3f subtract(Vector3f vec, Vector3f result) {
+ if(result == null) {
+ result = new Vector3f();
+ }
+ result.x = x - vec.x;
+ result.y = y - vec.y;
+ result.z = z - vec.z;
+ return result;
+ }
+
+ /**
+ *
+ * <code>subtract</code> subtracts the provided values from this vector,
+ * creating a new vector that is then returned.
+ *
+ * @param subtractX
+ * the x value to subtract.
+ * @param subtractY
+ * the y value to subtract.
+ * @param subtractZ
+ * the z value to subtract.
+ * @return the result vector.
+ */
+ public Vector3f subtract(float subtractX, float subtractY, float subtractZ) {
+ return new Vector3f(x - subtractX, y - subtractY, z - subtractZ);
+ }
+
+ /**
+ * <code>subtractLocal</code> subtracts the provided values from this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param subtractX
+ * the x value to subtract.
+ * @param subtractY
+ * the y value to subtract.
+ * @param subtractZ
+ * the z value to subtract.
+ * @return this
+ */
+ public Vector3f subtractLocal(float subtractX, float subtractY, float subtractZ) {
+ x -= subtractX;
+ y -= subtractY;
+ z -= subtractZ;
+ return this;
+ }
+
+ /**
+ * <code>normalize</code> returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector3f normalize() {
+// float length = length();
+// if (length != 0) {
+// return divide(length);
+// }
+//
+// return divide(1);
+ float length = x * x + y * y + z * z;
+ if (length != 1f && length != 0f){
+ length = 1.0f / FastMath.sqrt(length);
+ return new Vector3f(x * length, y * length, z * length);
+ }
+ return clone();
+ }
+
+ /**
+ * <code>normalizeLocal</code> makes this vector into a unit vector of
+ * itself.
+ *
+ * @return this.
+ */
+ public Vector3f normalizeLocal() {
+ // NOTE: this implementation is more optimized
+ // than the old jme normalize as this method
+ // is commonly used.
+ float length = x * x + y * y + z * z;
+ if (length != 1f && length != 0f){
+ length = 1.0f / FastMath.sqrt(length);
+ x *= length;
+ y *= length;
+ z *= length;
+ }
+ return this;
+ }
+
+ /**
+ * <code>maxLocal</code> computes the maximum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void maxLocal(Vector3f other){
+ x = other.x > x ? other.x : x;
+ y = other.y > y ? other.y : y;
+ z = other.z > z ? other.z : z;
+ }
+
+ /**
+ * <code>minLocal</code> computes the minimum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void minLocal(Vector3f other){
+ x = other.x < x ? other.x : x;
+ y = other.y < y ? other.y : y;
+ z = other.z < z ? other.z : z;
+ }
+
+ /**
+ * <code>zero</code> resets this vector's data to zero internally.
+ */
+ public Vector3f zero() {
+ x = y = z = 0;
+ return this;
+ }
+
+ /**
+ * <code>angleBetween</code> returns (in radians) the angle between two vectors.
+ * It is assumed that both this vector and the given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector a unit vector to find the angle against
+ * @return the angle in radians.
+ */
+ public float angleBetween(Vector3f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = FastMath.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the finalVec
+ * this=(1-changeAmnt)*this + changeAmnt * finalVec
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from this towards finalVec
+ */
+ public Vector3f interpolate(Vector3f finalVec, float changeAmnt) {
+ this.x=(1-changeAmnt)*this.x + changeAmnt*finalVec.x;
+ this.y=(1-changeAmnt)*this.y + changeAmnt*finalVec.y;
+ this.z=(1-changeAmnt)*this.z + changeAmnt*finalVec.z;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to finalVec
+ * this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ * @param beginVec the beging vector (changeAmnt=0)
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginVec towards finalVec
+ */
+ public Vector3f interpolate(Vector3f beginVec,Vector3f finalVec, float changeAmnt) {
+ this.x=(1-changeAmnt)*beginVec.x + changeAmnt*finalVec.x;
+ this.y=(1-changeAmnt)*beginVec.y + changeAmnt*finalVec.y;
+ this.z=(1-changeAmnt)*beginVec.z + changeAmnt*finalVec.z;
+ return this;
+ }
+
+ /**
+ * Check a vector... if it is null or its floats are NaN or infinite,
+ * return false. Else return true.
+ * @param vector the vector to check
+ * @return true or false as stated above.
+ */
+ public static boolean isValidVector(Vector3f vector) {
+ if (vector == null) return false;
+ if (Float.isNaN(vector.x) ||
+ Float.isNaN(vector.y) ||
+ Float.isNaN(vector.z)) return false;
+ if (Float.isInfinite(vector.x) ||
+ Float.isInfinite(vector.y) ||
+ Float.isInfinite(vector.z)) return false;
+ return true;
+ }
+
+ public static void generateOrthonormalBasis(Vector3f u, Vector3f v, Vector3f w) {
+ w.normalizeLocal();
+ generateComplementBasis(u, v, w);
+ }
+
+ public static void generateComplementBasis(Vector3f u, Vector3f v,
+ Vector3f w) {
+ float fInvLength;
+
+ if (FastMath.abs(w.x) >= FastMath.abs(w.y)) {
+ // w.x or w.z is the largest magnitude component, swap them
+ fInvLength = FastMath.invSqrt(w.x * w.x + w.z * w.z);
+ u.x = -w.z * fInvLength;
+ u.y = 0.0f;
+ u.z = +w.x * fInvLength;
+ v.x = w.y * u.z;
+ v.y = w.z * u.x - w.x * u.z;
+ v.z = -w.y * u.x;
+ } else {
+ // w.y or w.z is the largest magnitude component, swap them
+ fInvLength = FastMath.invSqrt(w.y * w.y + w.z * w.z);
+ u.x = 0.0f;
+ u.y = +w.z * fInvLength;
+ u.z = -w.y * fInvLength;
+ v.x = w.y * u.z - w.z * u.y;
+ v.y = -w.x * u.z;
+ v.z = w.x * u.y;
+ }
+ }
+
+ @Override
+ public Vector3f clone() {
+ try {
+ return (Vector3f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this Vector3f into the given float[] object.
+ *
+ * @param floats
+ * The float[] to take this Vector3f. If null, a new float[3] is
+ * created.
+ * @return The array, with X, Y, Z float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[3];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ floats[2] = z;
+ return floats;
+ }
+
+ /**
+ * are these two vectors the same? they are is they both have the same x,y,
+ * and z values.
+ *
+ * @param o
+ * the object to compare for equality
+ * @return true if they are equal
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof Vector3f)) { return false; }
+
+ if (this == o) { return true; }
+
+ Vector3f comp = (Vector3f) o;
+ if (Float.compare(x,comp.x) != 0) return false;
+ if (Float.compare(y,comp.y) != 0) return false;
+ if (Float.compare(z,comp.z) != 0) return false;
+ return true;
+ }
+
+ /**
+ * <code>hashCode</code> returns a unique code for this vector object based
+ * on it's values. If two vectors are logically equivalent, they will return
+ * the same hash code value.
+ * @return the hash code value of this vector.
+ */
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(x);
+ hash += 37 * hash + Float.floatToIntBits(y);
+ hash += 37 * hash + Float.floatToIntBits(z);
+ return hash;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this vector.
+ * The format is:
+ *
+ * org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ]
+ *
+ * @return the string representation of this vector.
+ */
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ")";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(x, "x", 0);
+ capsule.write(y, "y", 0);
+ capsule.write(z, "z", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ x = capsule.readFloat("x", 0);
+ y = capsule.readFloat("y", 0);
+ z = capsule.readFloat("z", 0);
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector3f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector3f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public Vector3f setZ(float z) {
+ this.z = z;
+ return this;
+ }
+
+ /**
+ * @param index
+ * @return x value if index == 0, y value if index == 1 or z value if index ==
+ * 2
+ * @throws IllegalArgumentException
+ * if index is not one of 0, 1, 2.
+ */
+ public float get(int index) {
+ switch (index) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1 or 2");
+ }
+
+ /**
+ * @param index
+ * which field index in this vector to set.
+ * @param value
+ * to set to one of x, y or z.
+ * @throws IllegalArgumentException
+ * if index is not one of 0, 1, 2.
+ */
+ public void set(int index, float value) {
+ switch (index) {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1 or 2");
+ }
+
+}
diff --git a/engine/src/core/com/jme3/math/Vector4f.java b/engine/src/core/com/jme3/math/Vector4f.java
new file mode 100644
index 0000000..d6e7ad7
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Vector4f.java
@@ -0,0 +1,1003 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * <code>Vector4f</code> defines a Vector for a four float value tuple.
+ * <code>Vector4f</code> can represent any four dimensional value, such as a
+ * vertex, a normal, etc. Utility methods are also included to aid in
+ * mathematical calculations.
+ *
+ * @author Maarten Steur
+ */
+public final class Vector4f implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Vector4f.class.getName());
+
+ public final static Vector4f ZERO = new Vector4f(0, 0, 0, 0);
+ public final static Vector4f NAN = new Vector4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
+ public final static Vector4f UNIT_X = new Vector4f(1, 0, 0, 0);
+ public final static Vector4f UNIT_Y = new Vector4f(0, 1, 0, 0);
+ public final static Vector4f UNIT_Z = new Vector4f(0, 0, 1, 0);
+ public final static Vector4f UNIT_W = new Vector4f(0, 0, 0, 1);
+ public final static Vector4f UNIT_XYZW = new Vector4f(1, 1, 1, 1);
+ public final static Vector4f POSITIVE_INFINITY = new Vector4f(
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY);
+ public final static Vector4f NEGATIVE_INFINITY = new Vector4f(
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY);
+
+ /**
+ * the x value of the vector.
+ */
+ public float x;
+
+ /**
+ * the y value of the vector.
+ */
+ public float y;
+
+ /**
+ * the z value of the vector.
+ */
+ public float z;
+
+ /**
+ * the w value of the vector.
+ */
+ public float w;
+
+ /**
+ * Constructor instantiates a new <code>Vector3f</code> with default
+ * values of (0,0,0).
+ *
+ */
+ public Vector4f() {
+ x = y = z = w = 0;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Vector4f</code> with provides
+ * values.
+ *
+ * @param x
+ * the x value of the vector.
+ * @param y
+ * the y value of the vector.
+ * @param z
+ * the z value of the vector.
+ * @param w
+ * the w value of the vector.
+ */
+ public Vector4f(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Vector3f</code> that is a copy
+ * of the provided vector
+ * @param copy The Vector3f to copy
+ */
+ public Vector4f(Vector4f copy) {
+ this.set(copy);
+ }
+
+ /**
+ * <code>set</code> sets the x,y,z,w values of the vector based on passed
+ * parameters.
+ *
+ * @param x
+ * the x value of the vector.
+ * @param y
+ * the y value of the vector.
+ * @param z
+ * the z value of the vector.
+ * @param w
+ * the w value of the vector.
+ * @return this vector
+ */
+ public Vector4f set(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ return this;
+ }
+
+ /**
+ * <code>set</code> sets the x,y,z values of the vector by copying the
+ * supplied vector.
+ *
+ * @param vect
+ * the vector to copy.
+ * @return this vector
+ */
+ public Vector4f set(Vector4f vect) {
+ this.x = vect.x;
+ this.y = vect.y;
+ this.z = vect.z;
+ this.w = vect.w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>add</code> adds a provided vector to this vector creating a
+ * resultant vector which is returned. If the provided vector is null, null
+ * is returned.
+ *
+ * @param vec
+ * the vector to add to this.
+ * @return the resultant vector.
+ */
+ public Vector4f add(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return new Vector4f(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
+ }
+
+ /**
+ *
+ * <code>add</code> adds the values of a provided vector storing the
+ * values in the supplied vector.
+ *
+ * @param vec
+ * the vector to add to this
+ * @param result
+ * the vector to store the result in
+ * @return result returns the supplied result vector.
+ */
+ public Vector4f add(Vector4f vec, Vector4f result) {
+ result.x = x + vec.x;
+ result.y = y + vec.y;
+ result.z = z + vec.z;
+ result.w = w + vec.w;
+ return result;
+ }
+
+ /**
+ * <code>addLocal</code> adds a provided vector to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls. If the
+ * provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to add to this vector.
+ * @return this
+ */
+ public Vector4f addLocal(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x += vec.x;
+ y += vec.y;
+ z += vec.z;
+ w += vec.w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>add</code> adds the provided values to this vector, creating a
+ * new vector that is then returned.
+ *
+ * @param addX
+ * the x value to add.
+ * @param addY
+ * the y value to add.
+ * @param addZ
+ * the z value to add.
+ * @return the result vector.
+ */
+ public Vector4f add(float addX, float addY, float addZ, float addW) {
+ return new Vector4f(x + addX, y + addY, z + addZ, w + addW);
+ }
+
+ /**
+ * <code>addLocal</code> adds the provided values to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param addX
+ * value to add to x
+ * @param addY
+ * value to add to y
+ * @param addZ
+ * value to add to z
+ * @return this
+ */
+ public Vector4f addLocal(float addX, float addY, float addZ, float addW) {
+ x += addX;
+ y += addY;
+ z += addZ;
+ w += addW;
+ return this;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> multiplies this vector by a scalar then adds the
+ * given Vector3f.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @param add
+ * the value to add
+ */
+ public Vector4f scaleAdd(float scalar, Vector4f add) {
+ x = x * scalar + add.x;
+ y = y * scalar + add.y;
+ z = z * scalar + add.z;
+ w = w * scalar + add.w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> multiplies the given vector by a scalar then adds
+ * the given vector.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @param mult
+ * the value to multiply the scalar by
+ * @param add
+ * the value to add
+ */
+ public Vector4f scaleAdd(float scalar, Vector4f mult, Vector4f add) {
+ this.x = mult.x * scalar + add.x;
+ this.y = mult.y * scalar + add.y;
+ this.z = mult.z * scalar + add.z;
+ this.w = mult.w * scalar + add.w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>dot</code> calculates the dot product of this vector with a
+ * provided vector. If the provided vector is null, 0 is returned.
+ *
+ * @param vec
+ * the vector to dot with this vector.
+ * @return the resultant dot product of this vector and a given vector.
+ */
+ public float dot(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, 0 returned.");
+ return 0;
+ }
+ return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
+ }
+
+ public Vector4f project(Vector4f other){
+ float n = this.dot(other); // A . B
+ float d = other.lengthSquared(); // |B|^2
+ return new Vector4f(other).normalizeLocal().multLocal(n/d);
+ }
+
+ /**
+ * Returns true if this vector is a unit vector (length() ~= 1),
+ * returns false otherwise.
+ *
+ * @return true if this vector is a unit vector (length() ~= 1),
+ * or false otherwise.
+ */
+ public boolean isUnitVector(){
+ float len = length();
+ return 0.99f < len && len < 1.01f;
+ }
+
+ /**
+ * <code>length</code> calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * <code>lengthSquared</code> calculates the squared value of the
+ * magnitude of the vector.
+ *
+ * @return the magnitude squared of the vector.
+ */
+ public float lengthSquared() {
+ return x * x + y * y + z * z + w * w;
+ }
+
+ /**
+ * <code>distanceSquared</code> calculates the distance squared between
+ * this vector and vector v.
+ *
+ * @param v the second vector to determine the distance squared.
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(Vector4f v) {
+ double dx = x - v.x;
+ double dy = y - v.y;
+ double dz = z - v.z;
+ double dw = w - v.w;
+ return (float) (dx * dx + dy * dy + dz * dz + dw * dw);
+ }
+
+ /**
+ * <code>distance</code> calculates the distance between this vector and
+ * vector v.
+ *
+ * @param v the second vector to determine the distance.
+ * @return the distance between the two vectors.
+ */
+ public float distance(Vector4f v) {
+ return FastMath.sqrt(distanceSquared(v));
+ }
+
+ /**
+ *
+ * <code>mult</code> multiplies this vector by a scalar. The resultant
+ * vector is returned.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @return the new vector.
+ */
+ public Vector4f mult(float scalar) {
+ return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
+ }
+
+ /**
+ *
+ * <code>mult</code> multiplies this vector by a scalar. The resultant
+ * vector is supplied as the second parameter and returned.
+ *
+ * @param scalar the scalar to multiply this vector by.
+ * @param product the product to store the result in.
+ * @return product
+ */
+ public Vector4f mult(float scalar, Vector4f product) {
+ if (null == product) {
+ product = new Vector4f();
+ }
+
+ product.x = x * scalar;
+ product.y = y * scalar;
+ product.z = z * scalar;
+ product.w = w * scalar;
+ return product;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param scalar
+ * the value to multiply this vector by.
+ * @return this
+ */
+ public Vector4f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ w *= scalar;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @return this
+ */
+ public Vector4f multLocal(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x *= vec.x;
+ y *= vec.y;
+ z *= vec.z;
+ w *= vec.w;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this vector by 3 scalars
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param w
+ * @return this
+ */
+ public Vector4f multLocal(float x, float y, float z, float w) {
+ this.x *= x;
+ this.y *= y;
+ this.z *= z;
+ this.w *= w;
+ return this;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @return this
+ */
+ public Vector4f mult(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return mult(vec, null);
+ }
+
+ /**
+ * <code>multLocal</code> multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to mult to this vector.
+ * @param store result vector (null to create a new vector)
+ * @return this
+ */
+ public Vector4f mult(Vector4f vec, Vector4f store) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ if (store == null) store = new Vector4f();
+ return store.set(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
+ }
+
+ /**
+ * <code>divide</code> divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar
+ * the value to divide this vectors attributes by.
+ * @return the result <code>Vector</code>.
+ */
+ public Vector4f divide(float scalar) {
+ scalar = 1f/scalar;
+ return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
+ }
+
+ /**
+ * <code>divideLocal</code> divides this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls. Dividing
+ * by zero will result in an exception.
+ *
+ * @param scalar
+ * the value to divides this vector by.
+ * @return this
+ */
+ public Vector4f divideLocal(float scalar) {
+ scalar = 1f/scalar;
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ w *= scalar;
+ return this;
+ }
+
+ /**
+ * <code>divide</code> divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar
+ * the value to divide this vectors attributes by.
+ * @return the result <code>Vector</code>.
+ */
+ public Vector4f divide(Vector4f scalar) {
+ return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
+ }
+
+ /**
+ * <code>divideLocal</code> divides this vector by a scalar internally,
+ * and returns a handle to this vector for easy chaining of calls. Dividing
+ * by zero will result in an exception.
+ *
+ * @param scalar
+ * the value to divides this vector by.
+ * @return this
+ */
+ public Vector4f divideLocal(Vector4f scalar) {
+ x /= scalar.x;
+ y /= scalar.y;
+ z /= scalar.z;
+ w /= scalar.w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>negate</code> returns the negative of this vector. All values are
+ * negated and set to a new vector.
+ *
+ * @return the negated vector.
+ */
+ public Vector4f negate() {
+ return new Vector4f(-x, -y, -z, -w);
+ }
+
+ /**
+ *
+ * <code>negateLocal</code> negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector4f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ w = -w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>subtract</code> subtracts the values of a given vector from those
+ * of this vector creating a new vector object. If the provided vector is
+ * null, null is returned.
+ *
+ * @param vec
+ * the vector to subtract from this vector.
+ * @return the result vector.
+ */
+ public Vector4f subtract(Vector4f vec) {
+ return new Vector4f(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
+ }
+
+ /**
+ * <code>subtractLocal</code> subtracts a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec
+ * the vector to subtract
+ * @return this
+ */
+ public Vector4f subtractLocal(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ z -= vec.z;
+ w -= vec.w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>subtract</code>
+ *
+ * @param vec
+ * the vector to subtract from this
+ * @param result
+ * the vector to store the result in
+ * @return result
+ */
+ public Vector4f subtract(Vector4f vec, Vector4f result) {
+ if(result == null) {
+ result = new Vector4f();
+ }
+ result.x = x - vec.x;
+ result.y = y - vec.y;
+ result.z = z - vec.z;
+ result.w = w - vec.w;
+ return result;
+ }
+
+ /**
+ *
+ * <code>subtract</code> subtracts the provided values from this vector,
+ * creating a new vector that is then returned.
+ *
+ * @param subtractX
+ * the x value to subtract.
+ * @param subtractY
+ * the y value to subtract.
+ * @param subtractZ
+ * the z value to subtract.
+ * @param subtractW
+ * the w value to subtract.
+ * @return the result vector.
+ */
+ public Vector4f subtract(float subtractX, float subtractY, float subtractZ, float subtractW) {
+ return new Vector4f(x - subtractX, y - subtractY, z - subtractZ, w - subtractW);
+ }
+
+ /**
+ * <code>subtractLocal</code> subtracts the provided values from this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param subtractX
+ * the x value to subtract.
+ * @param subtractY
+ * the y value to subtract.
+ * @param subtractZ
+ * the z value to subtract.
+ * @param subtractW
+ * the w value to subtract.
+ * @return this
+ */
+ public Vector4f subtractLocal(float subtractX, float subtractY, float subtractZ, float subtractW) {
+ x -= subtractX;
+ y -= subtractY;
+ z -= subtractZ;
+ w -= subtractW;
+ return this;
+ }
+
+ /**
+ * <code>normalize</code> returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector4f normalize() {
+// float length = length();
+// if (length != 0) {
+// return divide(length);
+// }
+//
+// return divide(1);
+ float length = x * x + y * y + z * z + w * w;
+ if (length != 1f && length != 0f){
+ length = 1.0f / FastMath.sqrt(length);
+ return new Vector4f(x * length, y * length, z * length, w * length);
+ }
+ return clone();
+ }
+
+ /**
+ * <code>normalizeLocal</code> makes this vector into a unit vector of
+ * itself.
+ *
+ * @return this.
+ */
+ public Vector4f normalizeLocal() {
+ // NOTE: this implementation is more optimized
+ // than the old jme normalize as this method
+ // is commonly used.
+ float length = x * x + y * y + z * z + w * w;
+ if (length != 1f && length != 0f){
+ length = 1.0f / FastMath.sqrt(length);
+ x *= length;
+ y *= length;
+ z *= length;
+ w *= length;
+ }
+ return this;
+ }
+
+ /**
+ * <code>maxLocal</code> computes the maximum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void maxLocal(Vector4f other){
+ x = other.x > x ? other.x : x;
+ y = other.y > y ? other.y : y;
+ z = other.z > z ? other.z : z;
+ w = other.w > w ? other.w : w;
+ }
+
+ /**
+ * <code>minLocal</code> computes the minimum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void minLocal(Vector4f other){
+ x = other.x < x ? other.x : x;
+ y = other.y < y ? other.y : y;
+ z = other.z < z ? other.z : z;
+ w = other.w < w ? other.w : w;
+ }
+
+ /**
+ * <code>zero</code> resets this vector's data to zero internally.
+ */
+ public Vector4f zero() {
+ x = y = z = w = 0;
+ return this;
+ }
+
+ /**
+ * <code>angleBetween</code> returns (in radians) the angle between two vectors.
+ * It is assumed that both this vector and the given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector a unit vector to find the angle against
+ * @return the angle in radians.
+ */
+ public float angleBetween(Vector4f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = FastMath.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the finalVec
+ * this=(1-changeAmnt)*this + changeAmnt * finalVec
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from this towards finalVec
+ */
+ public Vector4f interpolate(Vector4f finalVec, float changeAmnt) {
+ this.x=(1-changeAmnt)*this.x + changeAmnt*finalVec.x;
+ this.y=(1-changeAmnt)*this.y + changeAmnt*finalVec.y;
+ this.z=(1-changeAmnt)*this.z + changeAmnt*finalVec.z;
+ this.w=(1-changeAmnt)*this.w + changeAmnt*finalVec.w;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to finalVec
+ * this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ * @param beginVec the beging vector (changeAmnt=0)
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginVec towards finalVec
+ */
+ public Vector4f interpolate(Vector4f beginVec,Vector4f finalVec, float changeAmnt) {
+ this.x=(1-changeAmnt)*beginVec.x + changeAmnt*finalVec.x;
+ this.y=(1-changeAmnt)*beginVec.y + changeAmnt*finalVec.y;
+ this.z=(1-changeAmnt)*beginVec.z + changeAmnt*finalVec.z;
+ this.w=(1-changeAmnt)*beginVec.w + changeAmnt*finalVec.w;
+ return this;
+ }
+
+ /**
+ * Check a vector... if it is null or its floats are NaN or infinite,
+ * return false. Else return true.
+ * @param vector the vector to check
+ * @return true or false as stated above.
+ */
+ public static boolean isValidVector(Vector4f vector) {
+ if (vector == null) return false;
+ if (Float.isNaN(vector.x) ||
+ Float.isNaN(vector.y) ||
+ Float.isNaN(vector.z)||
+ Float.isNaN(vector.w)) return false;
+ if (Float.isInfinite(vector.x) ||
+ Float.isInfinite(vector.y) ||
+ Float.isInfinite(vector.z) ||
+ Float.isInfinite(vector.w)) return false;
+ return true;
+ }
+
+ @Override
+ public Vector4f clone() {
+ try {
+ return (Vector4f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this Vector3f into the given float[] object.
+ *
+ * @param floats
+ * The float[] to take this Vector3f. If null, a new float[3] is
+ * created.
+ * @return The array, with X, Y, Z float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[4];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ floats[2] = z;
+ floats[3] = w;
+ return floats;
+ }
+
+ /**
+ * are these two vectors the same? they are is they both have the same x,y,
+ * and z values.
+ *
+ * @param o
+ * the object to compare for equality
+ * @return true if they are equal
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof Vector4f)) { return false; }
+
+ if (this == o) { return true; }
+
+ Vector4f comp = (Vector4f) o;
+ if (Float.compare(x,comp.x) != 0) return false;
+ if (Float.compare(y,comp.y) != 0) return false;
+ if (Float.compare(z,comp.z) != 0) return false;
+ if (Float.compare(w,comp.w) != 0) return false;
+ return true;
+ }
+
+ /**
+ * <code>hashCode</code> returns a unique code for this vector object based
+ * on it's values. If two vectors are logically equivalent, they will return
+ * the same hash code value.
+ * @return the hash code value of this vector.
+ */
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(x);
+ hash += 37 * hash + Float.floatToIntBits(y);
+ hash += 37 * hash + Float.floatToIntBits(z);
+ hash += 37 * hash + Float.floatToIntBits(w);
+ return hash;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this vector.
+ * The format is:
+ *
+ * org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ, W=WW.WWWW]
+ *
+ * @return the string representation of this vector.
+ */
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ", " + w + ")";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(x, "x", 0);
+ capsule.write(y, "y", 0);
+ capsule.write(z, "z", 0);
+ capsule.write(w, "w", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ x = capsule.readFloat("x", 0);
+ y = capsule.readFloat("y", 0);
+ z = capsule.readFloat("z", 0);
+ w = capsule.readFloat("w", 0);
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector4f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector4f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public Vector4f setZ(float z) {
+ this.z = z;
+ return this;
+ }
+
+ public float getW() {
+ return w;
+ }
+
+ public Vector4f setW(float w) {
+ this.w = w;
+ return this;
+ }
+
+ /**
+ * @param index
+ * @return x value if index == 0, y value if index == 1 or z value if index ==
+ * 2
+ * @throws IllegalArgumentException
+ * if index is not one of 0, 1, 2.
+ */
+ public float get(int index) {
+ switch (index) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
+ }
+
+ /**
+ * @param index
+ * which field index in this vector to set.
+ * @param value
+ * to set to one of x, y, z or w.
+ * @throws IllegalArgumentException
+ * if index is not one of 0, 1, 2, 3.
+ */
+ public void set(int index, float value) {
+ switch (index) {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ case 3:
+ w = value;
+ return;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/math/package.html b/engine/src/core/com/jme3/math/package.html
new file mode 100644
index 0000000..64da8aa
--- /dev/null
+++ b/engine/src/core/com/jme3/math/package.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.math</code> package provides mathematic data structures
+and utilities which are used by the rest of the engine.
+The math package provides the following classes:<br>
+<h3>General purpose vectors</h3>
+<ul>
+ <li>{@link com.jme3.math.Vector2f} - 2D general purpose vector</li>
+ <li>{@link com.jme3.math.Vector3f} - 3D general purpose vector</li>
+ <li>{@link com.jme3.math.Vector4f} - 4D general purpose vector</li>
+</ul>
+<h3>Special purpose vectors</h3>
+<ul>
+ <li>{@link com.jme3.math.ColorRGBA} - Floating-point RGB color with alpha</li>
+ <li>{@link com.jme3.math.Quaternion} - Specialized 4D data structure to represent rotation</li>
+</ul>
+<h3>Matrices</h3>
+<ul>
+ <li>{@link com.jme3.math.Matrix3f} - 3x3 matrix, usually used to represent rotation</li>
+ <li>{@link com.jme3.math.Matrix4f} - 4x4 matrix, used as an efficient transform representation</li>
+</ul>
+<h3>Shapes</h3>
+<ul>
+ <li>{@link com.jme3.math.AbstractTriangle} - Abstract triangle. Data to be provided by implementation</li>
+ <li>{@link com.jme3.math.Triangle} - Concrete implementation of AbstractTriangle with center and normal vectors</li>
+ <li>{@link com.jme3.math.Line} - Infinite 3D line</li>
+ <li>{@link com.jme3.math.LineSegment} - 3D line with start and end point</li>
+ <li>{@link com.jme3.math.Plane} - 3D plane</li>
+ <li>{@link com.jme3.math.Ray} - 3D ray</li>
+ <li>{@link com.jme3.math.Rectangle} - 3D rectangle</li>
+ <li>{@link com.jme3.math.Ring} - 3D ring</li>
+</ul>
+<h3>Curves</h3>
+<ul>
+ <li>{@link com.jme3.math.Spline} - 3D curve defined by control points and a function</li>
+</ul>
+<h3>Utility classes</h3>
+<ul>
+ <li>{@link com.jme3.math.Transform} - Representation of a transform with translation, rotation, and scale</li>
+ <li>{@link com.jme3.math.FastMath} - Contains static methods for floating-point math</li>
+ <li>{@link com.jme3.math.CurveAndSurfaceMath} - Contains static methods specific to curve and surface math</li>
+ <li>{@link com.jme3.math.Eigen3f} - Provides computation of eigenvectors given a matrix</li>
+</ul>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/post/Filter.java b/engine/src/core/com/jme3/post/Filter.java
new file mode 100644
index 0000000..ef9a6ff
--- /dev/null
+++ b/engine/src/core/com/jme3/post/Filter.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Filters are 2D effects applied to the rendered scene.<br>
+ * The filter is fed with the rendered scene image rendered in an offscreen frame buffer.<br>
+ * This texture is applied on a fullscreen quad, with a special material.<br>
+ * This material uses a shader that aplly the desired effect to the scene texture.<br>
+ * <br>
+ * This class is abstract, any Filter must extend it.<br>
+ * Any filter holds a frameBuffer and a texture<br>
+ * The getMaterial must return a Material that use a GLSL shader immplementing the desired effect<br>
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
+public abstract class Filter implements Savable {
+
+
+ private String name;
+ protected Pass defaultPass;
+ protected List<Pass> postRenderPasses;
+ protected Material material;
+ protected boolean enabled = true;
+ protected FilterPostProcessor processor;
+
+ public Filter(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Inner class Pass
+ * Pass are like filters in filters.
+ * Some filters will need multiple passes before the final render
+ */
+ public class Pass {
+
+ protected FrameBuffer renderFrameBuffer;
+ protected Texture2D renderedTexture;
+ protected Texture2D depthTexture;
+ protected Material passMaterial;
+
+ /**
+ * init the pass called internally
+ * @param renderer
+ * @param width
+ * @param height
+ * @param textureFormat
+ * @param depthBufferFormat
+ * @param numSamples
+ */
+ public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples, boolean renderDepth) {
+ Collection<Caps> caps = renderer.getCaps();
+ if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample) && caps.contains(Caps.OpenGL31)) {
+ renderFrameBuffer = new FrameBuffer(width, height, numSamples);
+ renderedTexture = new Texture2D(width, height, numSamples, textureFormat);
+ renderFrameBuffer.setDepthBuffer(depthBufferFormat);
+ if (renderDepth) {
+ depthTexture = new Texture2D(width, height, numSamples, depthBufferFormat);
+ renderFrameBuffer.setDepthTexture(depthTexture);
+ }
+ } else {
+ renderFrameBuffer = new FrameBuffer(width, height, 1);
+ renderedTexture = new Texture2D(width, height, textureFormat);
+ renderFrameBuffer.setDepthBuffer(depthBufferFormat);
+ if (renderDepth) {
+ depthTexture = new Texture2D(width, height, depthBufferFormat);
+ renderFrameBuffer.setDepthTexture(depthTexture);
+ }
+ }
+
+ renderFrameBuffer.setColorTexture(renderedTexture);
+
+
+ }
+
+ /**
+ * init the pass called internally
+ * @param renderer
+ * @param width
+ * @param height
+ * @param textureFormat
+ * @param depthBufferFormat
+ */
+ public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat) {
+ init(renderer, width, height, textureFormat, depthBufferFormat, 1);
+ }
+
+ public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples) {
+ init(renderer, width, height, textureFormat, depthBufferFormat, numSamples, false);
+ }
+
+ /**
+ * init the pass called internally
+ * @param renderer
+ * @param width
+ * @param height
+ * @param textureFormat
+ * @param depthBufferFormat
+ * @param numSample
+ * @param material
+ */
+ public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSample, Material material) {
+ init(renderer, width, height, textureFormat, depthBufferFormat, numSample);
+ passMaterial = material;
+ }
+
+ public boolean requiresSceneAsTexture() {
+ return false;
+ }
+
+ public boolean requiresDepthAsTexture() {
+ return false;
+ }
+
+ public void beforeRender() {
+ }
+
+ public FrameBuffer getRenderFrameBuffer() {
+ return renderFrameBuffer;
+ }
+
+ public void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) {
+ this.renderFrameBuffer = renderFrameBuffer;
+ }
+
+ public Texture2D getDepthTexture() {
+ return depthTexture;
+ }
+
+ public Texture2D getRenderedTexture() {
+ return renderedTexture;
+ }
+
+ public void setRenderedTexture(Texture2D renderedTexture) {
+ this.renderedTexture = renderedTexture;
+ }
+
+ public Material getPassMaterial() {
+ return passMaterial;
+ }
+
+ public void setPassMaterial(Material passMaterial) {
+ this.passMaterial = passMaterial;
+ }
+
+ public void cleanup(Renderer r) {
+ }
+ }
+
+ /**
+ * returns the default pass texture format
+ * @return
+ */
+ protected Format getDefaultPassTextureFormat() {
+ return Format.RGBA8;
+ }
+
+ /**
+ * returns the default pass depth format
+ * @return
+ */
+ protected Format getDefaultPassDepthFormat() {
+ return Format.Depth;
+ }
+
+ /**
+ * contruct a Filter
+ */
+ protected Filter() {
+ this("filter");
+ }
+
+ /**
+ *
+ * initialize this filter
+ * use InitFilter for overriding filter initialization
+ * @param manager the assetManager
+ * @param renderManager the renderManager
+ * @param vp the viewport
+ * @param w the width
+ * @param h the height
+ */
+ protected final void init(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ // cleanup(renderManager.getRenderer());
+ defaultPass = new Pass();
+ defaultPass.init(renderManager.getRenderer(), w, h, getDefaultPassTextureFormat(), getDefaultPassDepthFormat());
+ initFilter(manager, renderManager, vp, w, h);
+ }
+
+ /**
+ * cleanup this filter
+ * @param r
+ */
+ protected final void cleanup(Renderer r) {
+ processor = null;
+ if (defaultPass != null) {
+ defaultPass.cleanup(r);
+ }
+ if (postRenderPasses != null) {
+ for (Iterator<Pass> it = postRenderPasses.iterator(); it.hasNext();) {
+ Pass pass = it.next();
+ pass.cleanup(r);
+ }
+ }
+ cleanUpFilter(r);
+ }
+
+ /**
+ * Initialization of sub classes filters
+ * This method is called once when the filter is added to the FilterPostProcessor
+ * It should contain Material initializations and extra passes initialization
+ * @param manager the assetManager
+ * @param renderManager the renderManager
+ * @param vp the viewPort where this filter is rendered
+ * @param w the width of the filter
+ * @param h the height of the filter
+ */
+ protected abstract void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h);
+
+ /**
+ * override this method if you have some cleanup to do
+ * @param r the renderer
+ */
+ protected void cleanUpFilter(Renderer r) {
+ }
+
+ ;
+
+ /**
+ * Must return the material used for this filter.
+ * this method is called every frame.
+ *
+ * @return the material used for this filter.
+ */
+ protected abstract Material getMaterial();
+
+ /**
+ * Override this method if you want to make a pre pass, before the actual rendering of the frame
+ * @param renderManager
+ * @param viewPort
+ */
+ protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
+ }
+
+ /**
+ * Override this method if you want to modify parameters according to tpf before the rendering of the frame.
+ * This is usefull for animated filters
+ * Also it can be the place to render pre passes
+ * @param tpf the time used to render the previous frame
+ */
+ protected void preFrame(float tpf) {
+ }
+
+ /**
+ * Override this method if you want to make a pass just after the frame has been rendered and just before the filter rendering
+ * @param renderManager
+ * @param viewPort
+ * @param prevFilterBuffer
+ * @param sceneBuffer
+ */
+ protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
+ }
+
+ /**
+ * Override this method if you want to save extra properties when the filter is saved else only basic properties of the filter will be saved
+ * This method should always begin by super.write(ex);
+ * @param ex
+ * @throws IOException
+ */
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", "");
+ oc.write(enabled, "enabled", true);
+ }
+
+ /**
+ * Override this method if you want to load extra properties when the filter
+ * is loaded else only basic properties of the filter will be loaded
+ * This method should always begin by super.read(im);
+ */
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", "");
+ enabled = ic.readBoolean("enabled", true);
+ }
+
+ /**
+ * returns the name of the filter
+ * @return
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of the filter
+ * @param name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * returns the default pass frame buffer
+ * @return
+ */
+ protected FrameBuffer getRenderFrameBuffer() {
+ return defaultPass.renderFrameBuffer;
+ }
+
+ /**
+ * sets the default pas frame buffer
+ * @param renderFrameBuffer
+ */
+ protected void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) {
+ this.defaultPass.renderFrameBuffer = renderFrameBuffer;
+ }
+
+ /**
+ * returns the rendered texture of this filter
+ * @return
+ */
+ protected Texture2D getRenderedTexture() {
+ return defaultPass.renderedTexture;
+ }
+
+ /**
+ * sets the rendered texture of this filter
+ * @param renderedTexture
+ */
+ protected void setRenderedTexture(Texture2D renderedTexture) {
+ this.defaultPass.renderedTexture = renderedTexture;
+ }
+
+ /**
+ * Override this method and return true if your Filter needs the depth texture
+ *
+ * @return true if your Filter need the depth texture
+ */
+ protected boolean isRequiresDepthTexture() {
+ return false;
+ }
+
+ /**
+ * Override this method and return false if your Filter does not need the scene texture
+ *
+ * @return false if your Filter does not need the scene texture
+ */
+ protected boolean isRequiresSceneTexture() {
+ return true;
+ }
+
+ /**
+ * returns the list of the postRender passes
+ * @return
+ */
+ protected List<Pass> getPostRenderPasses() {
+ return postRenderPasses;
+ }
+
+ /**
+ * Enable or disable this filter
+ * @param enabled true to enable
+ */
+ public void setEnabled(boolean enabled) {
+ if (processor != null) {
+ processor.setFilterState(this, enabled);
+ } else {
+ this.enabled = enabled;
+ }
+ }
+
+ /**
+ * returns ttrue if the filter is enabled
+ * @return enabled
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * sets a reference to the FilterPostProcessor ti which this filter is attached
+ * @param proc
+ */
+ protected void setProcessor(FilterPostProcessor proc) {
+ processor = proc;
+ }
+}
diff --git a/engine/src/core/com/jme3/post/FilterPostProcessor.java b/engine/src/core/com/jme3/post/FilterPostProcessor.java
new file mode 100644
index 0000000..2e48f0f
--- /dev/null
+++ b/engine/src/core/com/jme3/post/FilterPostProcessor.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import com.jme3.renderer.*;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A FilterPostProcessor is a processor that can apply several {@link Filter}s to a rendered scene<br>
+ * It manages a list of filters that will be applied in the order in which they've been added to the list
+ * @author Rémy Bouquet aka Nehon
+ */
+public class FilterPostProcessor implements SceneProcessor, Savable {
+
+ private RenderManager renderManager;
+ private Renderer renderer;
+ private ViewPort viewPort;
+ private FrameBuffer renderFrameBufferMS;
+ private int numSamples = 1;
+ private FrameBuffer renderFrameBuffer;
+ private Texture2D filterTexture;
+ private Texture2D depthTexture;
+ private List<Filter> filters = new ArrayList<Filter>();
+ private AssetManager assetManager;
+ private Camera filterCam = new Camera(1, 1);
+ private Picture fsQuad;
+ private boolean computeDepth = false;
+ private FrameBuffer outputBuffer;
+ private int width;
+ private int height;
+ private float bottom;
+ private float left;
+ private float right;
+ private float top;
+ private int originalWidth;
+ private int originalHeight;
+ private int lastFilterIndex = -1;
+ private boolean cameraInit = false;
+
+ /**
+ * Create a FilterProcessor
+ * @param assetManager the assetManager
+ */
+ public FilterPostProcessor(AssetManager assetManager) {
+ this.assetManager = assetManager;
+ }
+
+ /**
+ * Don't use this constructor use {@link FilterPostProcessor(AssetManager assetManager)}<br>
+ * This constructor is used for serialization only
+ */
+ public FilterPostProcessor() {
+ }
+
+ /**
+ * Adds a filter to the filters list<br>
+ * @param filter the filter to add
+ */
+ public void addFilter(Filter filter) {
+ filters.add(filter);
+ filter.setProcessor(this);
+
+ if (isInitialized()) {
+ initFilter(filter, viewPort);
+ }
+
+ setFilterState(filter, filter.isEnabled());
+
+ }
+
+ /**
+ * removes this filters from the filters list
+ * @param filter
+ */
+ public void removeFilter(Filter filter) {
+ filters.remove(filter);
+ filter.cleanup(renderer);
+ updateLastFilterIndex();
+ }
+
+ public Iterator<Filter> getFilterIterator() {
+ return filters.iterator();
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ renderManager = rm;
+ renderer = rm.getRenderer();
+ viewPort = vp;
+ fsQuad = new Picture("filter full screen quad");
+
+ Camera cam = vp.getCamera();
+
+ //save view port diensions
+ left = cam.getViewPortLeft();
+ right = cam.getViewPortRight();
+ top = cam.getViewPortTop();
+ bottom = cam.getViewPortBottom();
+ originalWidth = cam.getWidth();
+ originalHeight = cam.getHeight();
+ //first call to reshape
+ reshape(vp, cam.getWidth(), cam.getHeight());
+ }
+
+ /**
+ * init the given filter
+ * @param filter
+ * @param vp
+ */
+ private void initFilter(Filter filter, ViewPort vp) {
+ filter.init(assetManager, renderManager, vp, width, height);
+ if (filter.isRequiresDepthTexture()) {
+ if (!computeDepth && renderFrameBuffer != null) {
+ depthTexture = new Texture2D(width, height, Format.Depth24);
+ renderFrameBuffer.setDepthTexture(depthTexture);
+ }
+ computeDepth = true;
+ filter.getMaterial().setTexture("DepthTexture", depthTexture);
+ }
+ }
+
+ /**
+ * renders a filter on a fullscreen quad
+ * @param r
+ * @param buff
+ * @param mat
+ */
+ private void renderProcessing(Renderer r, FrameBuffer buff, Material mat) {
+ if (buff == outputBuffer) {
+ fsQuad.setWidth(width);
+ fsQuad.setHeight(height);
+ filterCam.resize(originalWidth, originalHeight, true);
+ fsQuad.setPosition(left * originalWidth, bottom * originalHeight);
+ } else {
+ fsQuad.setWidth(buff.getWidth());
+ fsQuad.setHeight(buff.getHeight());
+ filterCam.resize(buff.getWidth(), buff.getHeight(), true);
+ fsQuad.setPosition(0, 0);
+ }
+
+ if (mat.getAdditionalRenderState().isDepthWrite()) {
+ mat.getAdditionalRenderState().setDepthTest(false);
+ mat.getAdditionalRenderState().setDepthWrite(false);
+ }
+
+ fsQuad.setMaterial(mat);
+ fsQuad.updateGeometricState();
+
+ renderManager.setCamera(filterCam, true);
+ r.setFrameBuffer(buff);
+ r.clearBuffers(false, true, true);
+ renderManager.renderGeometry(fsQuad);
+
+ }
+
+ public boolean isInitialized() {
+ return viewPort != null;
+ }
+
+ public void postQueue(RenderQueue rq) {
+
+ for (Iterator<Filter> it = filters.iterator(); it.hasNext();) {
+ Filter filter = it.next();
+ if (filter.isEnabled()) {
+ filter.postQueue(renderManager, viewPort);
+ }
+ }
+
+ }
+ Picture pic = new Picture("debug");
+
+ /**
+ * iterate through the filter list and renders filters
+ * @param r
+ * @param sceneFb
+ */
+ private void renderFilterChain(Renderer r, FrameBuffer sceneFb) {
+ Texture2D tex = filterTexture;
+ FrameBuffer buff = sceneFb;
+ boolean msDepth = depthTexture != null && depthTexture.getImage().getMultiSamples() > 1;
+ for (int i = 0; i < filters.size(); i++) {
+ Filter filter = filters.get(i);
+ if (filter.isEnabled()) {
+ if (filter.getPostRenderPasses() != null) {
+ for (Iterator<Filter.Pass> it1 = filter.getPostRenderPasses().iterator(); it1.hasNext();) {
+ Filter.Pass pass = it1.next();
+ pass.beforeRender();
+ if (pass.requiresSceneAsTexture()) {
+ pass.getPassMaterial().setTexture("Texture", tex);
+ if (tex.getImage().getMultiSamples() > 1) {
+ pass.getPassMaterial().setInt("NumSamples", tex.getImage().getMultiSamples());
+ } else {
+ pass.getPassMaterial().clearParam("NumSamples");
+
+ }
+ }
+ if (pass.requiresDepthAsTexture()) {
+ pass.getPassMaterial().setTexture("DepthTexture", depthTexture);
+ if (msDepth) {
+ pass.getPassMaterial().setInt("NumSamplesDepth", depthTexture.getImage().getMultiSamples());
+ } else {
+ pass.getPassMaterial().clearParam("NumSamplesDepth");
+ }
+ }
+ renderProcessing(r, pass.getRenderFrameBuffer(), pass.getPassMaterial());
+ }
+ }
+
+ filter.postFrame(renderManager, viewPort, buff, sceneFb);
+
+ Material mat = filter.getMaterial();
+ if (msDepth && filter.isRequiresDepthTexture()) {
+ mat.setInt("NumSamplesDepth", depthTexture.getImage().getMultiSamples());
+ }
+
+ if (filter.isRequiresSceneTexture()) {
+ mat.setTexture("Texture", tex);
+ if (tex.getImage().getMultiSamples() > 1) {
+ mat.setInt("NumSamples", tex.getImage().getMultiSamples());
+ } else {
+ mat.clearParam("NumSamples");
+ }
+ }
+
+ buff = outputBuffer;
+ if (i != lastFilterIndex) {
+ buff = filter.getRenderFrameBuffer();
+ tex = filter.getRenderedTexture();
+
+ }
+ renderProcessing(r, buff, mat);
+ }
+ }
+ }
+
+ public void postFrame(FrameBuffer out) {
+
+ FrameBuffer sceneBuffer = renderFrameBuffer;
+ if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL31)) {
+ renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer);
+ } else if (renderFrameBufferMS != null) {
+ sceneBuffer = renderFrameBufferMS;
+ }
+ renderFilterChain(renderer, sceneBuffer);
+ renderer.setFrameBuffer(outputBuffer);
+
+ //viewport can be null if no filters are enabled
+ if (viewPort != null) {
+ renderManager.setCamera(viewPort.getCamera(), false);
+ }
+
+ }
+
+ public void preFrame(float tpf) {
+ if (filters.isEmpty() || lastFilterIndex == -1) {
+ //If the camera is initialized and there are no filter to render, the camera viewport is restored as it was
+ if (cameraInit) {
+ viewPort.getCamera().resize(originalWidth, originalHeight, true);
+ viewPort.getCamera().setViewPort(left, right, bottom, top);
+ viewPort.setOutputFrameBuffer(outputBuffer);
+ cameraInit = false;
+ }
+
+ } else {
+ if (renderFrameBufferMS != null) {
+ viewPort.setOutputFrameBuffer(renderFrameBufferMS);
+ } else {
+ viewPort.setOutputFrameBuffer(renderFrameBuffer);
+ }
+ //init of the camera if it wasn't already
+ if (!cameraInit) {
+ viewPort.getCamera().resize(width, height, true);
+ viewPort.getCamera().setViewPort(0, 1, 0, 1);
+ }
+ }
+
+ for (Iterator<Filter> it = filters.iterator(); it.hasNext();) {
+ Filter filter = it.next();
+ if (filter.isEnabled()) {
+ filter.preFrame(tpf);
+ }
+ }
+
+ }
+
+ /**
+ * sets the filter to enabled or disabled
+ * @param filter
+ * @param enabled
+ */
+ protected void setFilterState(Filter filter, boolean enabled) {
+ if (filters.contains(filter)) {
+ filter.enabled = enabled;
+ updateLastFilterIndex();
+ }
+ }
+
+ /**
+ * compute the index of the last filter to render
+ */
+ private void updateLastFilterIndex() {
+ lastFilterIndex = -1;
+ for (int i = filters.size() - 1; i >= 0 && lastFilterIndex == -1; i--) {
+ if (filters.get(i).isEnabled()) {
+ lastFilterIndex = i;
+ return;
+ }
+ }
+ if (lastFilterIndex == -1) {
+ cleanup();
+ }
+ }
+
+ public void cleanup() {
+ if (viewPort != null) {
+ //reseting the viewport camera viewport to its initial value
+ viewPort.getCamera().resize(originalWidth, originalHeight, true);
+ viewPort.getCamera().setViewPort(left, right, bottom, top);
+ viewPort.setOutputFrameBuffer(outputBuffer);
+ viewPort = null;
+ }
+
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ //this has no effect at first init but is useful when resizing the canvas with multi views
+ Camera cam = vp.getCamera();
+ cam.setViewPort(left, right, bottom, top);
+ //resizing the camera to fit the new viewport and saving original dimensions
+ cam.resize(w, h, false);
+ left = cam.getViewPortLeft();
+ right = cam.getViewPortRight();
+ top = cam.getViewPortTop();
+ bottom = cam.getViewPortBottom();
+ originalWidth = w;
+ originalHeight = h;
+ cam.setViewPort(0, 1, 0, 1);
+
+ //computing real dimension of the viewport and resizing he camera
+ width = (int) (w * (Math.abs(right - left)));
+ height = (int) (h * (Math.abs(bottom - top)));
+ width = Math.max(1, width);
+ height = Math.max(1, height);
+ cam.resize(width, height, false);
+ cameraInit = true;
+ computeDepth = false;
+
+ if (renderFrameBuffer == null) {
+ outputBuffer = viewPort.getOutputFrameBuffer();
+ }
+
+ Collection<Caps> caps = renderer.getCaps();
+
+ //antialiasing on filters only supported in opengl 3 due to depth read problem
+ if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)) {
+ renderFrameBufferMS = new FrameBuffer(width, height, numSamples);
+ if (caps.contains(Caps.OpenGL31)) {
+ Texture2D msColor = new Texture2D(width, height, numSamples, Format.RGBA8);
+ Texture2D msDepth = new Texture2D(width, height, numSamples, Format.Depth);
+ renderFrameBufferMS.setDepthTexture(msDepth);
+ renderFrameBufferMS.setColorTexture(msColor);
+ filterTexture = msColor;
+ depthTexture = msDepth;
+ } else {
+ renderFrameBufferMS.setDepthBuffer(Format.Depth);
+ renderFrameBufferMS.setColorBuffer(Format.RGBA8);
+ }
+ }
+
+ if (numSamples <= 1 || !caps.contains(Caps.OpenGL31)) {
+ renderFrameBuffer = new FrameBuffer(width, height, 1);
+ renderFrameBuffer.setDepthBuffer(Format.Depth);
+ filterTexture = new Texture2D(width, height, Format.RGBA8);
+ renderFrameBuffer.setColorTexture(filterTexture);
+ }
+
+ for (Iterator<Filter> it = filters.iterator(); it.hasNext();) {
+ Filter filter = it.next();
+ initFilter(filter, vp);
+ }
+
+ if (renderFrameBufferMS != null) {
+ viewPort.setOutputFrameBuffer(renderFrameBufferMS);
+ } else {
+ viewPort.setOutputFrameBuffer(renderFrameBuffer);
+ }
+ }
+
+ /**
+ * return the number of samples for antialiasing
+ * @return numSamples
+ */
+ public int getNumSamples() {
+ return numSamples;
+ }
+
+ /**
+ *
+ * Removes all the filters from this processor
+ */
+ public void removeAllFilters() {
+ filters.clear();
+ updateLastFilterIndex();
+ }
+
+ /**
+ * Sets the number of samples for antialiasing
+ * @param numSamples the number of Samples
+ */
+ public void setNumSamples(int numSamples) {
+ if (numSamples <= 0) {
+ throw new IllegalArgumentException("numSamples must be > 0");
+ }
+
+ this.numSamples = numSamples;
+ }
+
+ /**
+ * Sets the asset manager for this processor
+ * @param assetManager
+ */
+ public void setAssetManager(AssetManager assetManager) {
+ this.assetManager = assetManager;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(numSamples, "numSamples", 0);
+ oc.writeSavableArrayList((ArrayList) filters, "filters", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ numSamples = ic.readInt("numSamples", 0);
+ filters = ic.readSavableArrayList("filters", null);
+ for (Filter filter : filters) {
+ filter.setProcessor(this);
+ setFilterState(filter, filter.isEnabled());
+ }
+ assetManager = im.getAssetManager();
+ }
+
+ /**
+ * For internal use only<br>
+ * returns the depth texture of the scene
+ * @return
+ */
+ public Texture2D getDepthTexture() {
+ return depthTexture;
+ }
+
+ /**
+ * For internal use only<br>
+ * returns the rendered texture of the scene
+ * @return
+ */
+ public Texture2D getFilterTexture() {
+ return filterTexture;
+ }
+}
diff --git a/engine/src/core/com/jme3/post/HDRRenderer.java b/engine/src/core/com/jme3/post/HDRRenderer.java
new file mode 100644
index 0000000..bac8334
--- /dev/null
+++ b/engine/src/core/com/jme3/post/HDRRenderer.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.post;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.renderer.*;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.MagFilter;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+public class HDRRenderer implements SceneProcessor {
+
+ private static final int LUMMODE_NONE = 0x1,
+ LUMMODE_ENCODE_LUM = 0x2,
+ LUMMODE_DECODE_LUM = 0x3;
+
+ private Renderer renderer;
+ private RenderManager renderManager;
+ private ViewPort viewPort;
+ private static final Logger logger = Logger.getLogger(HDRRenderer.class.getName());
+
+ private Camera fbCam = new Camera(1, 1);
+
+ private FrameBuffer msFB;
+
+ private FrameBuffer mainSceneFB;
+ private Texture2D mainScene;
+ private FrameBuffer scene64FB;
+ private Texture2D scene64;
+ private FrameBuffer scene8FB;
+ private Texture2D scene8;
+ private FrameBuffer scene1FB[] = new FrameBuffer[2];
+ private Texture2D scene1[] = new Texture2D[2];
+
+ private Material hdr64;
+ private Material hdr8;
+ private Material hdr1;
+ private Material tone;
+
+ private Picture fsQuad;
+ private float time = 0;
+ private int curSrc = -1;
+ private int oppSrc = -1;
+ private float blendFactor = 0;
+
+ private int numSamples = 0;
+ private float exposure = 0.18f;
+ private float whiteLevel = 100f;
+ private float throttle = -1;
+ private int maxIterations = -1;
+ private Image.Format bufFormat = Format.RGB16F;
+
+ private MinFilter fbMinFilter = MinFilter.BilinearNoMipMaps;
+ private MagFilter fbMagFilter = MagFilter.Bilinear;
+ private AssetManager manager;
+
+ private boolean enabled = true;
+
+ public HDRRenderer(AssetManager manager, Renderer renderer){
+ this.manager = manager;
+ this.renderer = renderer;
+
+ Collection<Caps> caps = renderer.getCaps();
+ if (caps.contains(Caps.PackedFloatColorBuffer))
+ bufFormat = Format.RGB111110F;
+ else if (caps.contains(Caps.FloatColorBuffer))
+ bufFormat = Format.RGB16F;
+ else{
+ enabled = false;
+ return;
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setSamples(int samples){
+ this.numSamples = samples;
+ }
+
+ public void setExposure(float exp){
+ this.exposure = exp;
+ }
+
+ public void setWhiteLevel(float whiteLevel){
+ this.whiteLevel = whiteLevel;
+ }
+
+ public void setMaxIterations(int maxIterations){
+ this.maxIterations = maxIterations;
+
+ // regenerate shaders if needed
+ if (hdr64 != null)
+ createLumShaders();
+ }
+
+ public void setThrottle(float throttle){
+ this.throttle = throttle;
+ }
+
+ public void setUseFastFilter(boolean fastFilter){
+ if (fastFilter){
+ fbMagFilter = MagFilter.Nearest;
+ fbMinFilter = MinFilter.NearestNoMipMaps;
+ }else{
+ fbMagFilter = MagFilter.Bilinear;
+ fbMinFilter = MinFilter.BilinearNoMipMaps;
+ }
+ }
+
+ public Picture createDisplayQuad(/*int mode, Texture tex*/){
+ if (scene64 == null)
+ return null;
+
+ Material mat = new Material(manager, "Common/MatDefs/Hdr/LogLum.j3md");
+// if (mode == LUMMODE_ENCODE_LUM)
+// mat.setBoolean("EncodeLum", true);
+// else if (mode == LUMMODE_DECODE_LUM)
+ mat.setBoolean("DecodeLum", true);
+ mat.setTexture("Texture", scene64);
+// mat.setTexture("Texture", tex);
+
+ Picture dispQuad = new Picture("Luminance Display");
+ dispQuad.setMaterial(mat);
+ return dispQuad;
+ }
+
+ private Material createLumShader(int srcW, int srcH, int bufW, int bufH, int mode,
+ int iters, Texture tex){
+ Material mat = new Material(manager, "Common/MatDefs/Hdr/LogLum.j3md");
+
+ Vector2f blockSize = new Vector2f(1f / bufW, 1f / bufH);
+ Vector2f pixelSize = new Vector2f(1f / srcW, 1f / srcH);
+ Vector2f blocks = new Vector2f();
+ float numPixels = Float.POSITIVE_INFINITY;
+ if (iters != -1){
+ do {
+ pixelSize.multLocal(2);
+ blocks.set(blockSize.x / pixelSize.x,
+ blockSize.y / pixelSize.y);
+ numPixels = blocks.x * blocks.y;
+ } while (numPixels > iters);
+ }else{
+ blocks.set(blockSize.x / pixelSize.x,
+ blockSize.y / pixelSize.y);
+ numPixels = blocks.x * blocks.y;
+ }
+ System.out.println(numPixels);
+
+ mat.setBoolean("Blocks", true);
+ if (mode == LUMMODE_ENCODE_LUM)
+ mat.setBoolean("EncodeLum", true);
+ else if (mode == LUMMODE_DECODE_LUM)
+ mat.setBoolean("DecodeLum", true);
+
+ mat.setTexture("Texture", tex);
+ mat.setVector2("BlockSize", blockSize);
+ mat.setVector2("PixelSize", pixelSize);
+ mat.setFloat("NumPixels", numPixels);
+
+ return mat;
+ }
+
+ private void createLumShaders(){
+ int w = mainSceneFB.getWidth();
+ int h = mainSceneFB.getHeight();
+ hdr64 = createLumShader(w, h, 64, 64, LUMMODE_ENCODE_LUM, maxIterations, mainScene);
+ hdr8 = createLumShader(64, 64, 8, 8, LUMMODE_NONE, maxIterations, scene64);
+ hdr1 = createLumShader(8, 8, 1, 1, LUMMODE_NONE, maxIterations, scene8);
+ }
+
+ private int opposite(int i){
+ return i == 1 ? 0 : 1;
+ }
+
+ private void renderProcessing(Renderer r, FrameBuffer dst, Material mat){
+ if (dst == null){
+ fsQuad.setWidth(mainSceneFB.getWidth());
+ fsQuad.setHeight(mainSceneFB.getHeight());
+ fbCam.resize(mainSceneFB.getWidth(), mainSceneFB.getHeight(), true);
+ }else{
+ fsQuad.setWidth(dst.getWidth());
+ fsQuad.setHeight(dst.getHeight());
+ fbCam.resize(dst.getWidth(), dst.getHeight(), true);
+ }
+ fsQuad.setMaterial(mat);
+ fsQuad.updateGeometricState();
+ renderManager.setCamera(fbCam, true);
+
+ r.setFrameBuffer(dst);
+ r.clearBuffers(true, true, true);
+ renderManager.renderGeometry(fsQuad);
+ }
+
+ private void renderToneMap(Renderer r, FrameBuffer out){
+ tone.setFloat("A", exposure);
+ tone.setFloat("White", whiteLevel);
+ tone.setTexture("Lum", scene1[oppSrc]);
+ tone.setTexture("Lum2", scene1[curSrc]);
+ tone.setFloat("BlendFactor", blendFactor);
+ renderProcessing(r, out, tone);
+ }
+
+ private void updateAverageLuminance(Renderer r){
+ renderProcessing(r, scene64FB, hdr64);
+ renderProcessing(r, scene8FB, hdr8);
+ renderProcessing(r, scene1FB[curSrc], hdr1);
+ }
+
+ public boolean isInitialized(){
+ return viewPort != null;
+ }
+
+ public void reshape(ViewPort vp, int w, int h){
+ if (mainSceneFB != null){
+ renderer.deleteFrameBuffer(mainSceneFB);
+ }
+
+ mainSceneFB = new FrameBuffer(w, h, 1);
+ mainScene = new Texture2D(w, h, bufFormat);
+ mainSceneFB.setDepthBuffer(Format.Depth);
+ mainSceneFB.setColorTexture(mainScene);
+ mainScene.setMagFilter(fbMagFilter);
+ mainScene.setMinFilter(fbMinFilter);
+
+ if (msFB != null){
+ renderer.deleteFrameBuffer(msFB);
+ }
+
+ tone.setTexture("Texture", mainScene);
+
+ Collection<Caps> caps = renderer.getCaps();
+ if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)){
+ msFB = new FrameBuffer(w, h, numSamples);
+ msFB.setDepthBuffer(Format.Depth);
+ msFB.setColorBuffer(bufFormat);
+ vp.setOutputFrameBuffer(msFB);
+ }else{
+ if (numSamples > 1)
+ logger.warning("FBO multisampling not supported on this GPU, request ignored.");
+
+ vp.setOutputFrameBuffer(mainSceneFB);
+ }
+
+ createLumShaders();
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp){
+ if (!enabled)
+ return;
+
+ renderer = rm.getRenderer();
+ renderManager = rm;
+ viewPort = vp;
+
+ // loadInitial()
+ fsQuad = new Picture("HDR Fullscreen Quad");
+
+ Format lumFmt = Format.RGB8;
+ scene64FB = new FrameBuffer(64, 64, 1);
+ scene64 = new Texture2D(64, 64, lumFmt);
+ scene64FB.setColorTexture(scene64);
+ scene64.setMagFilter(fbMagFilter);
+ scene64.setMinFilter(fbMinFilter);
+
+ scene8FB = new FrameBuffer(8, 8, 1);
+ scene8 = new Texture2D(8, 8, lumFmt);
+ scene8FB.setColorTexture(scene8);
+ scene8.setMagFilter(fbMagFilter);
+ scene8.setMinFilter(fbMinFilter);
+
+ scene1FB[0] = new FrameBuffer(1, 1, 1);
+ scene1[0] = new Texture2D(1, 1, lumFmt);
+ scene1FB[0].setColorTexture(scene1[0]);
+
+ scene1FB[1] = new FrameBuffer(1, 1, 1);
+ scene1[1] = new Texture2D(1, 1, lumFmt);
+ scene1FB[1].setColorTexture(scene1[1]);
+
+ // prepare tonemap shader
+ tone = new Material(manager, "Common/MatDefs/Hdr/ToneMap.j3md");
+ tone.setFloat("A", 0.18f);
+ tone.setFloat("White", 100);
+
+ // load();
+ int w = vp.getCamera().getWidth();
+ int h = vp.getCamera().getHeight();
+ reshape(vp, w, h);
+
+
+ }
+
+ public void preFrame(float tpf) {
+ if (!enabled)
+ return;
+
+ time += tpf;
+ blendFactor = (time / throttle);
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ public void postFrame(FrameBuffer out) {
+ if (!enabled)
+ return;
+
+ if (msFB != null){
+ // first render to multisampled FB
+// renderer.setFrameBuffer(msFB);
+// renderer.clearBuffers(true,true,true);
+//
+// renderManager.renderViewPortRaw(viewPort);
+
+ // render back to non-multisampled FB
+ renderer.copyFrameBuffer(msFB, mainSceneFB);
+ }else{
+// renderer.setFrameBuffer(mainSceneFB);
+// renderer.clearBuffers(true,true,false);
+//
+// renderManager.renderViewPortRaw(viewPort);
+ }
+
+ // should we update avg lum?
+ if (throttle == -1){
+ // update every frame
+ curSrc = 0;
+ oppSrc = 0;
+ blendFactor = 0;
+ time = 0;
+ updateAverageLuminance(renderer);
+ }else{
+ if (curSrc == -1){
+ curSrc = 0;
+ oppSrc = 0;
+
+ // initial update
+ updateAverageLuminance(renderer);
+
+ blendFactor = 0;
+ time = 0;
+ }else if (time > throttle){
+
+ // time to switch
+ oppSrc = curSrc;
+ curSrc = opposite(curSrc);
+
+ updateAverageLuminance(renderer);
+
+ blendFactor = 0;
+ time = 0;
+ }
+ }
+
+ // since out == mainSceneFB, tonemap into the main screen instead
+ //renderToneMap(renderer, out);
+ renderToneMap(renderer, null);
+
+ renderManager.setCamera(viewPort.getCamera(), false);
+ }
+
+ public void cleanup() {
+ if (!enabled)
+ return;
+
+ if (msFB != null)
+ renderer.deleteFrameBuffer(msFB);
+ if (mainSceneFB != null)
+ renderer.deleteFrameBuffer(mainSceneFB);
+ if (scene64FB != null){
+ renderer.deleteFrameBuffer(scene64FB);
+ renderer.deleteFrameBuffer(scene8FB);
+ renderer.deleteFrameBuffer(scene1FB[0]);
+ renderer.deleteFrameBuffer(scene1FB[1]);
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/post/PreDepthProcessor.java b/engine/src/core/com/jme3/post/PreDepthProcessor.java
new file mode 100644
index 0000000..2544aab
--- /dev/null
+++ b/engine/src/core/com/jme3/post/PreDepthProcessor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.post;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+
+/**
+ * Processor that lays depth first, this can improve performance in complex
+ * scenes.
+ */
+public class PreDepthProcessor implements SceneProcessor {
+
+ private RenderManager rm;
+ private ViewPort vp;
+ private AssetManager assetManager;
+ private Material preDepth;
+ private RenderState forcedRS;
+
+ public PreDepthProcessor(AssetManager assetManager){
+ this.assetManager = assetManager;
+ preDepth = new Material(assetManager, "Common/MatDefs/Shadow/PreShadow.j3md");
+ preDepth.getAdditionalRenderState().setPolyOffset(0, 0);
+ preDepth.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Back);
+
+ forcedRS = new RenderState();
+ forcedRS.setDepthTest(true);
+ forcedRS.setDepthWrite(false);
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ this.rm = rm;
+ this.vp = vp;
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ this.vp = vp;
+ }
+
+ public boolean isInitialized() {
+ return vp != null;
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ // lay depth first
+ rm.setForcedMaterial(preDepth);
+ rq.renderQueue(RenderQueue.Bucket.Opaque, rm, vp.getCamera(), false);
+ rm.setForcedMaterial(null);
+
+ rm.setForcedRenderState(forcedRS);
+ }
+
+ public void postFrame(FrameBuffer out) {
+ rm.setForcedRenderState(null);
+ }
+
+ public void cleanup() {
+ vp = null;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/post/SceneProcessor.java b/engine/src/core/com/jme3/post/SceneProcessor.java
new file mode 100644
index 0000000..7b73706
--- /dev/null
+++ b/engine/src/core/com/jme3/post/SceneProcessor.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.post;
+
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+
+/**
+ * Scene processors are used to compute/render things before and after the classic render of the scene.
+ * They have to be added to a viewport and are rendered in the order they've been added
+ *
+ * @author Kirill Vainer
+ */
+public interface SceneProcessor {
+
+ /**
+ * Called in the render thread to initialize the scene processor.
+ *
+ * @param rm The render manager to which the SP was added to
+ * @param vp The viewport to which the SP is assigned
+ */
+ public void initialize(RenderManager rm, ViewPort vp);
+
+ /**
+ * Called when the resolution of the viewport has been changed.
+ * @param vp
+ */
+ public void reshape(ViewPort vp, int w, int h);
+
+ /**
+ * @return True if initialize() has been called on this SceneProcessor,
+ * false if otherwise.
+ */
+ public boolean isInitialized();
+
+ /**
+ * Called before a frame
+ *
+ * @param tpf Time per frame
+ */
+ public void preFrame(float tpf);
+
+ /**
+ * Called after the scene graph has been queued, but before it is flushed.
+ *
+ * @param rq The render queue
+ */
+ public void postQueue(RenderQueue rq);
+
+ /**
+ * Called after a frame has been rendered and the queue flushed.
+ *
+ * @param out The FB to which the scene was rendered.
+ */
+ public void postFrame(FrameBuffer out);
+
+ /**
+ * Called when the SP is removed from the RM.
+ */
+ public void cleanup();
+
+}
diff --git a/engine/src/core/com/jme3/post/package.html b/engine/src/core/com/jme3/post/package.html
new file mode 100644
index 0000000..632e715
--- /dev/null
+++ b/engine/src/core/com/jme3/post/package.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.post</code> package provides utilities for
+render processing.
+<p>
+The {@link com.jme3.post.SceneProcessor} interface is used as the base interface
+for all render processing. The SceneProcessor contains hooks for various rendering
+events.
+<p>
+One use of render processing is post-processing, which is applying effects
+on an already-rendered scene. The engine's post-processing system is implemented
+in the {@link com.jme3.post.FilterPostProcessor} class, which contains a list
+of {@link com.jme3.post.Filter filters}. Each are invoked in order to apply
+various effects on the rendered scene.
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/renderer/Camera.java b/engine/src/core/com/jme3/renderer/Camera.java
new file mode 100644
index 0000000..74ba6cf
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Camera.java
@@ -0,0 +1,1436 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.renderer;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.export.*;
+import com.jme3.math.*;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>Camera</code> is a standalone, purely mathematical class for doing
+ * camera-related computations.
+ *
+ * <p>
+ * Given input data such as location, orientation (direction, left, up),
+ * and viewport settings, it can compute data necessary to render objects
+ * with the graphics library. Two matrices are generated, the view matrix
+ * transforms objects from world space into eye space, while the projection
+ * matrix transforms objects from eye space into clip space.
+ * </p>
+ * <p>Another purpose of the camera class is to do frustum culling operations,
+ * defined by six planes which define a 3D frustum shape, it is possible to
+ * test if an object bounded by a mathematically defined volume is inside
+ * the camera frustum, and thus to avoid rendering objects that are outside
+ * the frustum
+ * </p>
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Camera implements Savable, Cloneable {
+
+ private static final Logger logger = Logger.getLogger(Camera.class.getName());
+
+ /**
+ * The <code>FrustumIntersect</code> enum is returned as a result
+ * of a culling check operation,
+ * see {@link #contains(com.jme3.bounding.BoundingVolume) }
+ */
+ public enum FrustumIntersect {
+
+ /**
+ * defines a constant assigned to spatials that are completely outside
+ * of this camera's view frustum.
+ */
+ Outside,
+ /**
+ * defines a constant assigned to spatials that are completely inside
+ * the camera's view frustum.
+ */
+ Inside,
+ /**
+ * defines a constant assigned to spatials that are intersecting one of
+ * the six planes that define the view frustum.
+ */
+ Intersects;
+ }
+ /**
+ * LEFT_PLANE represents the left plane of the camera frustum.
+ */
+ private static final int LEFT_PLANE = 0;
+ /**
+ * RIGHT_PLANE represents the right plane of the camera frustum.
+ */
+ private static final int RIGHT_PLANE = 1;
+ /**
+ * BOTTOM_PLANE represents the bottom plane of the camera frustum.
+ */
+ private static final int BOTTOM_PLANE = 2;
+ /**
+ * TOP_PLANE represents the top plane of the camera frustum.
+ */
+ private static final int TOP_PLANE = 3;
+ /**
+ * FAR_PLANE represents the far plane of the camera frustum.
+ */
+ private static final int FAR_PLANE = 4;
+ /**
+ * NEAR_PLANE represents the near plane of the camera frustum.
+ */
+ private static final int NEAR_PLANE = 5;
+ /**
+ * FRUSTUM_PLANES represents the number of planes of the camera frustum.
+ */
+ private static final int FRUSTUM_PLANES = 6;
+ /**
+ * MAX_WORLD_PLANES holds the maximum planes allowed by the system.
+ */
+ private static final int MAX_WORLD_PLANES = 6;
+ /**
+ * Camera's location
+ */
+ protected Vector3f location;
+ /**
+ * The orientation of the camera.
+ */
+ protected Quaternion rotation;
+ /**
+ * Distance from camera to near frustum plane.
+ */
+ protected float frustumNear;
+ /**
+ * Distance from camera to far frustum plane.
+ */
+ protected float frustumFar;
+ /**
+ * Distance from camera to left frustum plane.
+ */
+ protected float frustumLeft;
+ /**
+ * Distance from camera to right frustum plane.
+ */
+ protected float frustumRight;
+ /**
+ * Distance from camera to top frustum plane.
+ */
+ protected float frustumTop;
+ /**
+ * Distance from camera to bottom frustum plane.
+ */
+ protected float frustumBottom;
+ //Temporary values computed in onFrustumChange that are needed if a
+ //call is made to onFrameChange.
+ protected float[] coeffLeft;
+ protected float[] coeffRight;
+ protected float[] coeffBottom;
+ protected float[] coeffTop;
+ //view port coordinates
+ /**
+ * Percent value on display where horizontal viewing starts for this camera.
+ * Default is 0.
+ */
+ protected float viewPortLeft;
+ /**
+ * Percent value on display where horizontal viewing ends for this camera.
+ * Default is 1.
+ */
+ protected float viewPortRight;
+ /**
+ * Percent value on display where vertical viewing ends for this camera.
+ * Default is 1.
+ */
+ protected float viewPortTop;
+ /**
+ * Percent value on display where vertical viewing begins for this camera.
+ * Default is 0.
+ */
+ protected float viewPortBottom;
+ /**
+ * Array holding the planes that this camera will check for culling.
+ */
+ protected Plane[] worldPlane;
+ /**
+ * A mask value set during contains() that allows fast culling of a Node's
+ * children.
+ */
+ private int planeState;
+ protected int width;
+ protected int height;
+ protected boolean viewportChanged = true;
+ /**
+ * store the value for field parallelProjection
+ */
+ private boolean parallelProjection;
+ protected Matrix4f projectionMatrixOverride;
+ protected Matrix4f viewMatrix = new Matrix4f();
+ protected Matrix4f projectionMatrix = new Matrix4f();
+ protected Matrix4f viewProjectionMatrix = new Matrix4f();
+ private BoundingBox guiBounding = new BoundingBox();
+ /** The camera's name. */
+ protected String name;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Camera() {
+ worldPlane = new Plane[MAX_WORLD_PLANES];
+ for (int i = 0; i < MAX_WORLD_PLANES; i++) {
+ worldPlane[i] = new Plane();
+ }
+ }
+
+ /**
+ * Constructor instantiates a new <code>Camera</code> object. All
+ * values of the camera are set to default.
+ */
+ public Camera(int width, int height) {
+ this();
+ location = new Vector3f();
+ rotation = new Quaternion();
+
+ frustumNear = 1.0f;
+ frustumFar = 2.0f;
+ frustumLeft = -0.5f;
+ frustumRight = 0.5f;
+ frustumTop = 0.5f;
+ frustumBottom = -0.5f;
+
+ coeffLeft = new float[2];
+ coeffRight = new float[2];
+ coeffBottom = new float[2];
+ coeffTop = new float[2];
+
+ viewPortLeft = 0.0f;
+ viewPortRight = 1.0f;
+ viewPortTop = 1.0f;
+ viewPortBottom = 0.0f;
+
+ this.width = width;
+ this.height = height;
+
+ onFrustumChange();
+ onViewPortChange();
+ onFrameChange();
+
+ logger.log(Level.INFO, "Camera created (W: {0}, H: {1})", new Object[]{width, height});
+ }
+
+ @Override
+ public Camera clone() {
+ try {
+ Camera cam = (Camera) super.clone();
+ cam.viewportChanged = true;
+ cam.planeState = 0;
+
+ cam.worldPlane = new Plane[MAX_WORLD_PLANES];
+ for (int i = 0; i < worldPlane.length; i++) {
+ cam.worldPlane[i] = worldPlane[i].clone();
+ }
+
+ cam.coeffLeft = new float[2];
+ cam.coeffRight = new float[2];
+ cam.coeffBottom = new float[2];
+ cam.coeffTop = new float[2];
+
+ cam.location = location.clone();
+ cam.rotation = rotation.clone();
+
+ if (projectionMatrixOverride != null) {
+ cam.projectionMatrixOverride = projectionMatrixOverride.clone();
+ }
+
+ cam.viewMatrix = viewMatrix.clone();
+ cam.projectionMatrix = projectionMatrix.clone();
+ cam.viewProjectionMatrix = viewProjectionMatrix.clone();
+ cam.guiBounding = (BoundingBox) guiBounding.clone();
+
+ cam.update();
+
+ return cam;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * This method copise the settings of the given camera.
+ *
+ * @param cam
+ * the camera we copy the settings from
+ */
+ public void copyFrom(Camera cam) {
+ location.set(cam.location);
+ rotation.set(cam.rotation);
+
+ frustumNear = cam.frustumNear;
+ frustumFar = cam.frustumFar;
+ frustumLeft = cam.frustumLeft;
+ frustumRight = cam.frustumRight;
+ frustumTop = cam.frustumTop;
+ frustumBottom = cam.frustumBottom;
+
+ coeffLeft[0] = cam.coeffLeft[0];
+ coeffLeft[1] = cam.coeffLeft[1];
+ coeffRight[0] = cam.coeffRight[0];
+ coeffRight[1] = cam.coeffRight[1];
+ coeffBottom[0] = cam.coeffBottom[0];
+ coeffBottom[1] = cam.coeffBottom[1];
+ coeffTop[0] = cam.coeffTop[0];
+ coeffTop[1] = cam.coeffTop[1];
+
+ viewPortLeft = cam.viewPortLeft;
+ viewPortRight = cam.viewPortRight;
+ viewPortTop = cam.viewPortTop;
+ viewPortBottom = cam.viewPortBottom;
+
+ this.width = cam.width;
+ this.height = cam.height;
+
+ this.planeState = cam.planeState;
+ this.viewportChanged = cam.viewportChanged;
+ for (int i = 0; i < MAX_WORLD_PLANES; ++i) {
+ worldPlane[i].setNormal(cam.worldPlane[i].getNormal());
+ worldPlane[i].setConstant(cam.worldPlane[i].getConstant());
+ }
+
+ this.parallelProjection = cam.parallelProjection;
+ if(cam.projectionMatrixOverride != null) {
+ if(this.projectionMatrixOverride == null) {
+ this.projectionMatrixOverride = cam.projectionMatrixOverride.clone();
+ } else {
+ this.projectionMatrixOverride.set(cam.projectionMatrixOverride);
+ }
+ } else {
+ this.projectionMatrixOverride = null;
+ }
+ this.viewMatrix.set(cam.viewMatrix);
+ this.projectionMatrix.set(cam.projectionMatrix);
+ this.viewProjectionMatrix.set(cam.viewProjectionMatrix);
+
+ this.guiBounding.setXExtent(cam.guiBounding.getXExtent());
+ this.guiBounding.setYExtent(cam.guiBounding.getYExtent());
+ this.guiBounding.setZExtent(cam.guiBounding.getZExtent());
+ this.guiBounding.setCenter(cam.guiBounding.getCenter());
+ this.guiBounding.setCheckPlane(cam.guiBounding.getCheckPlane());
+
+ this.name = cam.name;
+ }
+
+ /**
+ * This method sets the cameras name.
+ * @param name the cameras name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * This method returns the cameras name.
+ * @return the cameras name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets a clipPlane for this camera.
+ * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane
+ * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel
+ * more info here
+ * <ul>
+ * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a>
+ * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a>
+ * <li><a href="http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html">http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html</a>
+ * </ul>
+ *
+ * Note that this will work properly only if it's called on each update, and be aware that it won't work properly with the sky bucket.
+ * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java
+ * @param clipPlane the plane
+ * @param side the side the camera stands from the plane
+ */
+ public void setClipPlane(Plane clipPlane, Plane.Side side) {
+ float sideFactor = 1;
+ if (side == Plane.Side.Negative) {
+ sideFactor = -1;
+ }
+ //we are on the other side of the plane no need to clip anymore.
+ if (clipPlane.whichSide(location) == side) {
+ return;
+ }
+ Matrix4f p = projectionMatrix.clone();
+
+ Matrix4f ivm = viewMatrix.clone();
+
+ Vector3f point = clipPlane.getNormal().mult(clipPlane.getConstant());
+ Vector3f pp = ivm.mult(point);
+ Vector3f pn = ivm.multNormal(clipPlane.getNormal(), null);
+ Vector4f clipPlaneV = new Vector4f(pn.x * sideFactor, pn.y * sideFactor, pn.z * sideFactor, -(pp.dot(pn)) * sideFactor);
+
+ Vector4f v = new Vector4f(0, 0, 0, 0);
+
+ v.x = (Math.signum(clipPlaneV.x) + p.m02) / p.m00;
+ v.y = (Math.signum(clipPlaneV.y) + p.m12) / p.m11;
+ v.z = -1.0f;
+ v.w = (1.0f + p.m22) / p.m23;
+
+ float dot = clipPlaneV.dot(v);//clipPlaneV.x * v.x + clipPlaneV.y * v.y + clipPlaneV.z * v.z + clipPlaneV.w * v.w;
+ Vector4f c = clipPlaneV.mult(2.0f / dot);
+
+ p.m20 = c.x - p.m30;
+ p.m21 = c.y - p.m31;
+ p.m22 = c.z - p.m32;
+ p.m23 = c.w - p.m33;
+ setProjectionMatrix(p);
+ }
+
+ /**
+ * Sets a clipPlane for this camera.
+ * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane
+ * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel
+ * more info here
+ * <ul>
+ * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a></li>
+ * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a></li>
+ * <li><a href="http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html">
+ * http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html</a></li>
+ * </ul>
+ *
+ * Note that this will work properly only if it's called on each update, and be aware that it won't work properly with the sky bucket.
+ * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java
+ * @param clipPlane the plane
+ */
+ public void setClipPlane(Plane clipPlane) {
+ setClipPlane(clipPlane, clipPlane.whichSide(location));
+ }
+
+ /**
+ * Resizes this camera's view with the given width and height. This is
+ * similar to constructing a new camera, but reusing the same Object. This
+ * method is called by an associated {@link RenderManager} to notify the camera of
+ * changes in the display dimensions.
+ *
+ * @param width the view width
+ * @param height the view height
+ * @param fixAspect If true, the camera's aspect ratio will be recomputed.
+ * Recomputing the aspect ratio requires changing the frustum values.
+ */
+ public void resize(int width, int height, boolean fixAspect) {
+ this.width = width;
+ this.height = height;
+ onViewPortChange();
+
+ if (fixAspect /*&& !parallelProjection*/) {
+ frustumRight = frustumTop * ((float) width / height);
+ frustumLeft = -frustumRight;
+ onFrustumChange();
+ }
+ }
+
+ /**
+ * <code>getFrustumBottom</code> returns the value of the bottom frustum
+ * plane.
+ *
+ * @return the value of the bottom frustum plane.
+ */
+ public float getFrustumBottom() {
+ return frustumBottom;
+ }
+
+ /**
+ * <code>setFrustumBottom</code> sets the value of the bottom frustum
+ * plane.
+ *
+ * @param frustumBottom the value of the bottom frustum plane.
+ */
+ public void setFrustumBottom(float frustumBottom) {
+ this.frustumBottom = frustumBottom;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>getFrustumFar</code> gets the value of the far frustum plane.
+ *
+ * @return the value of the far frustum plane.
+ */
+ public float getFrustumFar() {
+ return frustumFar;
+ }
+
+ /**
+ * <code>setFrustumFar</code> sets the value of the far frustum plane.
+ *
+ * @param frustumFar the value of the far frustum plane.
+ */
+ public void setFrustumFar(float frustumFar) {
+ this.frustumFar = frustumFar;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>getFrustumLeft</code> gets the value of the left frustum plane.
+ *
+ * @return the value of the left frustum plane.
+ */
+ public float getFrustumLeft() {
+ return frustumLeft;
+ }
+
+ /**
+ * <code>setFrustumLeft</code> sets the value of the left frustum plane.
+ *
+ * @param frustumLeft the value of the left frustum plane.
+ */
+ public void setFrustumLeft(float frustumLeft) {
+ this.frustumLeft = frustumLeft;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>getFrustumNear</code> gets the value of the near frustum plane.
+ *
+ * @return the value of the near frustum plane.
+ */
+ public float getFrustumNear() {
+ return frustumNear;
+ }
+
+ /**
+ * <code>setFrustumNear</code> sets the value of the near frustum plane.
+ *
+ * @param frustumNear the value of the near frustum plane.
+ */
+ public void setFrustumNear(float frustumNear) {
+ this.frustumNear = frustumNear;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>getFrustumRight</code> gets the value of the right frustum plane.
+ *
+ * @return frustumRight the value of the right frustum plane.
+ */
+ public float getFrustumRight() {
+ return frustumRight;
+ }
+
+ /**
+ * <code>setFrustumRight</code> sets the value of the right frustum plane.
+ *
+ * @param frustumRight the value of the right frustum plane.
+ */
+ public void setFrustumRight(float frustumRight) {
+ this.frustumRight = frustumRight;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>getFrustumTop</code> gets the value of the top frustum plane.
+ *
+ * @return the value of the top frustum plane.
+ */
+ public float getFrustumTop() {
+ return frustumTop;
+ }
+
+ /**
+ * <code>setFrustumTop</code> sets the value of the top frustum plane.
+ *
+ * @param frustumTop the value of the top frustum plane.
+ */
+ public void setFrustumTop(float frustumTop) {
+ this.frustumTop = frustumTop;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>getLocation</code> retrieves the location vector of the camera.
+ *
+ * @return the position of the camera.
+ * @see Camera#getLocation()
+ */
+ public Vector3f getLocation() {
+ return location;
+ }
+
+ /**
+ * <code>getRotation</code> retrieves the rotation quaternion of the camera.
+ *
+ * @return the rotation of the camera.
+ */
+ public Quaternion getRotation() {
+ return rotation;
+ }
+
+ /**
+ * <code>getDirection</code> retrieves the direction vector the camera is
+ * facing.
+ *
+ * @return the direction the camera is facing.
+ * @see Camera#getDirection()
+ */
+ public Vector3f getDirection() {
+ return rotation.getRotationColumn(2);
+ }
+
+ /**
+ * <code>getLeft</code> retrieves the left axis of the camera.
+ *
+ * @return the left axis of the camera.
+ * @see Camera#getLeft()
+ */
+ public Vector3f getLeft() {
+ return rotation.getRotationColumn(0);
+ }
+
+ /**
+ * <code>getUp</code> retrieves the up axis of the camera.
+ *
+ * @return the up axis of the camera.
+ * @see Camera#getUp()
+ */
+ public Vector3f getUp() {
+ return rotation.getRotationColumn(1);
+ }
+
+ /**
+ * <code>getDirection</code> retrieves the direction vector the camera is
+ * facing.
+ *
+ * @return the direction the camera is facing.
+ * @see Camera#getDirection()
+ */
+ public Vector3f getDirection(Vector3f store) {
+ return rotation.getRotationColumn(2, store);
+ }
+
+ /**
+ * <code>getLeft</code> retrieves the left axis of the camera.
+ *
+ * @return the left axis of the camera.
+ * @see Camera#getLeft()
+ */
+ public Vector3f getLeft(Vector3f store) {
+ return rotation.getRotationColumn(0, store);
+ }
+
+ /**
+ * <code>getUp</code> retrieves the up axis of the camera.
+ *
+ * @return the up axis of the camera.
+ * @see Camera#getUp()
+ */
+ public Vector3f getUp(Vector3f store) {
+ return rotation.getRotationColumn(1, store);
+ }
+
+ /**
+ * <code>setLocation</code> sets the position of the camera.
+ *
+ * @param location the position of the camera.
+ */
+ public void setLocation(Vector3f location) {
+ this.location.set(location);
+ onFrameChange();
+ }
+
+ /**
+ * <code>setRotation</code> sets the orientation of this camera.
+ * This will be equivelant to setting each of the axes:
+ * <code><br>
+ * cam.setLeft(rotation.getRotationColumn(0));<br>
+ * cam.setUp(rotation.getRotationColumn(1));<br>
+ * cam.setDirection(rotation.getRotationColumn(2));<br>
+ * </code>
+ *
+ * @param rotation the rotation of this camera
+ */
+ public void setRotation(Quaternion rotation) {
+ this.rotation.set(rotation);
+ onFrameChange();
+ }
+
+ /**
+ * <code>lookAtDirection</code> sets the direction the camera is facing
+ * given a direction and an up vector.
+ *
+ * @param direction the direction this camera is facing.
+ */
+ public void lookAtDirection(Vector3f direction, Vector3f up) {
+ this.rotation.lookAt(direction, up);
+ onFrameChange();
+ }
+
+ /**
+ * <code>setAxes</code> sets the axes (left, up and direction) for this
+ * camera.
+ *
+ * @param left the left axis of the camera.
+ * @param up the up axis of the camera.
+ * @param direction the direction the camera is facing.
+ *
+ * @see Camera#setAxes(com.jme3.math.Quaternion)
+ */
+ public void setAxes(Vector3f left, Vector3f up, Vector3f direction) {
+ this.rotation.fromAxes(left, up, direction);
+ onFrameChange();
+ }
+
+ /**
+ * <code>setAxes</code> uses a rotational matrix to set the axes of the
+ * camera.
+ *
+ * @param axes the matrix that defines the orientation of the camera.
+ */
+ public void setAxes(Quaternion axes) {
+ this.rotation.set(axes);
+ onFrameChange();
+ }
+
+ /**
+ * normalize normalizes the camera vectors.
+ */
+ public void normalize() {
+ this.rotation.normalizeLocal();
+ onFrameChange();
+ }
+
+ /**
+ * <code>setFrustum</code> sets the frustum of this camera object.
+ *
+ * @param near the near plane.
+ * @param far the far plane.
+ * @param left the left plane.
+ * @param right the right plane.
+ * @param top the top plane.
+ * @param bottom the bottom plane.
+ * @see Camera#setFrustum(float, float, float, float,
+ * float, float)
+ */
+ public void setFrustum(float near, float far, float left, float right,
+ float top, float bottom) {
+
+ frustumNear = near;
+ frustumFar = far;
+ frustumLeft = left;
+ frustumRight = right;
+ frustumTop = top;
+ frustumBottom = bottom;
+ onFrustumChange();
+ }
+
+ /**
+ * <code>setFrustumPerspective</code> defines the frustum for the camera. This
+ * frustum is defined by a viewing angle, aspect ratio, and near/far planes
+ *
+ * @param fovY Frame of view angle along the Y in degrees.
+ * @param aspect Width:Height ratio
+ * @param near Near view plane distance
+ * @param far Far view plane distance
+ */
+ public void setFrustumPerspective(float fovY, float aspect, float near,
+ float far) {
+ if (Float.isNaN(aspect) || Float.isInfinite(aspect)) {
+ // ignore.
+ logger.log(Level.WARNING, "Invalid aspect given to setFrustumPerspective: {0}", aspect);
+ return;
+ }
+
+ float h = FastMath.tan(fovY * FastMath.DEG_TO_RAD * .5f) * near;
+ float w = h * aspect;
+ frustumLeft = -w;
+ frustumRight = w;
+ frustumBottom = -h;
+ frustumTop = h;
+ frustumNear = near;
+ frustumFar = far;
+
+ onFrustumChange();
+ }
+
+ /**
+ * <code>setFrame</code> sets the orientation and location of the camera.
+ *
+ * @param location the point position of the camera.
+ * @param left the left axis of the camera.
+ * @param up the up axis of the camera.
+ * @param direction the facing of the camera.
+ * @see Camera#setFrame(com.jme3.math.Vector3f,
+ * com.jme3.math.Vector3f, com.jme3.math.Vector3f, com.jme3.math.Vector3f)
+ */
+ public void setFrame(Vector3f location, Vector3f left, Vector3f up,
+ Vector3f direction) {
+
+ this.location = location;
+ this.rotation.fromAxes(left, up, direction);
+ onFrameChange();
+ }
+
+ /**
+ * <code>lookAt</code> is a convienence method for auto-setting the frame
+ * based on a world position the user desires the camera to look at. It
+ * repoints the camera towards the given position using the difference
+ * between the position and the current camera location as a direction
+ * vector and the worldUpVector to compute up and left camera vectors.
+ *
+ * @param pos where to look at in terms of world coordinates
+ * @param worldUpVector a normalized vector indicating the up direction of the world.
+ * (typically {0, 1, 0} in jME.)
+ */
+ public void lookAt(Vector3f pos, Vector3f worldUpVector) {
+ TempVars vars = TempVars.get();
+ Vector3f newDirection = vars.vect1;
+ Vector3f newUp = vars.vect2;
+ Vector3f newLeft = vars.vect3;
+
+ newDirection.set(pos).subtractLocal(location).normalizeLocal();
+
+ newUp.set(worldUpVector).normalizeLocal();
+ if (newUp.equals(Vector3f.ZERO)) {
+ newUp.set(Vector3f.UNIT_Y);
+ }
+
+ newLeft.set(newUp).crossLocal(newDirection).normalizeLocal();
+ if (newLeft.equals(Vector3f.ZERO)) {
+ if (newDirection.x != 0) {
+ newLeft.set(newDirection.y, -newDirection.x, 0f);
+ } else {
+ newLeft.set(0f, newDirection.z, -newDirection.y);
+ }
+ }
+
+ newUp.set(newDirection).crossLocal(newLeft).normalizeLocal();
+
+ this.rotation.fromAxes(newLeft, newUp, newDirection);
+ this.rotation.normalizeLocal();
+ vars.release();
+
+ onFrameChange();
+ }
+
+ /**
+ * <code>setFrame</code> sets the orientation and location of the camera.
+ *
+ * @param location
+ * the point position of the camera.
+ * @param axes
+ * the orientation of the camera.
+ */
+ public void setFrame(Vector3f location, Quaternion axes) {
+ this.location = location;
+ this.rotation.set(axes);
+ onFrameChange();
+ }
+
+ /**
+ * <code>update</code> updates the camera parameters by calling
+ * <code>onFrustumChange</code>,<code>onViewPortChange</code> and
+ * <code>onFrameChange</code>.
+ *
+ * @see Camera#update()
+ */
+ public void update() {
+ onFrustumChange();
+ onViewPortChange();
+ onFrameChange();
+ }
+
+ /**
+ * <code>getPlaneState</code> returns the state of the frustum planes. So
+ * checks can be made as to which frustum plane has been examined for
+ * culling thus far.
+ *
+ * @return the current plane state int.
+ */
+ public int getPlaneState() {
+ return planeState;
+ }
+
+ /**
+ * <code>setPlaneState</code> sets the state to keep track of tested
+ * planes for culling.
+ *
+ * @param planeState the updated state.
+ */
+ public void setPlaneState(int planeState) {
+ this.planeState = planeState;
+ }
+
+ /**
+ * <code>getViewPortLeft</code> gets the left boundary of the viewport
+ *
+ * @return the left boundary of the viewport
+ */
+ public float getViewPortLeft() {
+ return viewPortLeft;
+ }
+
+ /**
+ * <code>setViewPortLeft</code> sets the left boundary of the viewport
+ *
+ * @param left the left boundary of the viewport
+ */
+ public void setViewPortLeft(float left) {
+ viewPortLeft = left;
+ onViewPortChange();
+ }
+
+ /**
+ * <code>getViewPortRight</code> gets the right boundary of the viewport
+ *
+ * @return the right boundary of the viewport
+ */
+ public float getViewPortRight() {
+ return viewPortRight;
+ }
+
+ /**
+ * <code>setViewPortRight</code> sets the right boundary of the viewport
+ *
+ * @param right the right boundary of the viewport
+ */
+ public void setViewPortRight(float right) {
+ viewPortRight = right;
+ onViewPortChange();
+ }
+
+ /**
+ * <code>getViewPortTop</code> gets the top boundary of the viewport
+ *
+ * @return the top boundary of the viewport
+ */
+ public float getViewPortTop() {
+ return viewPortTop;
+ }
+
+ /**
+ * <code>setViewPortTop</code> sets the top boundary of the viewport
+ *
+ * @param top the top boundary of the viewport
+ */
+ public void setViewPortTop(float top) {
+ viewPortTop = top;
+ onViewPortChange();
+ }
+
+ /**
+ * <code>getViewPortBottom</code> gets the bottom boundary of the viewport
+ *
+ * @return the bottom boundary of the viewport
+ */
+ public float getViewPortBottom() {
+ return viewPortBottom;
+ }
+
+ /**
+ * <code>setViewPortBottom</code> sets the bottom boundary of the viewport
+ *
+ * @param bottom the bottom boundary of the viewport
+ */
+ public void setViewPortBottom(float bottom) {
+ viewPortBottom = bottom;
+ onViewPortChange();
+ }
+
+ /**
+ * <code>setViewPort</code> sets the boundaries of the viewport
+ *
+ * @param left the left boundary of the viewport (default: 0)
+ * @param right the right boundary of the viewport (default: 1)
+ * @param bottom the bottom boundary of the viewport (default: 0)
+ * @param top the top boundary of the viewport (default: 1)
+ */
+ public void setViewPort(float left, float right, float bottom, float top) {
+ this.viewPortLeft = left;
+ this.viewPortRight = right;
+ this.viewPortBottom = bottom;
+ this.viewPortTop = top;
+ onViewPortChange();
+ }
+
+ /**
+ * Returns the pseudo distance from the given position to the near
+ * plane of the camera. This is used for render queue sorting.
+ * @param pos The position to compute a distance to.
+ * @return Distance from the far plane to the point.
+ */
+ public float distanceToNearPlane(Vector3f pos) {
+ return worldPlane[NEAR_PLANE].pseudoDistance(pos);
+ }
+
+ /**
+ * <code>contains</code> tests a bounding volume against the planes of the
+ * camera's frustum. The frustums planes are set such that the normals all
+ * face in towards the viewable scene. Therefore, if the bounding volume is
+ * on the negative side of the plane is can be culled out.
+ *
+ * NOTE: This method is used internally for culling, for public usage,
+ * the plane state of the bounding volume must be saved and restored, e.g:
+ * <code>BoundingVolume bv;<br/>
+ * Camera c;<br/>
+ * int planeState = bv.getPlaneState();<br/>
+ * bv.setPlaneState(0);<br/>
+ * c.contains(bv);<br/>
+ * bv.setPlaneState(plateState);<br/>
+ * </code>
+ *
+ * @param bound the bound to check for culling
+ * @return See enums in <code>FrustumIntersect</code>
+ */
+ public FrustumIntersect contains(BoundingVolume bound) {
+ if (bound == null) {
+ return FrustumIntersect.Inside;
+ }
+
+ int mask;
+ FrustumIntersect rVal = FrustumIntersect.Inside;
+
+ for (int planeCounter = FRUSTUM_PLANES; planeCounter >= 0; planeCounter--) {
+ if (planeCounter == bound.getCheckPlane()) {
+ continue; // we have already checked this plane at first iteration
+ }
+ int planeId = (planeCounter == FRUSTUM_PLANES) ? bound.getCheckPlane() : planeCounter;
+// int planeId = planeCounter;
+
+ mask = 1 << (planeId);
+ if ((planeState & mask) == 0) {
+ Plane.Side side = bound.whichSide(worldPlane[planeId]);
+
+ if (side == Plane.Side.Negative) {
+ //object is outside of frustum
+ bound.setCheckPlane(planeId);
+ return FrustumIntersect.Outside;
+ } else if (side == Plane.Side.Positive) {
+ //object is visible on *this* plane, so mark this plane
+ //so that we don't check it for sub nodes.
+ planeState |= mask;
+ } else {
+ rVal = FrustumIntersect.Intersects;
+ }
+ }
+ }
+
+ return rVal;
+ }
+
+ /**
+ * <code>containsGui</code> tests a bounding volume against the ortho
+ * bounding box of the camera. A bounding box spanning from
+ * 0, 0 to Width, Height. Constrained by the viewport settings on the
+ * camera.
+ *
+ * @param bound the bound to check for culling
+ * @return True if the camera contains the gui element bounding volume.
+ */
+ public boolean containsGui(BoundingVolume bound) {
+ return guiBounding.intersects(bound);
+ }
+
+ /**
+ * @return the view matrix of the camera.
+ * The view matrix transforms world space into eye space.
+ * This matrix is usually defined by the position and
+ * orientation of the camera.
+ */
+ public Matrix4f getViewMatrix() {
+ return viewMatrix;
+ }
+
+ /**
+ * Overrides the projection matrix used by the camera. Will
+ * use the matrix for computing the view projection matrix as well.
+ * Use null argument to return to normal functionality.
+ *
+ * @param projMatrix
+ */
+ public void setProjectionMatrix(Matrix4f projMatrix) {
+ projectionMatrixOverride = projMatrix;
+ updateViewProjection();
+ }
+
+ /**
+ * @return the projection matrix of the camera.
+ * The view projection matrix transforms eye space into clip space.
+ * This matrix is usually defined by the viewport and perspective settings
+ * of the camera.
+ */
+ public Matrix4f getProjectionMatrix() {
+ if (projectionMatrixOverride != null) {
+ return projectionMatrixOverride;
+ }
+
+ return projectionMatrix;
+ }
+
+ /**
+ * Updates the view projection matrix.
+ */
+ public void updateViewProjection() {
+ if (projectionMatrixOverride != null) {
+ viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix);
+ } else {
+ //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix);
+ viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix);
+ }
+ }
+
+ /**
+ * @return The result of multiplying the projection matrix by the view
+ * matrix. This matrix is required for rendering an object. It is
+ * precomputed so as to not compute it every time an object is rendered.
+ */
+ public Matrix4f getViewProjectionMatrix() {
+ return viewProjectionMatrix;
+ }
+
+ /**
+ * @return True if the viewport (width, height, left, right, bottom, up)
+ * has been changed. This is needed in the renderer so that the proper
+ * viewport can be set-up.
+ */
+ public boolean isViewportChanged() {
+ return viewportChanged;
+ }
+
+ /**
+ * Clears the viewport changed flag once it has been updated inside
+ * the renderer.
+ */
+ public void clearViewportChanged() {
+ viewportChanged = false;
+ }
+
+ /**
+ * Called when the viewport has been changed.
+ */
+ public void onViewPortChange() {
+ viewportChanged = true;
+ setGuiBounding();
+ }
+
+ private void setGuiBounding() {
+ float sx = width * viewPortLeft;
+ float ex = width * viewPortRight;
+ float sy = height * viewPortBottom;
+ float ey = height * viewPortTop;
+ float xExtent = Math.max(0f, (ex - sx) / 2f);
+ float yExtent = Math.max(0f, (ey - sy) / 2f);
+ guiBounding.setCenter(new Vector3f(sx + xExtent, sy + yExtent, 0));
+ guiBounding.setXExtent(xExtent);
+ guiBounding.setYExtent(yExtent);
+ guiBounding.setZExtent(Float.MAX_VALUE);
+ }
+
+ /**
+ * <code>onFrustumChange</code> updates the frustum to reflect any changes
+ * made to the planes. The new frustum values are kept in a temporary
+ * location for use when calculating the new frame. The projection
+ * matrix is updated to reflect the current values of the frustum.
+ */
+ public void onFrustumChange() {
+ if (!isParallelProjection()) {
+ float nearSquared = frustumNear * frustumNear;
+ float leftSquared = frustumLeft * frustumLeft;
+ float rightSquared = frustumRight * frustumRight;
+ float bottomSquared = frustumBottom * frustumBottom;
+ float topSquared = frustumTop * frustumTop;
+
+ float inverseLength = FastMath.invSqrt(nearSquared + leftSquared);
+ coeffLeft[0] = frustumNear * inverseLength;
+ coeffLeft[1] = -frustumLeft * inverseLength;
+
+ inverseLength = FastMath.invSqrt(nearSquared + rightSquared);
+ coeffRight[0] = -frustumNear * inverseLength;
+ coeffRight[1] = frustumRight * inverseLength;
+
+ inverseLength = FastMath.invSqrt(nearSquared + bottomSquared);
+ coeffBottom[0] = frustumNear * inverseLength;
+ coeffBottom[1] = -frustumBottom * inverseLength;
+
+ inverseLength = FastMath.invSqrt(nearSquared + topSquared);
+ coeffTop[0] = -frustumNear * inverseLength;
+ coeffTop[1] = frustumTop * inverseLength;
+ } else {
+ coeffLeft[0] = 1;
+ coeffLeft[1] = 0;
+
+ coeffRight[0] = -1;
+ coeffRight[1] = 0;
+
+ coeffBottom[0] = 1;
+ coeffBottom[1] = 0;
+
+ coeffTop[0] = -1;
+ coeffTop[1] = 0;
+ }
+
+ projectionMatrix.fromFrustum(frustumNear, frustumFar, frustumLeft, frustumRight, frustumTop, frustumBottom, parallelProjection);
+// projectionMatrix.transposeLocal();
+
+ // The frame is effected by the frustum values
+ // update it as well
+ onFrameChange();
+ }
+
+ /**
+ * <code>onFrameChange</code> updates the view frame of the camera.
+ */
+ public void onFrameChange() {
+ TempVars vars = TempVars.get();
+
+ Vector3f left = getLeft(vars.vect1);
+ Vector3f direction = getDirection(vars.vect2);
+ Vector3f up = getUp(vars.vect3);
+
+ float dirDotLocation = direction.dot(location);
+
+ // left plane
+ Vector3f leftPlaneNormal = worldPlane[LEFT_PLANE].getNormal();
+ leftPlaneNormal.x = left.x * coeffLeft[0];
+ leftPlaneNormal.y = left.y * coeffLeft[0];
+ leftPlaneNormal.z = left.z * coeffLeft[0];
+ leftPlaneNormal.addLocal(direction.x * coeffLeft[1], direction.y
+ * coeffLeft[1], direction.z * coeffLeft[1]);
+ worldPlane[LEFT_PLANE].setConstant(location.dot(leftPlaneNormal));
+
+ // right plane
+ Vector3f rightPlaneNormal = worldPlane[RIGHT_PLANE].getNormal();
+ rightPlaneNormal.x = left.x * coeffRight[0];
+ rightPlaneNormal.y = left.y * coeffRight[0];
+ rightPlaneNormal.z = left.z * coeffRight[0];
+ rightPlaneNormal.addLocal(direction.x * coeffRight[1], direction.y
+ * coeffRight[1], direction.z * coeffRight[1]);
+ worldPlane[RIGHT_PLANE].setConstant(location.dot(rightPlaneNormal));
+
+ // bottom plane
+ Vector3f bottomPlaneNormal = worldPlane[BOTTOM_PLANE].getNormal();
+ bottomPlaneNormal.x = up.x * coeffBottom[0];
+ bottomPlaneNormal.y = up.y * coeffBottom[0];
+ bottomPlaneNormal.z = up.z * coeffBottom[0];
+ bottomPlaneNormal.addLocal(direction.x * coeffBottom[1], direction.y
+ * coeffBottom[1], direction.z * coeffBottom[1]);
+ worldPlane[BOTTOM_PLANE].setConstant(location.dot(bottomPlaneNormal));
+
+ // top plane
+ Vector3f topPlaneNormal = worldPlane[TOP_PLANE].getNormal();
+ topPlaneNormal.x = up.x * coeffTop[0];
+ topPlaneNormal.y = up.y * coeffTop[0];
+ topPlaneNormal.z = up.z * coeffTop[0];
+ topPlaneNormal.addLocal(direction.x * coeffTop[1], direction.y
+ * coeffTop[1], direction.z * coeffTop[1]);
+ worldPlane[TOP_PLANE].setConstant(location.dot(topPlaneNormal));
+
+ if (isParallelProjection()) {
+ worldPlane[LEFT_PLANE].setConstant(worldPlane[LEFT_PLANE].getConstant() + frustumLeft);
+ worldPlane[RIGHT_PLANE].setConstant(worldPlane[RIGHT_PLANE].getConstant() - frustumRight);
+ worldPlane[TOP_PLANE].setConstant(worldPlane[TOP_PLANE].getConstant() - frustumTop);
+ worldPlane[BOTTOM_PLANE].setConstant(worldPlane[BOTTOM_PLANE].getConstant() + frustumBottom);
+ }
+
+ // far plane
+ worldPlane[FAR_PLANE].setNormal(left);
+ worldPlane[FAR_PLANE].setNormal(-direction.x, -direction.y, -direction.z);
+ worldPlane[FAR_PLANE].setConstant(-(dirDotLocation + frustumFar));
+
+ // near plane
+ worldPlane[NEAR_PLANE].setNormal(direction.x, direction.y, direction.z);
+ worldPlane[NEAR_PLANE].setConstant(dirDotLocation + frustumNear);
+
+ viewMatrix.fromFrame(location, direction, up, left);
+
+ vars.release();
+
+// viewMatrix.transposeLocal();
+ updateViewProjection();
+ }
+
+ /**
+ * @return true if parallel projection is enable, false if in normal perspective mode
+ * @see #setParallelProjection(boolean)
+ */
+ public boolean isParallelProjection() {
+ return this.parallelProjection;
+ }
+
+ /**
+ * Enable/disable parallel projection.
+ *
+ * @param value true to set up this camera for parallel projection is enable, false to enter normal perspective mode
+ */
+ public void setParallelProjection(final boolean value) {
+ this.parallelProjection = value;
+ onFrustumChange();
+ }
+
+ /**
+ * @see Camera#getWorldCoordinates
+ */
+ public Vector3f getWorldCoordinates(Vector2f screenPos, float zPos) {
+ return getWorldCoordinates(screenPos, zPos, null);
+ }
+
+ /**
+ * @see Camera#getWorldCoordinates
+ */
+ public Vector3f getWorldCoordinates(Vector2f screenPosition,
+ float zPos, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ Matrix4f inverseMat = new Matrix4f(viewProjectionMatrix);
+ inverseMat.invertLocal();
+
+ store.set(
+ (screenPosition.x / getWidth() - viewPortLeft) / (viewPortRight - viewPortLeft) * 2 - 1,
+ (screenPosition.y / getHeight() - viewPortBottom) / (viewPortTop - viewPortBottom) * 2 - 1,
+ zPos * 2 - 1);
+
+ float w = inverseMat.multProj(store, store);
+ store.multLocal(1f / w);
+
+ return store;
+ }
+
+ /**
+ * Converts the given position from world space to screen space.
+ *
+ * @see Camera#getScreenCoordinates
+ */
+ public Vector3f getScreenCoordinates(Vector3f worldPos) {
+ return getScreenCoordinates(worldPos, null);
+ }
+
+ /**
+ * Converts the given position from world space to screen space.
+ *
+ * @see Camera#getScreenCoordinates(Vector3f, Vector3f)
+ */
+ public Vector3f getScreenCoordinates(Vector3f worldPosition, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+// TempVars vars = vars.lock();
+// Quaternion tmp_quat = vars.quat1;
+// tmp_quat.set( worldPosition.x, worldPosition.y, worldPosition.z, 1 );
+// viewProjectionMatrix.mult(tmp_quat, tmp_quat);
+// tmp_quat.multLocal( 1.0f / tmp_quat.getW() );
+// store.x = ( ( tmp_quat.getX() + 1 ) * ( viewPortRight - viewPortLeft ) / 2 + viewPortLeft ) * getWidth();
+// store.y = ( ( tmp_quat.getY() + 1 ) * ( viewPortTop - viewPortBottom ) / 2 + viewPortBottom ) * getHeight();
+// store.z = ( tmp_quat.getZ() + 1 ) / 2;
+// vars.release();
+
+ float w = viewProjectionMatrix.multProj(worldPosition, store);
+ store.divideLocal(w);
+
+ store.x = ((store.x + 1f) * (viewPortRight - viewPortLeft) / 2f + viewPortLeft) * getWidth();
+ store.y = ((store.y + 1f) * (viewPortTop - viewPortBottom) / 2f + viewPortBottom) * getHeight();
+ store.z = (store.z + 1f) / 2f;
+
+ return store;
+ }
+
+ /**
+ * @return the width/resolution of the display.
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return the height/resolution of the display.
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ @Override
+ public String toString() {
+ return "Camera[location=" + location + "\n, direction=" + getDirection() + "\n"
+ + "res=" + width + "x" + height + ", parallel=" + parallelProjection + "\n"
+ + "near=" + frustumNear + ", far=" + frustumFar + "]";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(location, "location", Vector3f.ZERO);
+ capsule.write(rotation, "rotation", Quaternion.DIRECTION_Z);
+ capsule.write(frustumNear, "frustumNear", 1);
+ capsule.write(frustumFar, "frustumFar", 2);
+ capsule.write(frustumLeft, "frustumLeft", -0.5f);
+ capsule.write(frustumRight, "frustumRight", 0.5f);
+ capsule.write(frustumTop, "frustumTop", 0.5f);
+ capsule.write(frustumBottom, "frustumBottom", -0.5f);
+ capsule.write(coeffLeft, "coeffLeft", new float[2]);
+ capsule.write(coeffRight, "coeffRight", new float[2]);
+ capsule.write(coeffBottom, "coeffBottom", new float[2]);
+ capsule.write(coeffTop, "coeffTop", new float[2]);
+ capsule.write(viewPortLeft, "viewPortLeft", 0);
+ capsule.write(viewPortRight, "viewPortRight", 1);
+ capsule.write(viewPortTop, "viewPortTop", 1);
+ capsule.write(viewPortBottom, "viewPortBottom", 0);
+ capsule.write(width, "width", 0);
+ capsule.write(height, "height", 0);
+ capsule.write(name, "name", null);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ location = (Vector3f) capsule.readSavable("location", Vector3f.ZERO.clone());
+ rotation = (Quaternion) capsule.readSavable("rotation", Quaternion.DIRECTION_Z.clone());
+ frustumNear = capsule.readFloat("frustumNear", 1);
+ frustumFar = capsule.readFloat("frustumFar", 2);
+ frustumLeft = capsule.readFloat("frustumLeft", -0.5f);
+ frustumRight = capsule.readFloat("frustumRight", 0.5f);
+ frustumTop = capsule.readFloat("frustumTop", 0.5f);
+ frustumBottom = capsule.readFloat("frustumBottom", -0.5f);
+ coeffLeft = capsule.readFloatArray("coeffLeft", new float[2]);
+ coeffRight = capsule.readFloatArray("coeffRight", new float[2]);
+ coeffBottom = capsule.readFloatArray("coeffBottom", new float[2]);
+ coeffTop = capsule.readFloatArray("coeffTop", new float[2]);
+ viewPortLeft = capsule.readFloat("viewPortLeft", 0);
+ viewPortRight = capsule.readFloat("viewPortRight", 1);
+ viewPortTop = capsule.readFloat("viewPortTop", 1);
+ viewPortBottom = capsule.readFloat("viewPortBottom", 0);
+ width = capsule.readInt("width", 1);
+ height = capsule.readInt("height", 1);
+ name = capsule.readString("name", null);
+ onFrustumChange();
+ onViewPortChange();
+ onFrameChange();
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/Caps.java b/engine/src/core/com/jme3/renderer/Caps.java
new file mode 100644
index 0000000..4dd1ed8
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Caps.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer;
+
+import com.jme3.shader.Shader;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.RenderBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import java.util.Collection;
+
+/**
+ * <code>Caps</code> is an enum specifying a capability that the {@link Renderer}
+ * supports.
+ *
+ * @author Kirill Vainer
+ */
+public enum Caps {
+
+ /**
+ * Supports {@link FrameBuffer FrameBuffers}.
+ * <p>
+ * OpenGL: Renderer exposes the GL_EXT_framebuffer_object extension.<br>
+ * OpenGL ES: Renderer supports OpenGL ES 2.0.
+ */
+ FrameBuffer,
+
+ /**
+ * Supports framebuffer Multiple Render Targets (MRT)
+ * <p>
+ * OpenGL: Renderer exposes the GL_ARB_draw_buffers extension
+ */
+ FrameBufferMRT,
+
+ /**
+ * Supports framebuffer multi-sampling
+ * <p>
+ * OpenGL: Renderer exposes the GL EXT framebuffer multisample extension<br>
+ * OpenGL ES: Renderer exposes GL_APPLE_framebuffer_multisample or
+ * GL_ANGLE_framebuffer_multisample.
+ */
+ FrameBufferMultisample,
+
+ /**
+ * Supports texture multi-sampling
+ * <p>
+ * OpenGL: Renderer exposes the GL_ARB_texture_multisample extension<br>
+ * OpenGL ES: Renderer exposes the GL_IMG_multisampled_render_to_texture
+ * extension.
+ */
+ TextureMultisample,
+
+ /**
+ * Supports OpenGL 2.0 or OpenGL ES 2.0.
+ */
+ OpenGL20,
+
+ /**
+ * Supports OpenGL 2.1
+ */
+ OpenGL21,
+
+ /**
+ * Supports OpenGL 3.0
+ */
+ OpenGL30,
+
+ /**
+ * Supports OpenGL 3.1
+ */
+ OpenGL31,
+
+ /**
+ * Supports OpenGL 3.2
+ */
+ OpenGL32,
+
+ /**
+ * Supports OpenGL ARB program.
+ * <p>
+ * OpenGL: Renderer exposes ARB_vertex_program and ARB_fragment_program
+ * extensions.
+ */
+ ARBprogram,
+
+ /**
+ * Supports GLSL 1.0
+ */
+ GLSL100,
+
+ /**
+ * Supports GLSL 1.1
+ */
+ GLSL110,
+
+ /**
+ * Supports GLSL 1.2
+ */
+ GLSL120,
+
+ /**
+ * Supports GLSL 1.3
+ */
+ GLSL130,
+
+ /**
+ * Supports GLSL 1.4
+ */
+ GLSL140,
+
+ /**
+ * Supports GLSL 1.5
+ */
+ GLSL150,
+
+ /**
+ * Supports GLSL 3.3
+ */
+ GLSL330,
+
+ /**
+ * Supports reading from textures inside the vertex shader.
+ */
+ VertexTextureFetch,
+
+ /**
+ * Supports geometry shader.
+ */
+ GeometryShader,
+
+ /**
+ * Supports texture arrays
+ */
+ TextureArray,
+
+ /**
+ * Supports texture buffers
+ */
+ TextureBuffer,
+
+ /**
+ * Supports floating point textures (Format.RGB16F)
+ */
+ FloatTexture,
+
+ /**
+ * Supports floating point FBO color buffers (Format.RGB16F)
+ */
+ FloatColorBuffer,
+
+ /**
+ * Supports floating point depth buffer
+ */
+ FloatDepthBuffer,
+
+ /**
+ * Supports Format.RGB111110F for textures
+ */
+ PackedFloatTexture,
+
+ /**
+ * Supports Format.RGB9E5 for textures
+ */
+ SharedExponentTexture,
+
+ /**
+ * Supports Format.RGB111110F for FBO color buffers
+ */
+ PackedFloatColorBuffer,
+
+ /**
+ * Supports Format.RGB9E5 for FBO color buffers
+ */
+ SharedExponentColorBuffer,
+
+ /**
+ * Supports Format.LATC for textures, this includes
+ * support for ATI's 3Dc texture compression.
+ */
+ TextureCompressionLATC,
+
+ /**
+ * Supports Non-Power-Of-Two (NPOT) textures and framebuffers
+ */
+ NonPowerOfTwoTextures,
+
+ /// Vertex Buffer features
+ MeshInstancing,
+
+ /**
+ * Supports VAO, or vertex buffer arrays
+ */
+ VertexBufferArray,
+
+ /**
+ * Supports multisampling on the screen
+ */
+ Multisample;
+
+ /**
+ * Returns true if given the renderer capabilities, the texture
+ * can be supported by the renderer.
+ * <p>
+ * This only checks the format of the texture, non-power-of-2
+ * textures are scaled automatically inside the renderer
+ * if are not supported natively.
+ *
+ * @param caps The collection of renderer capabilities {@link Renderer#getCaps() }.
+ * @param tex The texture to check
+ * @return True if it is supported, false otherwise.
+ */
+ public static boolean supports(Collection<Caps> caps, Texture tex){
+ if (tex.getType() == Texture.Type.TwoDimensionalArray
+ && !caps.contains(Caps.TextureArray))
+ return false;
+
+ Image img = tex.getImage();
+ if (img == null)
+ return true;
+
+ Format fmt = img.getFormat();
+ switch (fmt){
+ case Depth32F:
+ return caps.contains(Caps.FloatDepthBuffer);
+ case LATC:
+ return caps.contains(Caps.TextureCompressionLATC);
+ case RGB16F_to_RGB111110F:
+ case RGB111110F:
+ return caps.contains(Caps.PackedFloatTexture);
+ case RGB16F_to_RGB9E5:
+ case RGB9E5:
+ return caps.contains(Caps.SharedExponentTexture);
+ default:
+ if (fmt.isFloatingPont())
+ return caps.contains(Caps.FloatTexture);
+
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if given the renderer capabilities, the framebuffer
+ * can be supported by the renderer.
+ *
+ * @param caps The collection of renderer capabilities {@link Renderer#getCaps() }.
+ * @param fb The framebuffer to check
+ * @return True if it is supported, false otherwise.
+ */
+ public static boolean supports(Collection<Caps> caps, FrameBuffer fb){
+ if (!caps.contains(Caps.FrameBuffer))
+ return false;
+
+ if (fb.getSamples() > 1
+ && !caps.contains(Caps.FrameBufferMultisample))
+ return false;
+
+ RenderBuffer colorBuf = fb.getColorBuffer();
+ RenderBuffer depthBuf = fb.getDepthBuffer();
+
+ if (depthBuf != null){
+ Format depthFmt = depthBuf.getFormat();
+ if (!depthFmt.isDepthFormat()){
+ return false;
+ }else{
+ if (depthFmt == Format.Depth32F
+ && !caps.contains(Caps.FloatDepthBuffer))
+ return false;
+ }
+ }
+ if (colorBuf != null){
+ Format colorFmt = colorBuf.getFormat();
+ if (colorFmt.isDepthFormat())
+ return false;
+
+ if (colorFmt.isCompressed())
+ return false;
+
+ switch (colorFmt){
+ case RGB111110F:
+ return caps.contains(Caps.PackedFloatColorBuffer);
+ case RGB16F_to_RGB111110F:
+ case RGB16F_to_RGB9E5:
+ case RGB9E5:
+ return false;
+ default:
+ if (colorFmt.isFloatingPont())
+ return caps.contains(Caps.FloatColorBuffer);
+
+ return true;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if given the renderer capabilities, the shader
+ * can be supported by the renderer.
+ *
+ * @param caps The collection of renderer capabilities {@link Renderer#getCaps() }.
+ * @param shader The shader to check
+ * @return True if it is supported, false otherwise.
+ */
+ public static boolean supports(Collection<Caps> caps, Shader shader){
+ String lang = shader.getLanguage();
+ if (lang.startsWith("GLSL")){
+ int ver = Integer.parseInt(lang.substring(4));
+ switch (ver){
+ case 100:
+ return caps.contains(Caps.GLSL100);
+ case 110:
+ return caps.contains(Caps.GLSL110);
+ case 120:
+ return caps.contains(Caps.GLSL120);
+ case 130:
+ return caps.contains(Caps.GLSL130);
+ case 140:
+ return caps.contains(Caps.GLSL140);
+ case 150:
+ return caps.contains(Caps.GLSL150);
+ case 330:
+ return caps.contains(Caps.GLSL330);
+ default:
+ return false;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/GL1Renderer.java b/engine/src/core/com/jme3/renderer/GL1Renderer.java
new file mode 100644
index 0000000..78df0c6
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/GL1Renderer.java
@@ -0,0 +1,26 @@
+package com.jme3.renderer;
+
+import com.jme3.material.FixedFuncBinding;
+
+/**
+ * Renderer sub-interface that is used for non-shader based renderers.
+ * <p>
+ * The <code>GL1Renderer</code> provides a single call,
+ * {@link #setFixedFuncBinding(com.jme3.material.FixedFuncBinding, java.lang.Object) }
+ * which allows to set fixed functionality state.
+ *
+ * @author Kirill Vainer
+ */
+public interface GL1Renderer extends Renderer {
+
+ /**
+ * Set the fixed functionality state.
+ * <p>
+ * See {@link FixedFuncBinding} for various values that
+ * can be set.
+ *
+ * @param ffBinding The binding to set
+ * @param val The value
+ */
+ public void setFixedFuncBinding(FixedFuncBinding ffBinding, Object val);
+}
diff --git a/engine/src/core/com/jme3/renderer/IDList.java b/engine/src/core/com/jme3/renderer/IDList.java
new file mode 100644
index 0000000..2db7294
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/IDList.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer;
+
+import java.util.Arrays;
+
+/**
+ * A specialized data-structure used to optimize state changes of "slot"
+ * based state.
+ */
+public class IDList {
+
+ public int[] newList = new int[16];
+ public int[] oldList = new int[16];
+ public int newLen = 0;
+ public int oldLen = 0;
+
+ /**
+ * Reset all states to zero
+ */
+ public void reset(){
+ newLen = 0;
+ oldLen = 0;
+ Arrays.fill(newList, 0);
+ Arrays.fill(oldList, 0);
+ }
+
+ /**
+ * Adds an index to the new list.
+ * If the index was not in the old list, false is returned,
+ * if the index was in the old list, it is removed from the old
+ * list and true is returned.
+ *
+ * @param idx The index to move
+ * @return True if it existed in old list and was removed
+ * from there, false otherwise.
+ */
+ public boolean moveToNew(int idx){
+ if (newLen == 0 || newList[newLen-1] != idx)
+ // add item to newList first
+ newList[newLen++] = idx;
+
+ // find idx in oldList, if removed successfuly, return true.
+ for (int i = 0; i < oldLen; i++){
+ if (oldList[i] == idx){
+ // found index in slot i
+ // delete index from old list
+ oldLen --;
+ for (int j = i; j < oldLen; j++){
+ oldList[j] = oldList[j+1];
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Copies the new list to the old list, and clears the new list.
+ */
+ public void copyNewToOld(){
+ System.arraycopy(newList, 0, oldList, 0, newLen);
+ oldLen = newLen;
+ newLen = 0;
+ }
+
+ /**
+ * Prints the contents of the lists
+ */
+ public void print(){
+ if (newLen > 0){
+ System.out.print("New List: ");
+ for (int i = 0; i < newLen; i++){
+ if (i == newLen -1)
+ System.out.println(newList[i]);
+ else
+ System.out.print(newList[i]+", ");
+ }
+ }
+ if (oldLen > 0){
+ System.out.print("Old List: ");
+ for (int i = 0; i < oldLen; i++){
+ if (i == oldLen -1)
+ System.out.println(oldList[i]);
+ else
+ System.out.print(oldList[i]+", ");
+ }
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/RenderContext.java b/engine/src/core/com/jme3/renderer/RenderContext.java
new file mode 100644
index 0000000..ee9ff9c
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/RenderContext.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer;
+
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+
+/**
+ * Represents the current state of the graphics library. This class is used
+ * internally to reduce state changes. NOTE: This class is specific to OpenGL.
+ */
+public class RenderContext {
+
+ /**
+ * @see RenderState#setFaceCullMode(com.jme3.material.RenderState.FaceCullMode)
+ */
+ public RenderState.FaceCullMode cullMode = RenderState.FaceCullMode.Off;
+
+ /**
+ * @see RenderState#setDepthTest(boolean)
+ */
+ public boolean depthTestEnabled = false;
+
+ /**
+ * @see RenderState#setAlphaTest(boolean)
+ */
+ public boolean alphaTestEnabled = false;
+
+ /**
+ * @see RenderState#setDepthWrite(boolean)
+ */
+ public boolean depthWriteEnabled = true;
+
+ /**
+ * @see RenderState#setColorWrite(boolean)
+ */
+ public boolean colorWriteEnabled = true;
+
+ /**
+ * @see Renderer#setClipRect(int, int, int, int)
+ */
+ public boolean clipRectEnabled = false;
+
+ /**
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public boolean polyOffsetEnabled = false;
+
+ /**
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public float polyOffsetFactor = 0;
+
+ /**
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public float polyOffsetUnits = 0;
+
+ /**
+ * For normals only. Uses GL_NORMALIZE.
+ *
+ * @see VertexBuffer#setNormalized(boolean)
+ */
+ public boolean normalizeEnabled = false;
+
+ /**
+ * For glMatrixMode.
+ *
+ * @see Renderer#setWorldMatrix(com.jme3.math.Matrix4f)
+ * @see Renderer#setViewProjectionMatrices(com.jme3.math.Matrix4f, com.jme3.math.Matrix4f)
+ */
+ public int matrixMode = -1;
+
+ /**
+ * @see Mesh#setPointSize(float)
+ */
+ public float pointSize = 1;
+
+ /**
+ * @see Mesh#setLineWidth(float)
+ */
+ public float lineWidth = 1;
+
+ /**
+ * @see RenderState#setBlendMode(com.jme3.material.RenderState.BlendMode)
+ */
+ public RenderState.BlendMode blendMode = RenderState.BlendMode.Off;
+
+ /**
+ * @see RenderState#setWireframe(boolean)
+ */
+ public boolean wireframe = false;
+
+ /**
+ * @see RenderState#setPointSprite(boolean)
+ */
+ public boolean pointSprite = false;
+
+ /**
+ * @see Renderer#setShader(com.jme3.shader.Shader)
+ */
+ public int boundShaderProgram;
+
+ /**
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ */
+ public int boundFBO = 0;
+
+ /**
+ * Currently bound Renderbuffer
+ *
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ */
+ public int boundRB = 0;
+
+ /**
+ * Currently bound draw buffer
+ * -2 = GL_NONE
+ * -1 = GL_BACK
+ * 0 = GL_COLOR_ATTACHMENT0
+ * n = GL_COLOR_ATTACHMENTn
+ * where n is an integer greater than 1
+ *
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ * @see FrameBuffer#setTargetIndex(int)
+ */
+ public int boundDrawBuf = -1;
+
+ /**
+ * Currently bound read buffer
+ *
+ * @see RenderContext#boundDrawBuf
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ * @see FrameBuffer#setTargetIndex(int)
+ */
+ public int boundReadBuf = -1;
+
+ /**
+ * Currently bound element array vertex buffer.
+ *
+ * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int)
+ */
+ public int boundElementArrayVBO;
+
+ /**
+ * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int)
+ */
+ public int boundVertexArray;
+
+ /**
+ * Currently bound array vertex buffer.
+ *
+ * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int)
+ */
+ public int boundArrayVBO;
+
+ public int numTexturesSet = 0;
+
+ /**
+ * Current bound texture IDs for each texture unit.
+ *
+ * @see Renderer#setTexture(int, com.jme3.texture.Texture)
+ */
+ public Image[] boundTextures = new Image[16];
+
+ /**
+ * IDList for texture units
+ *
+ * @see Renderer#setTexture(int, com.jme3.texture.Texture)
+ */
+ public IDList textureIndexList = new IDList();
+
+ /**
+ * Currently bound texture unit
+ *
+ * @see Renderer#setTexture(int, com.jme3.texture.Texture)
+ */
+ public int boundTextureUnit = 0;
+
+ /**
+ * Stencil Buffer state
+ */
+ public boolean stencilTest = false;
+ public RenderState.StencilOperation frontStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation frontStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation frontStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation backStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation backStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation backStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ public RenderState.TestFunction frontStencilFunction = RenderState.TestFunction.Always;
+ public RenderState.TestFunction backStencilFunction = RenderState.TestFunction.Always;
+
+ /**
+ * Vertex attribs currently bound and enabled. If a slot is null, then
+ * it is disabled.
+ */
+ public VertexBuffer[] boundAttribs = new VertexBuffer[16];
+
+ /**
+ * IDList for vertex attributes
+ */
+ public IDList attribIndexList = new IDList();
+
+ /**
+ * Ambient color (GL1 only)
+ */
+ public ColorRGBA ambient;
+
+ /**
+ * Diffuse color (GL1 only)
+ */
+ public ColorRGBA diffuse;
+
+ /**
+ * Specular color (GL1 only)
+ */
+ public ColorRGBA specular;
+
+ /**
+ * Material color (GL1 only)
+ */
+ public ColorRGBA color;
+
+ /**
+ * Shininess (GL1 only)
+ */
+ public float shininess;
+
+ /**
+ * Use vertex color (GL1 only)
+ */
+ public boolean useVertexColor;
+
+ /**
+ * Reset the RenderContext to default GL state
+ */
+ public void reset(){
+ cullMode = RenderState.FaceCullMode.Off;
+ depthTestEnabled = false;
+ alphaTestEnabled = false;
+ depthWriteEnabled = false;
+ colorWriteEnabled = false;
+ clipRectEnabled = false;
+ polyOffsetEnabled = false;
+ polyOffsetFactor = 0;
+ polyOffsetUnits = 0;
+ normalizeEnabled = false;
+ matrixMode = -1;
+ pointSize = 1;
+ blendMode = RenderState.BlendMode.Off;
+ wireframe = false;
+ boundShaderProgram = 0;
+ boundFBO = 0;
+ boundRB = 0;
+ boundDrawBuf = -1;
+ boundReadBuf = -1;
+ boundElementArrayVBO = 0;
+ boundVertexArray = 0;
+ boundArrayVBO = 0;
+ numTexturesSet = 0;
+ for (int i = 0; i < boundTextures.length; i++)
+ boundTextures[i] = null;
+
+ textureIndexList.reset();
+ boundTextureUnit = 0;
+ for (int i = 0; i < boundAttribs.length; i++)
+ boundAttribs[i] = null;
+
+ attribIndexList.reset();
+
+ stencilTest = false;
+ frontStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ frontStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ frontStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ backStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ backStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ backStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ frontStencilFunction = RenderState.TestFunction.Always;
+ backStencilFunction = RenderState.TestFunction.Always;
+
+ ambient = diffuse = specular = color = null;
+ shininess = 0;
+ useVertexColor = false;
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java
new file mode 100644
index 0000000..1d58d22
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/RenderManager.java
@@ -0,0 +1,1170 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.renderer;
+
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.RenderState;
+import com.jme3.material.Technique;
+import com.jme3.math.*;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.*;
+import com.jme3.shader.Uniform;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VarType;
+import com.jme3.system.NullRenderer;
+import com.jme3.system.Timer;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.TempVars;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * <code>RenderManager</code> is a high-level rendering interface that is
+ * above the Renderer implementation. RenderManager takes care
+ * of rendering the scene graphs attached to each viewport and
+ * handling SceneProcessors.
+ *
+ * @see SceneProcessor
+ * @see ViewPort
+ * @see Spatial
+ */
+public class RenderManager {
+
+ private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
+
+ private Renderer renderer;
+ private Timer timer;
+ private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
+ private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
+ private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
+ private Camera prevCam = null;
+ private Material forcedMaterial = null;
+ private String forcedTechnique = null;
+ private RenderState forcedRenderState = null;
+ private boolean shader;
+ private int viewX, viewY, viewWidth, viewHeight;
+ private float near, far;
+ private Matrix4f orthoMatrix = new Matrix4f();
+ private Matrix4f viewMatrix = new Matrix4f();
+ private Matrix4f projMatrix = new Matrix4f();
+ private Matrix4f viewProjMatrix = new Matrix4f();
+ private Matrix4f worldMatrix = new Matrix4f();
+ private Vector3f camUp = new Vector3f(),
+ camLeft = new Vector3f(),
+ camDir = new Vector3f(),
+ camLoc = new Vector3f();
+ //temp technique
+ private String tmpTech;
+ private boolean handleTranlucentBucket = true;
+
+ /**
+ * Create a high-level rendering interface over the
+ * low-level rendering interface.
+ * @param renderer
+ */
+ public RenderManager(Renderer renderer) {
+ this.renderer = renderer;
+ //this.shader = renderer.getCaps().contains(Caps.GLSL100);
+ }
+
+ /**
+ * Returns the pre ViewPort with the given name.
+ *
+ * @param viewName The name of the pre ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getPreView(String viewName) {
+ for (int i = 0; i < preViewPorts.size(); i++) {
+ if (preViewPorts.get(i).getName().equals(viewName)) {
+ return preViewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the specified pre ViewPort.
+ *
+ * @param view The pre ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePreView(ViewPort view) {
+ return preViewPorts.remove(view);
+ }
+
+ /**
+ * Returns the main ViewPort with the given name.
+ *
+ * @param viewName The name of the main ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getMainView(String viewName) {
+ for (int i = 0; i < viewPorts.size(); i++) {
+ if (viewPorts.get(i).getName().equals(viewName)) {
+ return viewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the main ViewPort with the specified name.
+ *
+ * @param viewName The main ViewPort name to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removeMainView(String viewName) {
+ for (int i = 0; i < viewPorts.size(); i++) {
+ if (viewPorts.get(i).getName().equals(viewName)) {
+ viewPorts.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified main ViewPort.
+ *
+ * @param view The main ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removeMainView(ViewPort view) {
+ return viewPorts.remove(view);
+ }
+
+ /**
+ * Returns the post ViewPort with the given name.
+ *
+ * @param viewName The name of the post ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getPostView(String viewName) {
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ if (postViewPorts.get(i).getName().equals(viewName)) {
+ return postViewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the post ViewPort with the specified name.
+ *
+ * @param viewName The post ViewPort name to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePostView(String viewName) {
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ if (postViewPorts.get(i).getName().equals(viewName)) {
+ postViewPorts.remove(i);
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified post ViewPort.
+ *
+ * @param view The post ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePostView(ViewPort view) {
+ return postViewPorts.remove(view);
+ }
+
+ /**
+ * Returns a read-only list of all pre ViewPorts
+ * @return a read-only list of all pre ViewPorts
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getPreViews() {
+ return Collections.unmodifiableList(preViewPorts);
+ }
+
+ /**
+ * Returns a read-only list of all main ViewPorts
+ * @return a read-only list of all main ViewPorts
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getMainViews() {
+ return Collections.unmodifiableList(viewPorts);
+ }
+
+ /**
+ * Returns a read-only list of all post ViewPorts
+ * @return a read-only list of all post ViewPorts
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getPostViews() {
+ return Collections.unmodifiableList(postViewPorts);
+ }
+
+ /**
+ * Creates a new pre ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed before the main and post viewports.
+ */
+ public ViewPort createPreView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ preViewPorts.add(vp);
+ return vp;
+ }
+
+ /**
+ * Creates a new main ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed before the post viewports but after
+ * the pre viewports.
+ */
+ public ViewPort createMainView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ viewPorts.add(vp);
+ return vp;
+ }
+
+ /**
+ * Creates a new post ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed after the pre and main viewports.
+ */
+ public ViewPort createPostView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ postViewPorts.add(vp);
+ return vp;
+ }
+
+ private void notifyReshape(ViewPort vp, int w, int h) {
+ List<SceneProcessor> processors = vp.getProcessors();
+ for (SceneProcessor proc : processors) {
+ if (!proc.isInitialized()) {
+ proc.initialize(this, vp);
+ } else {
+ proc.reshape(vp, w, h);
+ }
+ }
+ }
+
+ /**
+ * Internal use only.
+ * Updates the resolution of all on-screen cameras to match
+ * the given width and height.
+ */
+ public void notifyReshape(int w, int h) {
+ for (ViewPort vp : preViewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ for (ViewPort vp : viewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ for (ViewPort vp : postViewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ }
+
+ /**
+ * Internal use only.
+ * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
+ * based on the current world state.
+ */
+ public void updateUniformBindings(List<Uniform> params) {
+ // assums worldMatrix is properly set.
+ TempVars vars = TempVars.get();
+
+ Matrix4f tempMat4 = vars.tempMat4;
+ Matrix3f tempMat3 = vars.tempMat3;
+ Vector2f tempVec2 = vars.vect2d;
+ Quaternion tempVec4 = vars.quat1;
+
+ for (int i = 0; i < params.size(); i++) {
+ Uniform u = params.get(i);
+ switch (u.getBinding()) {
+ case WorldMatrix:
+ u.setValue(VarType.Matrix4, worldMatrix);
+ break;
+ case ViewMatrix:
+ u.setValue(VarType.Matrix4, viewMatrix);
+ break;
+ case ProjectionMatrix:
+ u.setValue(VarType.Matrix4, projMatrix);
+ break;
+ case ViewProjectionMatrix:
+ u.setValue(VarType.Matrix4, viewProjMatrix);
+ break;
+ case WorldViewMatrix:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case NormalMatrix:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal();
+ tempMat3.transposeLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case WorldViewProjectionMatrix:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.multLocal(worldMatrix);
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldMatrixInverse:
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ProjectionMatrixInverse:
+ tempMat4.set(projMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewProjectionMatrixInverse:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldViewMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case NormalMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal();
+ tempMat3.transposeLocal();
+ tempMat3.invertLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case WorldViewProjectionMatrixInverse:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewPort:
+ tempVec4.set(viewX, viewY, viewWidth, viewHeight);
+ u.setValue(VarType.Vector4, tempVec4);
+ break;
+ case Resolution:
+ tempVec2.set(viewWidth, viewHeight);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case Aspect:
+ float aspect = ((float) viewWidth) / viewHeight;
+ u.setValue(VarType.Float, aspect);
+ break;
+ case FrustumNearFar:
+ tempVec2.set(near, far);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case CameraPosition:
+ u.setValue(VarType.Vector3, camLoc);
+ break;
+ case CameraDirection:
+ u.setValue(VarType.Vector3, camDir);
+ break;
+ case CameraLeft:
+ u.setValue(VarType.Vector3, camLeft);
+ break;
+ case CameraUp:
+ u.setValue(VarType.Vector3, camUp);
+ break;
+ case Time:
+ u.setValue(VarType.Float, timer.getTimeInSeconds());
+ break;
+ case Tpf:
+ u.setValue(VarType.Float, timer.getTimePerFrame());
+ break;
+ case FrameRate:
+ u.setValue(VarType.Float, timer.getFrameRate());
+ break;
+ }
+ }
+
+ vars.release();
+ }
+
+ /**
+ * Set the material to use to render all future objects.
+ * This overrides the material set on the geometry and renders
+ * with the provided material instead.
+ * Use null to clear the material and return renderer to normal
+ * functionality.
+ * @param mat The forced material to set, or null to return to normal
+ */
+ public void setForcedMaterial(Material mat) {
+ forcedMaterial = mat;
+ }
+
+ /**
+ * Returns the forced render state previously set with
+ * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
+ * @return the forced render state
+ */
+ public RenderState getForcedRenderState() {
+ return forcedRenderState;
+ }
+
+ /**
+ * Set the render state to use for all future objects.
+ * This overrides the render state set on the material and instead
+ * forces this render state to be applied for all future materials
+ * rendered. Set to null to return to normal functionality.
+ *
+ * @param forcedRenderState The forced render state to set, or null
+ * to return to normal
+ */
+ public void setForcedRenderState(RenderState forcedRenderState) {
+ this.forcedRenderState = forcedRenderState;
+ }
+
+ /**
+ * Set the timer that should be used to query the time based
+ * {@link UniformBinding}s for material world parameters.
+ *
+ * @param timer The timer to query time world parameters
+ */
+ public void setTimer(Timer timer) {
+ this.timer = timer;
+ }
+
+ /**
+ * Returns the forced technique name set.
+ *
+ * @return the forced technique name set.
+ *
+ * @see #setForcedTechnique(java.lang.String)
+ */
+ public String getForcedTechnique() {
+ return forcedTechnique;
+ }
+
+ /**
+ * Sets the forced technique to use when rendering geometries.
+ * <p>
+ * If the specified technique name is available on the geometry's
+ * material, then it is used, otherwise, the
+ * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
+ * If a forced material is not set and the forced technique name cannot
+ * be found on the material, the geometry will <em>not</em> be rendered.
+ *
+ * @param forcedTechnique The forced technique name to use, set to null
+ * to return to normal functionality.
+ *
+ * @see #renderGeometry(com.jme3.scene.Geometry)
+ */
+ public void setForcedTechnique(String forcedTechnique) {
+ this.forcedTechnique = forcedTechnique;
+ }
+
+ /**
+ * Enable or disable alpha-to-coverage.
+ * <p>
+ * When alpha to coverage is enabled and the renderer implementation
+ * supports it, then alpha blending will be replaced with alpha dissolve
+ * if multi-sampling is also set on the renderer.
+ * This feature allows avoiding of alpha blending artifacts due to
+ * lack of triangle-level back-to-front sorting.
+ *
+ * @param value True to enable alpha-to-coverage, false otherwise.
+ */
+ public void setAlphaToCoverage(boolean value) {
+ renderer.setAlphaToCoverage(value);
+ }
+
+ /**
+ * True if the translucent bucket should automatically be rendered
+ * by the RenderManager.
+ *
+ * @return Whether or not the translucent bucket is rendered.
+ *
+ * @see #setHandleTranslucentBucket(boolean)
+ */
+ public boolean isHandleTranslucentBucket() {
+ return handleTranlucentBucket;
+ }
+
+ /**
+ * Enable or disable rendering of the
+ * {@link Bucket#Translucent translucent bucket}
+ * by the RenderManager. The default is enabled.
+ *
+ * @param handleTranslucentBucket Whether or not the translucent bucket should
+ * be rendered.
+ */
+ public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
+ this.handleTranlucentBucket = handleTranslucentBucket;
+ }
+
+ /**
+ * Internal use only. Sets the world matrix to use for future
+ * rendering. This has no effect unless objects are rendered manually
+ * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
+ * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will
+ * override this value.
+ *
+ * @param mat The world matrix to set
+ */
+ public void setWorldMatrix(Matrix4f mat) {
+ if (shader) {
+ worldMatrix.set(mat);
+ } else {
+ renderer.setWorldMatrix(mat);
+ }
+ }
+
+ /**
+ * Renders the given geometry.
+ * <p>
+ * First the proper world matrix is set, if
+ * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
+ * feature is enabled, the identity world matrix is used, otherwise, the
+ * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.
+ * <p>
+ * Once the world matrix is applied, the proper material is chosen for rendering.
+ * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
+ * set on this RenderManager, then it is used for rendering the geometry,
+ * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
+ * <p>
+ * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
+ * set on this RenderManager, then it is selected automatically
+ * on the geometry's material and is used for rendering. Otherwise, one
+ * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
+ * used.
+ * <p>
+ * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
+ * render state} is set on this RenderManager, then it is used
+ * for rendering the material, and the material's own render state is ignored.
+ * Otherwise, the material's render state is used as intended.
+ *
+ * @param g The geometry to render
+ *
+ * @see Technique
+ * @see RenderState
+ * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+ * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
+ */
+ public void renderGeometry(Geometry g) {
+ if (g.isIgnoreTransform()) {
+ setWorldMatrix(Matrix4f.IDENTITY);
+ } else {
+ setWorldMatrix(g.getWorldMatrix());
+ }
+
+ //if forcedTechnique we try to force it for render,
+ //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
+ //else the geom is not rendered
+ if (forcedTechnique != null) {
+ if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
+ tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
+ g.getMaterial().selectTechnique(forcedTechnique, this);
+ // use geometry's material
+ g.getMaterial().render(g, this);
+ g.getMaterial().selectTechnique(tmpTech, this);
+ //Reverted this part from revision 6197
+ //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
+ } else if (forcedMaterial != null) {
+ // use forced material
+ forcedMaterial.render(g, this);
+ }
+ } else if (forcedMaterial != null) {
+ // use forced material
+ forcedMaterial.render(g, this);
+ } else {
+ g.getMaterial().render(g, this);
+ }
+ }
+
+ /**
+ * Renders the given GeometryList.
+ * <p>
+ * For every geometry in the list, the
+ * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
+ *
+ * @param gl The geometry list to render.
+ *
+ * @see GeometryList
+ * @see #renderGeometry(com.jme3.scene.Geometry)
+ */
+ public void renderGeometryList(GeometryList gl) {
+ for (int i = 0; i < gl.size(); i++) {
+ renderGeometry(gl.get(i));
+ }
+ }
+
+ /**
+ * If a spatial is not inside the eye frustum, it
+ * is still rendered in the shadow frustum (shadow casting queue)
+ * through this recursive method.
+ */
+ private void renderShadow(Spatial s, RenderQueue rq) {
+ if (s instanceof Node) {
+ Node n = (Node) s;
+ List<Spatial> children = n.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ renderShadow(children.get(i), rq);
+ }
+ } else if (s instanceof Geometry) {
+ Geometry gm = (Geometry) s;
+
+ RenderQueue.ShadowMode shadowMode = s.getShadowMode();
+ if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
+ //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
+ rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
+ }
+ }
+ }
+
+ /**
+ * Preloads a scene for rendering.
+ * <p>
+ * After invocation of this method, the underlying
+ * renderer would have uploaded any textures, shaders and meshes
+ * used by the given scene to the video driver.
+ * Using this method is useful when wishing to avoid the initial pause
+ * when rendering a scene for the first time. Note that it is not
+ * guaranteed that the underlying renderer will actually choose to upload
+ * the data to the GPU so some pause is still to be expected.
+ *
+ * @param scene The scene to preload
+ */
+ public void preloadScene(Spatial scene) {
+ if (scene instanceof Node) {
+ // recurse for all children
+ Node n = (Node) scene;
+ List<Spatial> children = n.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ preloadScene(children.get(i));
+ }
+ } else if (scene instanceof Geometry) {
+ // add to the render queue
+ Geometry gm = (Geometry) scene;
+ if (gm.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
+ }
+
+ gm.getMaterial().preload(this);
+ Mesh mesh = gm.getMesh();
+ if (mesh != null) {
+ for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
+ VertexBuffer buf = entry.getValue();
+ if (buf.getData() != null) {
+ renderer.updateBufferData(buf);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Flattens the given scene graph into the ViewPort's RenderQueue,
+ * checking for culling as the call goes down the graph recursively.
+ * <p>
+ * First, the scene is checked for culling based on the <code>Spatial</code>s
+ * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
+ * if the camera frustum contains the scene, then this method is recursively
+ * called on its children.
+ * <p>
+ * When the scene's leaves or {@link Geometry geometries} are reached,
+ * they are each enqueued into the
+ * {@link ViewPort#getQueue() ViewPort's render queue}.
+ * <p>
+ * In addition to enqueuing the visible geometries, this method
+ * also scenes which cast or receive shadows, by putting them into the
+ * RenderQueue's
+ * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
+ * shadow queue}. Each Spatial which has its
+ * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
+ * set to not off, will be put into the appropriate shadow queue, note that
+ * this process does not check for frustum culling on any
+ * {@link ShadowMode#Cast shadow casters}, as they don't have to be
+ * in the eye camera frustum to cast shadows on objects that are inside it.
+ *
+ * @param scene The scene to flatten into the queue
+ * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
+ * used for culling and the {@link ViewPort#getQueue() queue} used to
+ * contain the flattened scene graph.
+ */
+ public void renderScene(Spatial scene, ViewPort vp) {
+ if (scene.getParent() == null) {
+ vp.getCamera().setPlaneState(0);
+ }
+ // check culling first.
+ if (!scene.checkCulling(vp.getCamera())) {
+ // move on to shadow-only render
+ if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
+ renderShadow(scene, vp.getQueue());
+ }
+ return;
+ }
+
+ scene.runControlRender(this, vp);
+ if (scene instanceof Node) {
+ // recurse for all children
+ Node n = (Node) scene;
+ List<Spatial> children = n.getChildren();
+ //saving cam state for culling
+ int camState = vp.getCamera().getPlaneState();
+ for (int i = 0; i < children.size(); i++) {
+ //restoring cam state before proceeding children recusively
+ vp.getCamera().setPlaneState(camState);
+ renderScene(children.get(i), vp);
+
+ }
+ } else if (scene instanceof Geometry) {
+
+ // add to the render queue
+ Geometry gm = (Geometry) scene;
+ if (gm.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
+ }
+
+ vp.getQueue().addToQueue(gm, scene.getQueueBucket());
+
+ // add to shadow queue if needed
+ RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
+ if (shadowMode != RenderQueue.ShadowMode.Off) {
+ vp.getQueue().addToShadowQueue(gm, shadowMode);
+ }
+ }
+ }
+
+ /**
+ * Returns the camera currently used for rendering.
+ * <p>
+ * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
+ *
+ * @return the camera currently used for rendering.
+ */
+ public Camera getCurrentCamera() {
+ return prevCam;
+ }
+
+ /**
+ * The renderer implementation used for rendering operations.
+ *
+ * @return The renderer implementation
+ *
+ * @see #RenderManager(com.jme3.renderer.Renderer)
+ * @see Renderer
+ */
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ /**
+ * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
+ * by rendering each of its visible buckets.
+ * By default the queues will automatically be cleared after rendering,
+ * so there's no need to clear them manually.
+ *
+ * @param vp The ViewPort of which the queue will be flushed
+ *
+ * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
+ * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
+ */
+ public void flushQueue(ViewPort vp) {
+ renderViewPortQueues(vp, true);
+ }
+
+ /**
+ * Clears the queue of the given ViewPort.
+ * Simply calls {@link RenderQueue#clear() } on the ViewPort's
+ * {@link ViewPort#getQueue() render queue}.
+ *
+ * @param vp The ViewPort of which the queue will be cleared.
+ *
+ * @see RenderQueue#clear()
+ * @see ViewPort#getQueue()
+ */
+ public void clearQueue(ViewPort vp) {
+ vp.getQueue().clear();
+ }
+
+ /**
+ * Render the given viewport queues.
+ * <p>
+ * Changes the {@link Renderer#setDepthRange(float, float) depth range}
+ * appropriately as expected by each queue and then calls
+ * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
+ * on the queue. Makes sure to restore the depth range to [0, 1]
+ * at the end of the call.
+ * Note that the {@link Bucket#Translucent translucent bucket} is NOT
+ * rendered by this method. Instead the user should call
+ * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
+ * after this call.
+ *
+ * @param vp the viewport of which queue should be rendered
+ * @param flush If true, the queues will be cleared after
+ * rendering.
+ *
+ * @see RenderQueue
+ * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
+ */
+ public void renderViewPortQueues(ViewPort vp, boolean flush) {
+ RenderQueue rq = vp.getQueue();
+ Camera cam = vp.getCamera();
+ boolean depthRangeChanged = false;
+
+ // render opaque objects with default depth range
+ // opaque objects are sorted front-to-back, reducing overdraw
+ rq.renderQueue(Bucket.Opaque, this, cam, flush);
+
+ // render the sky, with depth range set to the farthest
+ if (!rq.isQueueEmpty(Bucket.Sky)) {
+ renderer.setDepthRange(1, 1);
+ rq.renderQueue(Bucket.Sky, this, cam, flush);
+ depthRangeChanged = true;
+ }
+
+
+ // transparent objects are last because they require blending with the
+ // rest of the scene's objects. Consequently, they are sorted
+ // back-to-front.
+ if (!rq.isQueueEmpty(Bucket.Transparent)) {
+ if (depthRangeChanged) {
+ renderer.setDepthRange(0, 1);
+ depthRangeChanged = false;
+ }
+
+ rq.renderQueue(Bucket.Transparent, this, cam, flush);
+ }
+
+ if (!rq.isQueueEmpty(Bucket.Gui)) {
+ renderer.setDepthRange(0, 0);
+ setCamera(cam, true);
+ rq.renderQueue(Bucket.Gui, this, cam, flush);
+ setCamera(cam, false);
+ depthRangeChanged = true;
+ }
+
+ // restore range to default
+ if (depthRangeChanged) {
+ renderer.setDepthRange(0, 1);
+ }
+ }
+
+ /**
+ * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
+ * <p>
+ * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
+ * is set to true. This method clears the translucent queue after rendering
+ * it.
+ *
+ * @param vp The viewport of which the translucent queue should be rendered.
+ *
+ * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
+ * @see #setHandleTranslucentBucket(boolean)
+ */
+ public void renderTranslucentQueue(ViewPort vp) {
+ RenderQueue rq = vp.getQueue();
+ if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
+ rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
+ }
+ }
+
+ private void setViewPort(Camera cam) {
+ // this will make sure to update viewport only if needed
+ if (cam != prevCam || cam.isViewportChanged()) {
+ viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
+ viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
+ viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
+ viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
+ renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
+ renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
+ cam.clearViewportChanged();
+ prevCam = cam;
+
+// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
+// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
+// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
+// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
+//
+// orthoMatrix.loadIdentity();
+// orthoMatrix.setTranslation(translateX, translateY, 0);
+// orthoMatrix.setScale(scaleX, scaleY, 0);
+
+ orthoMatrix.loadIdentity();
+ orthoMatrix.setTranslation(-1f, -1f, 0f);
+ orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
+ }
+ }
+
+ private void setViewProjection(Camera cam, boolean ortho) {
+ if (shader) {
+ if (ortho) {
+ viewMatrix.set(Matrix4f.IDENTITY);
+ projMatrix.set(orthoMatrix);
+ viewProjMatrix.set(orthoMatrix);
+ } else {
+ viewMatrix.set(cam.getViewMatrix());
+ projMatrix.set(cam.getProjectionMatrix());
+ viewProjMatrix.set(cam.getViewProjectionMatrix());
+ }
+
+ camLoc.set(cam.getLocation());
+ cam.getLeft(camLeft);
+ cam.getUp(camUp);
+ cam.getDirection(camDir);
+
+ near = cam.getFrustumNear();
+ far = cam.getFrustumFar();
+ } else {
+ if (ortho) {
+ renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
+ } else {
+ renderer.setViewProjectionMatrices(cam.getViewMatrix(),
+ cam.getProjectionMatrix());
+ }
+
+ }
+ }
+
+ /**
+ * Set the camera to use for rendering.
+ * <p>
+ * First, the camera's
+ * {@link Camera#setViewPort(float, float, float, float) view port parameters}
+ * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
+ * {@link Camera#getProjectionMatrix() projection} matrices are set
+ * on the renderer. If <code>ortho</code> is <code>true</code>, then
+ * instead of using the camera's view and projection matrices, an ortho
+ * matrix is computed and used instead of the view projection matrix.
+ * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
+ * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
+ *
+ * @param cam The camera to set
+ * @param ortho True if to use orthographic projection (for GUI rendering),
+ * false if to use the camera's view and projection matrices.
+ */
+ public void setCamera(Camera cam, boolean ortho) {
+ setViewPort(cam);
+ setViewProjection(cam, ortho);
+ }
+
+ /**
+ * Draws the viewport but without notifying {@link SceneProcessor scene
+ * processors} of any rendering events.
+ *
+ * @param vp The ViewPort to render
+ *
+ * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
+ */
+ public void renderViewPortRaw(ViewPort vp) {
+ setCamera(vp.getCamera(), false);
+ List<Spatial> scenes = vp.getScenes();
+ for (int i = scenes.size() - 1; i >= 0; i--) {
+ renderScene(scenes.get(i), vp);
+ }
+ flushQueue(vp);
+ }
+
+ /**
+ * Renders the {@link ViewPort}.
+ * <p>
+ * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
+ * returns immediately. Otherwise, the ViewPort is rendered by
+ * the following process:<br>
+ * <ul>
+ * <li>All {@link SceneProcessor scene processors} that are attached
+ * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
+ * </li>
+ * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
+ * is called.</li>
+ * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
+ * is set on the Renderer</li>
+ * <li>The camera is set on the renderer, including its view port parameters.
+ * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
+ * <li>Any buffers that the ViewPort requests to be cleared are cleared
+ * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
+ * <li>Every scene that is attached to the ViewPort is flattened into
+ * the ViewPort's render queue
+ * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
+ * </li>
+ * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
+ * method is called.</li>
+ * <li>The render queue is sorted and then flushed, sending
+ * rendering commands to the underlying Renderer implementation.
+ * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
+ * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
+ * method is called.</li>
+ * <li>The translucent queue of the ViewPort is sorted and then flushed
+ * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
+ * <li>If any objects remained in the render queue, they are removed
+ * from the queue. This is generally objects added to the
+ * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
+ * shadow queue}
+ * which were not rendered because of a missing shadow renderer.</li>
+ * </ul>
+ *
+ * @param vp
+ * @param tpf
+ */
+ public void renderViewPort(ViewPort vp, float tpf) {
+ if (!vp.isEnabled()) {
+ return;
+ }
+ List<SceneProcessor> processors = vp.getProcessors();
+ if (processors.isEmpty()) {
+ processors = null;
+ }
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ if (!proc.isInitialized()) {
+ proc.initialize(this, vp);
+ }
+ proc.preFrame(tpf);
+ }
+ }
+
+ renderer.setFrameBuffer(vp.getOutputFrameBuffer());
+ setCamera(vp.getCamera(), false);
+ if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
+ if (vp.isClearColor()) {
+ renderer.setBackgroundColor(vp.getBackgroundColor());
+ }
+ renderer.clearBuffers(vp.isClearColor(),
+ vp.isClearDepth(),
+ vp.isClearStencil());
+ }
+
+ List<Spatial> scenes = vp.getScenes();
+ for (int i = scenes.size() - 1; i >= 0; i--) {
+ renderScene(scenes.get(i), vp);
+ }
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ proc.postQueue(vp.getQueue());
+ }
+ }
+
+ flushQueue(vp);
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ proc.postFrame(vp.getOutputFrameBuffer());
+ }
+ }
+ //renders the translucent objects queue after processors have been rendered
+ renderTranslucentQueue(vp);
+ // clear any remaining spatials that were not rendered.
+ clearQueue(vp);
+ }
+
+ /**
+ * Called by the application to render any ViewPorts
+ * added to this RenderManager.
+ * <p>
+ * Renders any viewports that were added using the following methods:
+ * <ul>
+ * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * </ul>
+ *
+ * @param tpf Time per frame value
+ */
+ public void render(float tpf, boolean mainFrameBufferActive) {
+ if (renderer instanceof NullRenderer) {
+ return;
+ }
+
+ this.shader = renderer.getCaps().contains(Caps.GLSL100);
+
+ for (int i = 0; i < preViewPorts.size(); i++) {
+ ViewPort vp = preViewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ for (int i = 0; i < viewPorts.size(); i++) {
+ ViewPort vp = viewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ ViewPort vp = postViewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/Renderer.java b/engine/src/core/com/jme3/renderer/Renderer.java
new file mode 100644
index 0000000..6e366ab
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Renderer.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.renderer;
+
+import com.jme3.light.LightList;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import java.nio.ByteBuffer;
+import java.util.EnumSet;
+
+/**
+ * The <code>Renderer</code> is responsible for taking rendering commands and
+ * executing them on the underlying video hardware.
+ *
+ * @author Kirill Vainer
+ */
+public interface Renderer {
+
+ /**
+ * Get the capabilities of the renderer.
+ * @return The capabilities of the renderer.
+ */
+ public EnumSet<Caps> getCaps();
+
+ /**
+ * The statistics allow tracking of how data
+ * per frame, such as number of objects rendered, number of triangles, etc.
+ * These are updated when the Renderer's methods are used, make sure
+ * to call {@link Statistics#clearFrame() } at the appropriate time
+ * to get accurate info per frame.
+ */
+ public Statistics getStatistics();
+
+ /**
+ * Invalidates the current rendering state. Should be called after
+ * the GL state was changed manually or through an external library.
+ */
+ public void invalidateState();
+
+ /**
+ * Clears certain channels of the currently bound framebuffer.
+ *
+ * @param color True if to clear colors (RGBA)
+ * @param depth True if to clear depth/z
+ * @param stencil True if to clear stencil buffer (if available, otherwise
+ * ignored)
+ */
+ public void clearBuffers(boolean color, boolean depth, boolean stencil);
+
+ /**
+ * Sets the background (aka clear) color.
+ *
+ * @param color The background color to set
+ */
+ public void setBackgroundColor(ColorRGBA color);
+
+ /**
+ * Applies the given {@link RenderState}, making the necessary
+ * GL calls so that the state is applied.
+ */
+ public void applyRenderState(RenderState state);
+
+ /**
+ * Set the range of the depth values for objects. All rendered
+ * objects will have their depth clamped to this range.
+ *
+ * @param start The range start
+ * @param end The range end
+ */
+ public void setDepthRange(float start, float end);
+
+ /**
+ * Called when a new frame has been rendered.
+ */
+ public void onFrame();
+
+ /**
+ * Set the world matrix to use. Does nothing if the Renderer is
+ * shader based.
+ *
+ * @param worldMatrix World matrix to use.
+ */
+ public void setWorldMatrix(Matrix4f worldMatrix);
+
+ /**
+ * Sets the view and projection matrices to use. Does nothing if the Renderer
+ * is shader based.
+ *
+ * @param viewMatrix The view matrix to use.
+ * @param projMatrix The projection matrix to use.
+ */
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix);
+
+ /**
+ * Set the viewport location and resolution on the screen.
+ *
+ * @param x The x coordinate of the viewport
+ * @param y The y coordinate of the viewport
+ * @param width Width of the viewport
+ * @param height Height of the viewport
+ */
+ public void setViewPort(int x, int y, int width, int height);
+
+ /**
+ * Specifies a clipping rectangle.
+ * For all future rendering commands, no pixels will be allowed
+ * to be rendered outside of the clip rectangle.
+ *
+ * @param x The x coordinate of the clip rect
+ * @param y The y coordinate of the clip rect
+ * @param width Width of the clip rect
+ * @param height Height of the clip rect
+ */
+ public void setClipRect(int x, int y, int width, int height);
+
+ /**
+ * Clears the clipping rectangle set with
+ * {@link #setClipRect(int, int, int, int) }.
+ */
+ public void clearClipRect();
+
+ /**
+ * Set lighting state.
+ * Does nothing if the renderer is shader based.
+ * The lights should be provided in world space.
+ * Specify <code>null</code> to disable lighting.
+ *
+ * @param lights The light list to set.
+ */
+ public void setLighting(LightList lights);
+
+ /**
+ * Sets the shader to use for rendering.
+ * If the shader has not been uploaded yet, it is compiled
+ * and linked. If it has been uploaded, then the
+ * uniform data is updated and the shader is set.
+ *
+ * @param shader The shader to use for rendering.
+ */
+ public void setShader(Shader shader);
+
+ /**
+ * Deletes a shader. This method also deletes
+ * the attached shader sources.
+ *
+ * @param shader Shader to delete.
+ */
+ public void deleteShader(Shader shader);
+
+ /**
+ * Deletes the provided shader source.
+ *
+ * @param source The ShaderSource to delete.
+ */
+ public void deleteShaderSource(ShaderSource source);
+
+ /**
+ * Copies contents from src to dst, scaling if necessary.
+ */
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst);
+
+ /**
+ * Copies contents from src to dst, scaling if necessary.
+ * set copyDepth to false to only copy the color buffers.
+ */
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth);
+
+ /**
+ * Sets the framebuffer that will be drawn to.
+ */
+ public void setFrameBuffer(FrameBuffer fb);
+
+ /**
+ * Set the framebuffer that will be set instead of the main framebuffer
+ * when a call to setFrameBuffer(null) is made.
+ *
+ * @param fb
+ */
+ public void setMainFrameBufferOverride(FrameBuffer fb);
+
+ /**
+ * Reads the pixels currently stored in the specified framebuffer
+ * into the given ByteBuffer object.
+ * Only color pixels are transferred, the format is BGRA with 8 bits
+ * per component. The given byte buffer should have at least
+ * fb.getWidth() * fb.getHeight() * 4 bytes remaining.
+ *
+ * @param fb The framebuffer to read from
+ * @param byteBuf The bytebuffer to transfer color data to
+ */
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf);
+
+ /**
+ * Deletes a framebuffer and all attached renderbuffers
+ */
+ public void deleteFrameBuffer(FrameBuffer fb);
+
+ /**
+ * Sets the texture to use for the given texture unit.
+ */
+ public void setTexture(int unit, Texture tex);
+
+ /**
+ * Deletes a texture from the GPU.
+ */
+ public void deleteImage(Image image);
+
+ /**
+ * Uploads a vertex buffer to the GPU.
+ *
+ * @param vb The vertex buffer to upload
+ */
+ public void updateBufferData(VertexBuffer vb);
+
+ /**
+ * Deletes a vertex buffer from the GPU.
+ * @param vb The vertex buffer to delete
+ */
+ public void deleteBuffer(VertexBuffer vb);
+
+ /**
+ * Renders <code>count</code> meshes, with the geometry data supplied.
+ * The shader which is currently set with <code>setShader</code> is
+ * responsible for transforming the input verticies into clip space
+ * and shading it based on the given vertex attributes.
+ * The int variable gl_InstanceID can be used to access the current
+ * instance of the mesh being rendered inside the vertex shader.
+ *
+ * @param mesh The mesh to render
+ * @param lod The LOD level to use, see {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
+ * @param count Number of mesh instances to render
+ */
+ public void renderMesh(Mesh mesh, int lod, int count);
+
+ /**
+ * Resets all previously used {@link GLObject}s on this Renderer.
+ * The state of the GLObjects is reset in such way, that using
+ * them again will cause the renderer to reupload them.
+ * Call this method when you know the GL context is going to shutdown.
+ *
+ * @see GLObject#resetObject()
+ */
+ public void resetGLObjects();
+
+ /**
+ * Deletes all previously used {@link GLObject}s on this Renderer, and
+ * then resets the GLObjects.
+ *
+ * @see #resetGLObjects()
+ * @see GLObject#deleteObject(com.jme3.renderer.Renderer)
+ */
+ public void cleanup();
+
+ /**
+ * Sets the alpha to coverage state.
+ * <p>
+ * When alpha coverage and multi-sampling is enabled,
+ * each pixel will contain alpha coverage in all
+ * of its subsamples, which is then combined when
+ * other future alpha-blended objects are rendered.
+ * </p>
+ * <p>
+ * Alpha-to-coverage is useful for rendering transparent objects
+ * without having to worry about sorting them.
+ * </p>
+ */
+ public void setAlphaToCoverage(boolean value);
+}
diff --git a/engine/src/core/com/jme3/renderer/RendererException.java b/engine/src/core/com/jme3/renderer/RendererException.java
new file mode 100644
index 0000000..41964a2
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/RendererException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer;
+
+/**
+ * <code>RendererException</code> is raised when a renderer encounters
+ * a fatal rendering error.
+ *
+ * @author Kirill Vainer
+ */
+public class RendererException extends RuntimeException {
+
+ /**
+ * Creates a new instance of <code>RendererException</code>
+ */
+ public RendererException(String message){
+ super(message);
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/Statistics.java b/engine/src/core/com/jme3/renderer/Statistics.java
new file mode 100644
index 0000000..fb8f2a0
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Statistics.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer;
+
+import com.jme3.scene.Mesh;
+import com.jme3.shader.Shader;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import java.util.HashSet;
+
+/**
+ * The statistics class allows tracking of real-time rendering statistics.
+ * <p>
+ * The <code>Statistics</code> can be retrieved by using {@link Renderer#getStatistics() }.
+ *
+ * @author Kirill Vainer
+ */
+public class Statistics {
+
+ protected int numObjects;
+ protected int numTriangles;
+ protected int numVertices;
+ protected int numShaderSwitches;
+ protected int numTextureBinds;
+ protected int numFboSwitches;
+ protected int numUniformsSet;
+
+ protected int memoryShaders;
+ protected int memoryFrameBuffers;
+ protected int memoryTextures;
+
+ protected HashSet<Integer> shadersUsed = new HashSet<Integer>();
+ protected HashSet<Integer> texturesUsed = new HashSet<Integer>();
+ protected HashSet<Integer> fbosUsed = new HashSet<Integer>();
+
+ /**
+ * Returns a list of labels corresponding to each statistic.
+ *
+ * @return a list of labels corresponding to each statistic.
+ *
+ * @see #getData(int[])
+ */
+ public String[] getLabels(){
+ return new String[]{ "Vertices",
+ "Triangles",
+ "Uniforms",
+
+ "Objects",
+
+ "Shaders (S)",
+ "Shaders (F)",
+ "Shaders (M)",
+
+ "Textures (S)",
+ "Textures (F)",
+ "Textures (M)",
+
+ "FrameBuffers (S)",
+ "FrameBuffers (F)",
+ "FrameBuffers (M)" };
+
+ }
+
+ /**
+ * Retrieves the statistics data into the given array.
+ * The array should be as large as the array given in
+ * {@link #getLabels() }.
+ *
+ * @param data The data array to write to
+ */
+ public void getData(int[] data){
+ data[0] = numVertices;
+ data[1] = numTriangles;
+ data[2] = numUniformsSet;
+ data[3] = numObjects;
+
+ data[4] = numShaderSwitches;
+ data[5] = shadersUsed.size();
+ data[6] = memoryShaders;
+
+ data[7] = numTextureBinds;
+ data[8] = texturesUsed.size();
+ data[9] = memoryTextures;
+
+ data[10] = numFboSwitches;
+ data[11] = fbosUsed.size();
+ data[12] = memoryFrameBuffers;
+ }
+
+ /**
+ * Called by the Renderer when a mesh has been drawn.
+ *
+ */
+ public void onMeshDrawn(Mesh mesh, int lod){
+ numObjects ++;
+ numTriangles += mesh.getTriangleCount(lod);
+ numVertices += mesh.getVertexCount();
+ }
+
+ /**
+ * Called by the Renderer when a shader has been utilized.
+ *
+ * @param shader The shader that was used
+ * @param wasSwitched If true, the shader has required a state switch
+ */
+ public void onShaderUse(Shader shader, boolean wasSwitched){
+ assert shader.getId() >= 1;
+
+ if (!shadersUsed.contains(shader.getId()))
+ shadersUsed.add(shader.getId());
+
+ if (wasSwitched)
+ numShaderSwitches++;
+ }
+
+ /**
+ * Called by the Renderer when a uniform was set.
+ */
+ public void onUniformSet(){
+ numUniformsSet ++;
+ }
+
+ /**
+ * Called by the Renderer when a texture has been set.
+ *
+ * @param image The image that was set
+ * @param wasSwitched If true, the texture has required a state switch
+ */
+ public void onTextureUse(Image image, boolean wasSwitched){
+ assert image.getId() >= 1;
+
+ if (!texturesUsed.contains(image.getId()))
+ texturesUsed.add(image.getId());
+
+ if (wasSwitched)
+ numTextureBinds ++;
+ }
+
+ /**
+ * Called by the Renderer when a framebuffer has been set.
+ *
+ * @param fb The framebuffer that was set
+ * @param wasSwitched If true, the framebuffer required a state switch
+ */
+ public void onFrameBufferUse(FrameBuffer fb, boolean wasSwitched){
+ if (fb != null){
+ assert fb.getId() >= 1;
+
+ if (!fbosUsed.contains(fb.getId()))
+ fbosUsed.add(fb.getId());
+ }
+
+ if (wasSwitched)
+ numFboSwitches ++;
+ }
+
+ /**
+ * Clears all frame-specific statistics such as objects used per frame.
+ */
+ public void clearFrame(){
+ shadersUsed.clear();
+ texturesUsed.clear();
+ fbosUsed.clear();
+
+ numObjects = 0;
+ numTriangles = 0;
+ numVertices = 0;
+ numShaderSwitches = 0;
+ numTextureBinds = 0;
+ numFboSwitches = 0;
+ numUniformsSet = 0;
+ }
+
+ /**
+ * Called by the Renderer when it creates a new shader
+ */
+ public void onNewShader(){
+ memoryShaders ++;
+ }
+
+ /**
+ * Called by the Renderer when it creates a new texture
+ */
+ public void onNewTexture(){
+ memoryTextures ++;
+ }
+
+ /**
+ * Called by the Renderer when it creates a new framebuffer
+ */
+ public void onNewFrameBuffer(){
+ memoryFrameBuffers ++;
+ }
+
+ /**
+ * Called by the Renderer when it deletes a shader
+ */
+ public void onDeleteShader(){
+ memoryShaders --;
+ }
+
+ /**
+ * Called by the Renderer when it deletes a texture
+ */
+ public void onDeleteTexture(){
+ memoryTextures --;
+ }
+
+ /**
+ * Called by the Renderer when it deletes a framebuffer
+ */
+ public void onDeleteFrameBuffer(){
+ memoryFrameBuffers --;
+ }
+
+ /**
+ * Called when video memory is cleared.
+ */
+ public void clearMemory(){
+ memoryFrameBuffers = 0;
+ memoryShaders = 0;
+ memoryTextures = 0;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/ViewPort.java b/engine/src/core/com/jme3/renderer/ViewPort.java
new file mode 100644
index 0000000..ad9ab2f
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/ViewPort.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.FrameBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A <code>ViewPort</code> represents a view inside the display
+ * window or a {@link FrameBuffer} to which scenes will be rendered.
+ * <p>
+ * A viewport has a {@link #ViewPort(java.lang.String, com.jme3.renderer.Camera) camera}
+ * which is used to render a set of {@link #attachScene(com.jme3.scene.Spatial) scenes}.
+ * A view port has a location on the screen as set by the
+ * {@link Camera#setViewPort(float, float, float, float) } method.
+ * By default, a view port does not clear the framebuffer, but it can be
+ * set to {@link #setClearFlags(boolean, boolean, boolean) clear the framebuffer}.
+ * The background color which the color buffer is cleared to can be specified
+ * via the {@link #setBackgroundColor(com.jme3.math.ColorRGBA)} method.
+ * <p>
+ * A ViewPort has a list of {@link SceneProcessor}s which can
+ * control how the ViewPort is rendered by the {@link RenderManager}.
+ *
+ * @author Kirill Vainer
+ *
+ * @see RenderManager
+ * @see SceneProcessor
+ * @see Spatial
+ * @see Camera
+ */
+public class ViewPort {
+
+ protected final String name;
+ protected final Camera cam;
+ protected final RenderQueue queue = new RenderQueue();
+ protected final ArrayList<Spatial> sceneList = new ArrayList<Spatial>();
+ protected final ArrayList<SceneProcessor> processors = new ArrayList<SceneProcessor>();
+ protected FrameBuffer out = null;
+
+ protected final ColorRGBA backColor = new ColorRGBA(0,0,0,0);
+ protected boolean clearDepth = false, clearColor = false, clearStencil = false;
+ private boolean enabled = true;
+
+ /**
+ * Create a new viewport. User code should generally use these methods instead:<br>
+ * <ul>
+ * <li>{@link RenderManager#createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link RenderManager#createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link RenderManager#createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * </ul>
+ *
+ * @param name The name of the viewport. Used for debugging only.
+ * @param cam The camera through which the viewport is rendered. The camera
+ * cannot be swapped to a different one after creating the viewport.
+ */
+ public ViewPort(String name, Camera cam) {
+ this.name = name;
+ this.cam = cam;
+ }
+
+ /**
+ * Returns the name of the viewport as set in the constructor.
+ *
+ * @return the name of the viewport
+ *
+ * @see #ViewPort(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the list of {@link SceneProcessor scene processors} that were
+ * added to this <code>ViewPort</code>
+ *
+ * @return the list of processors attached to this ViewPort
+ *
+ * @see #addProcessor(com.jme3.post.SceneProcessor)
+ */
+ public List<SceneProcessor> getProcessors(){
+ return processors;
+ }
+
+ /**
+ * Adds a {@link SceneProcessor} to this ViewPort.
+ * <p>
+ * SceneProcessors that are added to the ViewPort will be notified
+ * of events as the ViewPort is being rendered by the {@link RenderManager}.
+ *
+ * @param processor The processor to add
+ *
+ * @see SceneProcessor
+ */
+ public void addProcessor(SceneProcessor processor){
+ processors.add(processor);
+ }
+
+ /**
+ * Removes a {@link SceneProcessor} from this ViewPort.
+ * <p>
+ * The processor will no longer receive events occurring to this ViewPort.
+ *
+ * @param processor The processor to remove
+ *
+ * @see SceneProcessor
+ */
+ public void removeProcessor(SceneProcessor processor){
+ processors.remove(processor);
+ processor.cleanup();
+ }
+
+ /**
+ * Check if depth buffer clearing is enabled.
+ *
+ * @return true if depth buffer clearing is enabled.
+ *
+ * @see #setClearDepth(boolean)
+ */
+ public boolean isClearDepth() {
+ return clearDepth;
+ }
+
+ /**
+ * Enable or disable clearing of the depth buffer for this ViewPort.
+ * <p>
+ * By default depth clearing is disabled.
+ *
+ * @param clearDepth Enable/disable depth buffer clearing.
+ */
+ public void setClearDepth(boolean clearDepth) {
+ this.clearDepth = clearDepth;
+ }
+
+ /**
+ * Check if color buffer clearing is enabled.
+ *
+ * @return true if color buffer clearing is enabled.
+ *
+ * @see #setClearColor(boolean)
+ */
+ public boolean isClearColor() {
+ return clearColor;
+ }
+
+ /**
+ * Enable or disable clearing of the color buffer for this ViewPort.
+ * <p>
+ * By default color clearing is disabled.
+ *
+ * @param clearColor Enable/disable color buffer clearing.
+ */
+ public void setClearColor(boolean clearColor) {
+ this.clearColor = clearColor;
+ }
+
+ /**
+ * Check if stencil buffer clearing is enabled.
+ *
+ * @return true if stencil buffer clearing is enabled.
+ *
+ * @see #setClearStencil(boolean)
+ */
+ public boolean isClearStencil() {
+ return clearStencil;
+ }
+
+ /**
+ * Enable or disable clearing of the stencil buffer for this ViewPort.
+ * <p>
+ * By default stencil clearing is disabled.
+ *
+ * @param clearStencil Enable/disable stencil buffer clearing.
+ */
+ public void setClearStencil(boolean clearStencil) {
+ this.clearStencil = clearStencil;
+ }
+
+ /**
+ * Set the clear flags (color, depth, stencil) in one call.
+ *
+ * @param color If color buffer clearing should be enabled.
+ * @param depth If depth buffer clearing should be enabled.
+ * @param stencil If stencil buffer clearing should be enabled.
+ *
+ * @see #setClearColor(boolean)
+ * @see #setClearDepth(boolean)
+ * @see #setClearStencil(boolean)
+ */
+ public void setClearFlags(boolean color, boolean depth, boolean stencil){
+ this.clearColor = color;
+ this.clearDepth = depth;
+ this.clearStencil = stencil;
+ }
+
+ /**
+ * Returns the framebuffer where this ViewPort's scenes are
+ * rendered to.
+ *
+ * @return the framebuffer where this ViewPort's scenes are
+ * rendered to.
+ *
+ * @see #setOutputFrameBuffer(com.jme3.texture.FrameBuffer)
+ */
+ public FrameBuffer getOutputFrameBuffer() {
+ return out;
+ }
+
+ /**
+ * Sets the output framebuffer for the ViewPort.
+ * <p>
+ * The output framebuffer specifies where the scenes attached
+ * to this ViewPort are rendered to. By default this is <code>null</code>
+ * which indicates the scenes are rendered to the display window.
+ *
+ * @param out The framebuffer to render scenes to, or null if to render
+ * to the screen.
+ */
+ public void setOutputFrameBuffer(FrameBuffer out) {
+ this.out = out;
+ }
+
+ /**
+ * Returns the camera which renders the attached scenes.
+ *
+ * @return the camera which renders the attached scenes.
+ *
+ * @see Camera
+ */
+ public Camera getCamera() {
+ return cam;
+ }
+
+ /**
+ * Internal use only.
+ */
+ public RenderQueue getQueue() {
+ return queue;
+ }
+
+ /**
+ * Attaches a new scene to render in this ViewPort.
+ *
+ * @param scene The scene to attach
+ *
+ * @see Spatial
+ */
+ public void attachScene(Spatial scene){
+ sceneList.add(scene);
+ }
+
+ /**
+ * Detaches a scene from rendering.
+ *
+ * @param scene The scene to detach
+ *
+ * @see #attachScene(com.jme3.scene.Spatial)
+ */
+ public void detachScene(Spatial scene){
+ sceneList.remove(scene);
+ }
+
+ /**
+ * Removes all attached scenes.
+ *
+ * @see #attachScene(com.jme3.scene.Spatial)
+ */
+ public void clearScenes() {
+ sceneList.clear();
+ }
+
+ /**
+ * Returns a list of all attached scenes.
+ *
+ * @return a list of all attached scenes.
+ *
+ * @see #attachScene(com.jme3.scene.Spatial)
+ */
+ public List<Spatial> getScenes(){
+ return sceneList;
+ }
+
+ /**
+ * Sets the background color.
+ * <p>
+ * When the ViewPort's color buffer is cleared
+ * (if {@link #setClearColor(boolean) color clearing} is enabled),
+ * this specifies the color to which the color buffer is set to.
+ * By default the background color is black without alpha.
+ *
+ * @param background the background color.
+ */
+ public void setBackgroundColor(ColorRGBA background){
+ backColor.set(background);
+ }
+
+ /**
+ * Returns the background color of this ViewPort
+ *
+ * @return the background color of this ViewPort
+ *
+ * @see #setBackgroundColor(com.jme3.math.ColorRGBA)
+ */
+ public ColorRGBA getBackgroundColor(){
+ return backColor;
+ }
+
+ /**
+ * Enable or disable this ViewPort.
+ * <p>
+ * Disabled ViewPorts are skipped by the {@link RenderManager} when
+ * rendering. By default all ViewPorts are enabled.
+ *
+ * @param enable If the viewport should be disabled or enabled.
+ */
+ public void setEnabled(boolean enable) {
+ this.enabled = enable;
+ }
+
+ /**
+ * Returns true if the viewport is enabled, false otherwise.
+ * @return true if the viewport is enabled, false otherwise.
+ * @see #setEnabled(boolean)
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/package.html b/engine/src/core/com/jme3/renderer/package.html
new file mode 100644
index 0000000..9d80ec8
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/package.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.renderer</code> package provides classes responsible for
+rendering.
+<p>
+The most critical classes are the {@link com.jme3.renderer.Renderer},
+which is the low-level rendering implementation and is abstract, and the
+{@link com.jme3.renderer.RenderManager} class, which provides the high-level
+rendering logic on top of the <code>Renderer</code>.
+<p>
+To accompany rendering, several helper classes are available.
+<ul>
+ <li>The {@link com.jme3.renderer.Camera} is used to specify the point-of-view
+ from which scenes are rendered.</li>
+ <li>The {@link com.jme3.renderer.ViewPort} is the
+aggregation of a Camera and a set of {@link com.jme3.scene.Spatial scenes}
+which are to be rendered, as well as additional info.</li>
+ <li>The {@link com.jme3.renderer.Caps} class contains renderer capabilities
+which the user can query to find out what features are available in the
+rendering implementation. </li>
+ <li>The {@link com.jme3.renderer.Statistics} class is updated in real time
+ by the Renderer, and is used to find out various statistics about
+ the rendering</li>
+ <li>The {@link com.jme3.renderer.GLObjectManager} and {@link com.jme3.renderer.GLObject} classes
+ provide a link between the renderer's native objects and Java's garbage collected objects,
+ allowing the engine to track when the Java object counterpart is garbage collected
+ and then delete the native object counterpart from the renderer.</li>
+</ul>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/renderer/queue/GeometryComparator.java b/engine/src/core/com/jme3/renderer/queue/GeometryComparator.java
new file mode 100644
index 0000000..8f42df7
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/GeometryComparator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import java.util.Comparator;
+
+/**
+ * <code>GeometryComparator</code> is a special version of {@link Comparator}
+ * that is used to sort geometries for rendering in the {@link RenderQueue}.
+ *
+ * @author Kirill Vainer
+ */
+public interface GeometryComparator extends Comparator<Geometry> {
+
+ /**
+ * Set the camera to use for sorting.
+ *
+ * @param cam The camera to use for sorting
+ */
+ public void setCamera(Camera cam);
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/GeometryList.java b/engine/src/core/com/jme3/renderer/queue/GeometryList.java
new file mode 100644
index 0000000..9f36ed9
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/GeometryList.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import com.jme3.util.SortUtil;
+
+/**
+ * This class is a special purpose list of {@link Geometry} objects for render
+ * queuing.
+ *
+ * @author Jack Lindamood
+ * @author Three Rings - better sorting alg.
+ * @author Kirill Vainer
+ */
+public class GeometryList {
+
+ private static final int DEFAULT_SIZE = 32;
+
+ private Geometry[] geometries;
+ private Geometry[] geometries2;
+ private int size;
+ private GeometryComparator comparator;
+
+ /**
+ * Initializes the GeometryList to use the given {@link GeometryComparator}
+ * to use for comparing geometries.
+ *
+ * @param comparator The comparator to use.
+ */
+ public GeometryList(GeometryComparator comparator) {
+ size = 0;
+ geometries = new Geometry[DEFAULT_SIZE];
+ geometries2 = new Geometry[DEFAULT_SIZE];
+ this.comparator = comparator;
+ }
+
+ /**
+ * Set the camera that will be set on the geometry comparators
+ * via {@link GeometryComparator#setCamera(com.jme3.renderer.Camera)}.
+ *
+ * @param cam Camera to use for sorting.
+ */
+ public void setCamera(Camera cam){
+ this.comparator.setCamera(cam);
+ }
+
+ /**
+ * Returns the number of elements in this GeometryList.
+ *
+ * @return Number of elements in the list
+ */
+ public int size(){
+ return size;
+ }
+
+ /**
+ * Returns the element at the given index.
+ *
+ * @param index The index to lookup
+ * @return Geometry at the index
+ */
+ public Geometry get(int index){
+ return geometries[index];
+ }
+
+ /**
+ * Adds a geometry to the list.
+ * List size is doubled if there is no room.
+ *
+ * @param g
+ * The geometry to add.
+ */
+ public void add(Geometry g) {
+ if (size == geometries.length) {
+ Geometry[] temp = new Geometry[size * 2];
+ System.arraycopy(geometries, 0, temp, 0, size);
+ geometries = temp; // original list replaced by double-size list
+
+ geometries2 = new Geometry[size * 2];
+ }
+ geometries[size++] = g;
+ }
+
+ /**
+ * Resets list size to 0.
+ */
+ public void clear() {
+ for (int i = 0; i < size; i++){
+ geometries[i] = null;
+ }
+
+ size = 0;
+ }
+
+ /**
+ * Sorts the elements in the list according to their Comparator.
+ */
+ public void sort() {
+ if (size > 1) {
+ // sort the spatial list using the comparator
+
+// SortUtil.qsort(geometries, 0, size, comparator);
+// Arrays.sort(geometries, 0, size, comparator);
+
+ System.arraycopy(geometries, 0, geometries2, 0, size);
+ SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
+
+
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/renderer/queue/GuiComparator.java b/engine/src/core/com/jme3/renderer/queue/GuiComparator.java
new file mode 100644
index 0000000..b35a03c
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/GuiComparator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+/**
+ * <code>GuiComparator</code> sorts geometries back-to-front based
+ * on their Z position.
+ *
+ * @author Kirill Vainer
+ */
+public class GuiComparator implements GeometryComparator {
+
+ public int compare(Geometry o1, Geometry o2) {
+ float z1 = o1.getWorldTranslation().getZ();
+ float z2 = o2.getWorldTranslation().getZ();
+ if (z1 > z2)
+ return 1;
+ else if (z1 < z2)
+ return -1;
+ else
+ return 0;
+ }
+
+ public void setCamera(Camera cam) {
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/NullComparator.java b/engine/src/core/com/jme3/renderer/queue/NullComparator.java
new file mode 100644
index 0000000..6463558
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/NullComparator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+/**
+ * <code>NullComparator</code> does not sort geometries. They will be in
+ * arbitrary order.
+ *
+ * @author Kirill Vainer
+ */
+public class NullComparator implements GeometryComparator {
+ public int compare(Geometry o1, Geometry o2) {
+ return 0;
+ }
+
+ public void setCamera(Camera cam) {
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java b/engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java
new file mode 100644
index 0000000..85f4093
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.queue;
+
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+public class OpaqueComparator implements GeometryComparator {
+
+ private Camera cam;
+ private final Vector3f tempVec = new Vector3f();
+ private final Vector3f tempVec2 = new Vector3f();
+
+ public void setCamera(Camera cam){
+ this.cam = cam;
+ }
+
+ public float distanceToCam(Geometry spat){
+ if (spat == null)
+ return Float.NEGATIVE_INFINITY;
+
+ if (spat.queueDistance != Float.NEGATIVE_INFINITY)
+ return spat.queueDistance;
+
+ Vector3f camPosition = cam.getLocation();
+ Vector3f viewVector = cam.getDirection(tempVec2);
+ Vector3f spatPosition = null;
+
+ if (spat.getWorldBound() != null){
+ spatPosition = spat.getWorldBound().getCenter();
+ }else{
+ spatPosition = spat.getWorldTranslation();
+ }
+
+ spatPosition.subtract(camPosition, tempVec);
+ spat.queueDistance = tempVec.dot(viewVector);
+
+ return spat.queueDistance;
+ }
+
+ public int compare(Geometry o1, Geometry o2) {
+ Material m1 = o1.getMaterial();
+ Material m2 = o2.getMaterial();
+
+ int sortId = m1.compareTo(m2);
+
+ if (sortId == 0){
+ // use the same shader.
+ // sort front-to-back then.
+ float d1 = distanceToCam(o1);
+ float d2 = distanceToCam(o2);
+
+ if (d1 == d2)
+ return 0;
+ else if (d1 < d2)
+ return -1;
+ else
+ return 1;
+ }else{
+ return sortId;
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/RenderQueue.java b/engine/src/core/com/jme3/renderer/queue/RenderQueue.java
new file mode 100644
index 0000000..dba604e
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/RenderQueue.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.renderer.queue;
+
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+
+/**
+ * <code>RenderQueue</code> is used to queue up and sort
+ * {@link Geometry geometries} for rendering.
+ *
+ * @author Kirill Vainer
+ */
+public class RenderQueue {
+
+ private GeometryList opaqueList;
+ private GeometryList guiList;
+ private GeometryList transparentList;
+ private GeometryList translucentList;
+ private GeometryList skyList;
+ private GeometryList shadowRecv;
+ private GeometryList shadowCast;
+
+ /**
+ * Creates a new RenderQueue, the default {@link GeometryComparator comparators}
+ * are used for all {@link GeometryList geometry lists}.
+ */
+ public RenderQueue() {
+ this.opaqueList = new GeometryList(new OpaqueComparator());
+ this.guiList = new GeometryList(new GuiComparator());
+ this.transparentList = new GeometryList(new TransparentComparator());
+ this.translucentList = new GeometryList(new TransparentComparator());
+ this.skyList = new GeometryList(new NullComparator());
+ this.shadowRecv = new GeometryList(new OpaqueComparator());
+ this.shadowCast = new GeometryList(new OpaqueComparator());
+ }
+
+ /**
+ * The render queue <code>Bucket</code> specifies the bucket
+ * to which the spatial will be placed when rendered.
+ * <p>
+ * The behavior of the rendering will differ depending on which
+ * bucket the spatial is placed. A spatial's queue bucket can be set
+ * via {@link Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket) }.
+ */
+ public enum Bucket {
+ /**
+ * The renderer will try to find the optimal order for rendering all
+ * objects using this mode.
+ * You should use this mode for most normal objects, except transparent
+ * ones, as it could give a nice performance boost to your application.
+ */
+ Opaque,
+
+ /**
+ * This is the mode you should use for object with
+ * transparency in them. It will ensure the objects furthest away are
+ * rendered first. That ensures when another transparent object is drawn on
+ * top of previously drawn objects, you can see those (and the object drawn
+ * using Opaque) through the transparent parts of the newly drawn
+ * object.
+ */
+ Transparent,
+
+ /**
+ * A special mode used for rendering really far away, flat objects -
+ * e.g. skies. In this mode, the depth is set to infinity so
+ * spatials in this bucket will appear behind everything, the downside
+ * to this bucket is that 3D objects will not be rendered correctly
+ * due to lack of depth testing.
+ */
+ Sky,
+
+ /**
+ * A special mode used for rendering transparent objects that
+ * should not be effected by {@link SceneProcessor}.
+ * Generally this would contain translucent objects, and
+ * also objects that do not write to the depth buffer such as
+ * particle emitters.
+ */
+ Translucent,
+
+ /**
+ * This is a special mode, for drawing 2D object
+ * without perspective (such as GUI or HUD parts).
+ * The spatial's world coordinate system has the range
+ * of [0, 0, -1] to [Width, Height, 1] where Width/Height is
+ * the resolution of the screen rendered to. Any spatials
+ * outside of that range are culled.
+ */
+ Gui,
+
+ /**
+ * A special mode, that will ensure that this spatial uses the same
+ * mode as the parent Node does.
+ */
+ Inherit,
+ }
+
+ /**
+ * <code>ShadowMode</code> is a marker used to specify how shadow
+ * effects should treat the spatial.
+ */
+ public enum ShadowMode {
+ /**
+ * Disable both shadow casting and shadow receiving for this spatial.
+ * Generally used for special effects like particle emitters.
+ */
+ Off,
+
+ /**
+ * Enable casting of shadows but not receiving them.
+ */
+ Cast,
+
+ /**
+ * Enable receiving of shadows but not casting them.
+ */
+ Receive,
+
+ /**
+ * Enable both receiving and casting of shadows.
+ */
+ CastAndReceive,
+
+ /**
+ * Inherit the <code>ShadowMode</code> from the parent node.
+ */
+ Inherit
+ }
+
+ /**
+ * Sets a different geometry comparator for the specified bucket, one
+ * of Gui, Opaque, Sky, or Transparent. The GeometryComparators are
+ * used to sort the accumulated list of geometries before actual rendering
+ * occurs.
+ *
+ * <p>The most significant comparator is the one for the transparent
+ * bucket since there is no correct way to sort the transparent bucket
+ * that will handle all geometry all the time. In certain cases, the
+ * application may know the best way to sort and now has the option of
+ * configuring a specific implementation.</p>
+ *
+ * <p>The default comparators are:</p>
+ * <ul>
+ * <li>Bucket.Opaque: {@link com.jme3.renderer.queue.OpaqueComparator} which sorts
+ * by material first and front to back within the same material.
+ * <li>Bucket.Transparent: {@link com.jme3.renderer.queue.TransparentComparator} which
+ * sorts purely back to front by leading bounding edge with no material sort.
+ * <li>Bucket.Translucent: {@link com.jme3.renderer.queue.TransparentComparator} which
+ * sorts purely back to front by leading bounding edge with no material sort. this bucket is rendered after post processors.
+ * <li>Bucket.Sky: {@link com.jme3.renderer.queue.NullComparator} which does no sorting
+ * at all.
+ * <li>Bucket.Gui: {@link com.jme3.renderer.queue.GuiComparator} sorts geometries back to
+ * front based on their Z values.
+ */
+ public void setGeometryComparator(Bucket bucket, GeometryComparator c) {
+ switch (bucket) {
+ case Gui:
+ guiList = new GeometryList(c);
+ break;
+ case Opaque:
+ opaqueList = new GeometryList(c);
+ break;
+ case Sky:
+ skyList = new GeometryList(c);
+ break;
+ case Transparent:
+ transparentList = new GeometryList(c);
+ break;
+ case Translucent:
+ translucentList = new GeometryList(c);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown bucket type: " + bucket);
+ }
+ }
+
+ /**
+ * Adds a geometry to a shadow bucket.
+ * Note that this operation is done automatically by the
+ * {@link RenderManager}. {@link SceneProcessor}s that handle
+ * shadow rendering should fetch the queue by using
+ * {@link #getShadowQueueContent(com.jme3.renderer.queue.RenderQueue.ShadowMode) },
+ * by default no action is taken on the shadow queues.
+ *
+ * @param g The geometry to add
+ * @param shadBucket The shadow bucket type, if it is
+ * {@link ShadowMode#CastAndReceive}, it is added to both the cast
+ * and the receive buckets.
+ */
+ public void addToShadowQueue(Geometry g, ShadowMode shadBucket) {
+ switch (shadBucket) {
+ case Inherit:
+ break;
+ case Off:
+ break;
+ case Cast:
+ shadowCast.add(g);
+ break;
+ case Receive:
+ shadowRecv.add(g);
+ break;
+ case CastAndReceive:
+ shadowCast.add(g);
+ shadowRecv.add(g);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized shadow bucket type: " + shadBucket);
+ }
+ }
+
+ /**
+ * Adds a geometry to the given bucket.
+ * The {@link RenderManager} automatically handles this task
+ * when flattening the scene graph. The bucket to add
+ * the geometry is determined by {@link Geometry#getQueueBucket() }.
+ *
+ * @param g The geometry to add
+ * @param bucket The bucket to add to, usually
+ * {@link Geometry#getQueueBucket() }.
+ */
+ public void addToQueue(Geometry g, Bucket bucket) {
+ switch (bucket) {
+ case Gui:
+ guiList.add(g);
+ break;
+ case Opaque:
+ opaqueList.add(g);
+ break;
+ case Sky:
+ skyList.add(g);
+ break;
+ case Transparent:
+ transparentList.add(g);
+ break;
+ case Translucent:
+ translucentList.add(g);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown bucket type: " + bucket);
+ }
+ }
+
+ /**
+ *
+ * @param shadBucket
+ * @return
+ */
+ public GeometryList getShadowQueueContent(ShadowMode shadBucket) {
+ switch (shadBucket) {
+ case Cast:
+ return shadowCast;
+ case Receive:
+ return shadowRecv;
+ default:
+ throw new IllegalArgumentException("Only Cast or Receive are allowed");
+ }
+ }
+
+ private void renderGeometryList(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
+ list.setCamera(cam); // select camera for sorting
+ list.sort();
+ for (int i = 0; i < list.size(); i++) {
+ Geometry obj = list.get(i);
+ assert obj != null;
+ rm.renderGeometry(obj);
+ obj.queueDistance = Float.NEGATIVE_INFINITY;
+ }
+ if (clear) {
+ list.clear();
+ }
+ }
+
+ public void renderShadowQueue(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
+ renderGeometryList(list, rm, cam, clear);
+ }
+
+ public void renderShadowQueue(ShadowMode shadBucket, RenderManager rm, Camera cam, boolean clear) {
+ switch (shadBucket) {
+ case Cast:
+ renderGeometryList(shadowCast, rm, cam, clear);
+ break;
+ case Receive:
+ renderGeometryList(shadowRecv, rm, cam, clear);
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected shadow bucket: " + shadBucket);
+ }
+ }
+
+ public boolean isQueueEmpty(Bucket bucket) {
+ switch (bucket) {
+ case Gui:
+ return guiList.size() == 0;
+ case Opaque:
+ return opaqueList.size() == 0;
+ case Sky:
+ return skyList.size() == 0;
+ case Transparent:
+ return transparentList.size() == 0;
+ case Translucent:
+ return translucentList.size() == 0;
+ default:
+ throw new UnsupportedOperationException("Unsupported bucket type: " + bucket);
+ }
+ }
+
+ public void renderQueue(Bucket bucket, RenderManager rm, Camera cam) {
+ renderQueue(bucket, rm, cam, true);
+ }
+
+ public void renderQueue(Bucket bucket, RenderManager rm, Camera cam, boolean clear) {
+ switch (bucket) {
+ case Gui:
+ renderGeometryList(guiList, rm, cam, clear);
+ break;
+ case Opaque:
+ renderGeometryList(opaqueList, rm, cam, clear);
+ break;
+ case Sky:
+ renderGeometryList(skyList, rm, cam, clear);
+ break;
+ case Transparent:
+ renderGeometryList(transparentList, rm, cam, clear);
+ break;
+ case Translucent:
+ renderGeometryList(translucentList, rm, cam, clear);
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported bucket type: " + bucket);
+ }
+ }
+
+ public void clear() {
+ opaqueList.clear();
+ guiList.clear();
+ transparentList.clear();
+ translucentList.clear();
+ skyList.clear();
+ shadowCast.clear();
+ shadowRecv.clear();
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/TransparentComparator.java b/engine/src/core/com/jme3/renderer/queue/TransparentComparator.java
new file mode 100644
index 0000000..0821eba
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/TransparentComparator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.queue;
+
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+public class TransparentComparator implements GeometryComparator {
+
+ private Camera cam;
+ private final Vector3f tempVec = new Vector3f();
+
+ public void setCamera(Camera cam){
+ this.cam = cam;
+ }
+
+ /**
+ * Calculates the distance from a spatial to the camera. Distance is a
+ * squared distance.
+ *
+ * @param spat
+ * Spatial to distancize.
+ * @return Distance from Spatial to camera.
+ */
+ private float distanceToCam2(Geometry spat){
+ if (spat == null)
+ return Float.NEGATIVE_INFINITY;
+
+ if (spat.queueDistance != Float.NEGATIVE_INFINITY)
+ return spat.queueDistance;
+
+ Vector3f camPosition = cam.getLocation();
+ Vector3f viewVector = cam.getDirection();
+ Vector3f spatPosition = null;
+
+ if (spat.getWorldBound() != null){
+ spatPosition = spat.getWorldBound().getCenter();
+ }else{
+ spatPosition = spat.getWorldTranslation();
+ }
+
+ spatPosition.subtract(camPosition, tempVec);
+ spat.queueDistance = tempVec.dot(tempVec);
+
+ float retval = Math.abs(tempVec.dot(viewVector)
+ / viewVector.dot(viewVector));
+ viewVector.mult(retval, tempVec);
+
+ spat.queueDistance = tempVec.length();
+
+ return spat.queueDistance;
+ }
+
+ private float distanceToCam(Geometry spat){
+ // NOTE: It is best to check the distance
+ // to the bound's closest edge vs. the bound's center here.
+ return spat.getWorldBound().distanceToEdge(cam.getLocation());
+ }
+
+ public int compare(Geometry o1, Geometry o2) {
+ float d1 = distanceToCam(o1);
+ float d2 = distanceToCam(o2);
+
+ if (d1 == d2)
+ return 0;
+ else if (d1 < d2)
+ return 1;
+ else
+ return -1;
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/AssetLinkNode.java b/engine/src/core/com/jme3/scene/AssetLinkNode.java
new file mode 100644
index 0000000..e50680c
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/AssetLinkNode.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.ModelKey;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.util.SafeArrayList;
+import java.io.IOException;
+import java.util.Map.Entry;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The AssetLinkNode does not store its children when exported to file.
+ * Instead, you can add a list of AssetKeys that will be loaded and attached
+ * when the AssetLinkNode is restored.
+ *
+ * @author normenhansen
+ */
+public class AssetLinkNode extends Node {
+
+ protected ArrayList<ModelKey> assetLoaderKeys = new ArrayList<ModelKey>();
+ protected Map<ModelKey, Spatial> assetChildren = new HashMap<ModelKey, Spatial>();
+
+ public AssetLinkNode() {
+ }
+
+ public AssetLinkNode(ModelKey key) {
+ this(key.getName(), key);
+ }
+
+ public AssetLinkNode(String name, ModelKey key) {
+ super(name);
+ assetLoaderKeys.add(key);
+ }
+
+ /**
+ * Add a "linked" child. These are loaded from the assetManager when the
+ * AssetLinkNode is loaded from a binary file.
+ * @param key
+ */
+ public void addLinkedChild(ModelKey key) {
+ if (assetLoaderKeys.contains(key)) {
+ return;
+ }
+ assetLoaderKeys.add(key);
+ }
+
+ public void removeLinkedChild(ModelKey key) {
+ assetLoaderKeys.remove(key);
+ }
+
+ public ArrayList<ModelKey> getAssetLoaderKeys() {
+ return assetLoaderKeys;
+ }
+
+ public void attachLinkedChild(AssetManager manager, ModelKey key) {
+ addLinkedChild(key);
+ Spatial child = manager.loadAsset(key);
+ assetChildren.put(key, child);
+ attachChild(child);
+ }
+
+ public void attachLinkedChild(Spatial spat, ModelKey key) {
+ addLinkedChild(key);
+ assetChildren.put(key, spat);
+ attachChild(spat);
+ }
+
+ public void detachLinkedChild(ModelKey key) {
+ Spatial spatial = assetChildren.get(key);
+ if (spatial != null) {
+ detachChild(spatial);
+ }
+ removeLinkedChild(key);
+ assetChildren.remove(key);
+ }
+
+ public void detachLinkedChild(Spatial child, ModelKey key) {
+ removeLinkedChild(key);
+ assetChildren.remove(key);
+ detachChild(child);
+ }
+
+ /**
+ * Loads the linked children AssetKeys from the AssetManager and attaches them to the Node<br>
+ * If they are already attached, they will be reloaded.
+ * @param manager
+ */
+ public void attachLinkedChildren(AssetManager manager) {
+ detachLinkedChildren();
+ for (Iterator<ModelKey> it = assetLoaderKeys.iterator(); it.hasNext();) {
+ ModelKey assetKey = it.next();
+ Spatial curChild = assetChildren.get(assetKey);
+ if (curChild != null) {
+ curChild.removeFromParent();
+ }
+ Spatial child = manager.loadAsset(assetKey);
+ attachChild(child);
+ assetChildren.put(assetKey, child);
+ }
+ }
+
+ public void detachLinkedChildren() {
+ Set<Entry<ModelKey, Spatial>> set = assetChildren.entrySet();
+ for (Iterator<Entry<ModelKey, Spatial>> it = set.iterator(); it.hasNext();) {
+ Entry<ModelKey, Spatial> entry = it.next();
+ entry.getValue().removeFromParent();
+ it.remove();
+ }
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ BinaryImporter importer = BinaryImporter.getInstance();
+ AssetManager loaderManager = e.getAssetManager();
+
+ assetLoaderKeys = (ArrayList<ModelKey>) capsule.readSavableArrayList("assetLoaderKeyList", new ArrayList<ModelKey>());
+ for (Iterator<ModelKey> it = assetLoaderKeys.iterator(); it.hasNext();) {
+ ModelKey modelKey = it.next();
+ AssetInfo info = loaderManager.locateAsset(modelKey);
+ Spatial child = null;
+ if (info != null) {
+ child = (Spatial) importer.load(info);
+ }
+ if (child != null) {
+ child.parent = this;
+ children.add(child);
+ assetChildren.put(modelKey, child);
+ } else {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot locate {0} for asset link node {1}",
+ new Object[]{ modelKey, key });
+ }
+ }
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ SafeArrayList<Spatial> childs = children;
+ children = new SafeArrayList<Spatial>(Spatial.class);
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.writeSavableArrayList(assetLoaderKeys, "assetLoaderKeyList", null);
+ children = childs;
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java
new file mode 100644
index 0000000..bc1b2cb
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/BatchNode.java
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene;
+
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * BatchNode holds geometrie that are batched version of all geometries that are in its sub scenegraph.
+ * There is one geometry per different material in the sub tree.
+ * this geometries are directly attached to the node in the scene graph.
+ * usage is like any other node except you have to call the {@link #batch()} method once all geoms have been attached to the sub scene graph and theire material set
+ * (see todo more automagic for further enhancements)
+ * all the geometry that have been batched are set to {@link CullHint#Always} to not render them.
+ * the sub geometries can be transformed as usual their transforms are used to update the mesh of the geometryBatch.
+ * sub geoms can be removed but it may be slower than the normal spatial removing
+ * Sub geoms can be added after the batch() method has been called but won't be batched and will be rendered as normal geometries.
+ * To integrate them in the batch you have to call the batch() method again on the batchNode.
+ *
+ * TODO normal or tangents or both looks a bit weird
+ * TODO more automagic (batch when needed in the updateLigicalState)
+ * @author Nehon
+ */
+public class BatchNode extends Node implements Savable {
+
+ private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
+ /**
+ * the map of geometry holding the batched meshes
+ */
+ protected Map<Material, Batch> batches = new HashMap<Material, Batch>();
+ /**
+ * used to store transformed vectors before proceeding to a bulk put into the FloatBuffer
+ */
+ private float[] tmpFloat;
+ private float[] tmpFloatN;
+ private float[] tmpFloatT;
+ int maxVertCount = 0;
+ boolean useTangents = false;
+ boolean needsFullRebatch = true;
+
+ /**
+ * Construct a batchNode
+ */
+ public BatchNode() {
+ super();
+ }
+
+ public BatchNode(String name) {
+ super(name);
+ }
+
+ @Override
+ public void updateGeometricState() {
+ if ((refreshFlags & RF_LIGHTLIST) != 0) {
+ updateWorldLightList();
+ }
+
+ if ((refreshFlags & RF_TRANSFORM) != 0) {
+ // combine with parent transforms- same for all spatial
+ // subclasses.
+ updateWorldTransforms();
+ }
+
+ if (!children.isEmpty()) {
+ // the important part- make sure child geometric state is refreshed
+ // first before updating own world bound. This saves
+ // a round-trip later on.
+ // NOTE 9/19/09
+ // Although it does save a round trip,
+
+ for (Spatial child : children.getArray()) {
+ child.updateGeometricState();
+ }
+
+ for (Batch batch : batches.values()) {
+ if (batch.needMeshUpdate) {
+ batch.geometry.getMesh().updateBound();
+ batch.geometry.updateWorldBound();
+ batch.needMeshUpdate = false;
+
+ }
+ }
+
+
+ }
+
+ if ((refreshFlags & RF_BOUND) != 0) {
+ updateWorldBound();
+ }
+
+ assert refreshFlags == 0;
+ }
+
+ protected Transform getTransforms(Geometry geom) {
+ return geom.getWorldTransform();
+ }
+
+ protected void updateSubBatch(Geometry bg) {
+ Batch batch = batches.get(bg.getMaterial());
+ if (batch != null) {
+ Mesh mesh = batch.geometry.getMesh();
+
+ VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
+ FloatBuffer posBuf = (FloatBuffer) pvb.getData();
+ VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
+ FloatBuffer normBuf = (FloatBuffer) nvb.getData();
+
+ if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
+
+ VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
+ FloatBuffer tanBuf = (FloatBuffer) tvb.getData();
+ doTransformsTangents(posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat);
+ tvb.updateData(tanBuf);
+ } else {
+ doTransforms(posBuf, normBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat);
+ }
+ pvb.updateData(posBuf);
+ nvb.updateData(normBuf);
+
+
+ batch.needMeshUpdate = true;
+ }
+ }
+
+ /**
+ * Batch this batchNode
+ * every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call
+ */
+ public void batch() {
+ doBatch();
+ //we set the batch geometries to ignore transforms to avoid transforms of parent nodes to be applied twice
+ for (Batch batch : batches.values()) {
+ batch.geometry.setIgnoreTransform(true);
+ }
+ }
+
+ protected void doBatch() {
+ Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>();
+ maxVertCount = 0;
+ int nbGeoms = 0;
+
+ gatherGeomerties(matMap, this, needsFullRebatch);
+ if (needsFullRebatch) {
+ for (Batch batch : batches.values()) {
+ batch.geometry.removeFromParent();
+ }
+ batches.clear();
+ }
+
+ for (Material material : matMap.keySet()) {
+ Mesh m = new Mesh();
+ List<Geometry> list = matMap.get(material);
+ nbGeoms += list.size();
+ if (!needsFullRebatch) {
+ list.add(batches.get(material).geometry);
+ }
+ mergeGeometries(m, list);
+ m.setDynamic();
+ Batch batch = new Batch();
+
+ batch.geometry = new Geometry(name + "-batch" + batches.size());
+ batch.geometry.setMaterial(material);
+ this.attachChild(batch.geometry);
+
+
+ batch.geometry.setMesh(m);
+ batch.geometry.getMesh().updateCounts();
+ batch.geometry.getMesh().updateBound();
+ batches.put(material, batch);
+ }
+
+ logger.log(Level.INFO, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
+
+
+ //init temp float arrays
+ tmpFloat = new float[maxVertCount * 3];
+ tmpFloatN = new float[maxVertCount * 3];
+ if (useTangents) {
+ tmpFloatT = new float[maxVertCount * 4];
+ }
+ }
+
+ private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) {
+
+ if (n.getClass() == Geometry.class) {
+
+ if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) {
+ Geometry g = (Geometry) n;
+ if (!g.isBatched() || rebatch) {
+ if (g.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching");
+ }
+ List<Geometry> list = map.get(g.getMaterial());
+ if (list == null) {
+ list = new ArrayList<Geometry>();
+ map.put(g.getMaterial(), list);
+ }
+ g.setTransformRefresh();
+ list.add(g);
+ }
+ }
+
+ } else if (n instanceof Node) {
+ for (Spatial child : ((Node) n).getChildren()) {
+ if (child instanceof BatchNode) {
+ continue;
+ }
+ gatherGeomerties(map, child, rebatch);
+ }
+ }
+
+ }
+
+ private boolean isBatch(Spatial s) {
+ for (Batch batch : batches.values()) {
+ if (batch.geometry == s) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the material to the all the batches of this BatchNode
+ * use setMaterial(Material material,int batchIndex) to set a material to a specific batch
+ *
+ * @param material the material to use for this geometry
+ */
+ @Override
+ public void setMaterial(Material material) {
+// for (Batch batch : batches.values()) {
+// batch.geometry.setMaterial(material);
+// }
+ throw new UnsupportedOperationException("Unsupported for now, please set the material on the geoms before batching");
+ }
+
+ /**
+ * Returns the material that is used for the first batch of this BatchNode
+ *
+ * use getMaterial(Material material,int batchIndex) to get a material from a specific batch
+ *
+ * @return the material that is used for the first batch of this BatchNode
+ *
+ * @see #setMaterial(com.jme3.material.Material)
+ */
+ public Material getMaterial() {
+ if (!batches.isEmpty()) {
+ Batch b = batches.get(batches.keySet().iterator().next());
+ return b.geometry.getMaterial();
+ }
+ return null;//material;
+ }
+
+// /**
+// * Sets the material to the a specific batch of this BatchNode
+// *
+// *
+// * @param material the material to use for this geometry
+// */
+// public void setMaterial(Material material,int batchIndex) {
+// if (!batches.isEmpty()) {
+//
+// }
+//
+// }
+//
+// /**
+// * Returns the material that is used for the first batch of this BatchNode
+// *
+// * use getMaterial(Material material,int batchIndex) to get a material from a specific batch
+// *
+// * @return the material that is used for the first batch of this BatchNode
+// *
+// * @see #setMaterial(com.jme3.material.Material)
+// */
+// public Material getMaterial(int batchIndex) {
+// if (!batches.isEmpty()) {
+// Batch b = batches.get(batches.keySet().iterator().next());
+// return b.geometry.getMaterial();
+// }
+// return null;//material;
+// }
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+//
+// if (material != null) {
+// oc.write(material.getAssetName(), "materialName", null);
+// }
+// oc.write(material, "material", null);
+
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+
+
+// material = null;
+// String matName = ic.readString("materialName", null);
+// if (matName != null) {
+// // Material name is set,
+// // Attempt to load material via J3M
+// try {
+// material = im.getAssetManager().loadMaterial(matName);
+// } catch (AssetNotFoundException ex) {
+// // Cannot find J3M file.
+// logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.",
+// matName);
+// }
+// }
+// // If material is NULL, try to load it from the geometry
+// if (material == null) {
+// material = (Material) ic.readSavable("material", null);
+// }
+
+ }
+
+ /**
+ * Merges all geometries in the collection into
+ * the output mesh. Does not take into account materials.
+ *
+ * @param geometries
+ * @param outMesh
+ */
+ private void mergeGeometries(Mesh outMesh, List<Geometry> geometries) {
+ int[] compsForBuf = new int[VertexBuffer.Type.values().length];
+ VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length];
+
+ int totalVerts = 0;
+ int totalTris = 0;
+ int totalLodLevels = 0;
+
+ Mesh.Mode mode = null;
+ for (Geometry geom : geometries) {
+ totalVerts += geom.getVertexCount();
+ totalTris += geom.getTriangleCount();
+ totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
+ if (maxVertCount < geom.getVertexCount()) {
+ maxVertCount = geom.getVertexCount();
+ }
+ Mesh.Mode listMode;
+ int components;
+ switch (geom.getMesh().getMode()) {
+ case Points:
+ listMode = Mesh.Mode.Points;
+ components = 1;
+ break;
+ case LineLoop:
+ case LineStrip:
+ case Lines:
+ listMode = Mesh.Mode.Lines;
+ components = 2;
+ break;
+ case TriangleFan:
+ case TriangleStrip:
+ case Triangles:
+ listMode = Mesh.Mode.Triangles;
+ components = 3;
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+
+ for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) {
+ compsForBuf[entry.getKey()] = entry.getValue().getNumComponents();
+ formatForBuf[entry.getKey()] = entry.getValue().getFormat();
+ }
+
+ if (mode != null && mode != listMode) {
+ throw new UnsupportedOperationException("Cannot combine different"
+ + " primitive types: " + mode + " != " + listMode);
+ }
+ mode = listMode;
+ compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
+ }
+
+ outMesh.setMode(mode);
+ if (totalVerts >= 65536) {
+ // make sure we create an UnsignedInt buffer so
+ // we can fit all of the meshes
+ formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
+ } else {
+ formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort;
+ }
+
+ // generate output buffers based on retrieved info
+ for (int i = 0; i < compsForBuf.length; i++) {
+ if (compsForBuf[i] == 0) {
+ continue;
+ }
+
+ Buffer data;
+ if (i == VertexBuffer.Type.Index.ordinal()) {
+ data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
+ } else {
+ data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
+ }
+
+ VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]);
+ vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data);
+ outMesh.setBuffer(vb);
+ }
+
+ int globalVertIndex = 0;
+ int globalTriIndex = 0;
+
+ for (Geometry geom : geometries) {
+ Mesh inMesh = geom.getMesh();
+ geom.batch(this, globalVertIndex);
+
+ int geomVertCount = inMesh.getVertexCount();
+ int geomTriCount = inMesh.getTriangleCount();
+
+ for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
+ VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
+
+ VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
+
+ if (outBuf == null) {
+ continue;
+ }
+
+ if (VertexBuffer.Type.Index.ordinal() == bufType) {
+ int components = compsForBuf[bufType];
+
+ IndexBuffer inIdx = inMesh.getIndicesAsList();
+ IndexBuffer outIdx = outMesh.getIndexBuffer();
+
+ for (int tri = 0; tri < geomTriCount; tri++) {
+ for (int comp = 0; comp < components; comp++) {
+ int idx = inIdx.get(tri * components + comp) + globalVertIndex;
+ outIdx.put((globalTriIndex + tri) * components + comp, idx);
+ }
+ }
+ } else if (VertexBuffer.Type.Position.ordinal() == bufType) {
+ FloatBuffer inPos = (FloatBuffer) inBuf.getData();
+ FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+ doCopyBuffer(inPos, globalVertIndex, outPos, 3);
+ } else if (VertexBuffer.Type.Normal.ordinal() == bufType || VertexBuffer.Type.Tangent.ordinal() == bufType) {
+ FloatBuffer inPos = (FloatBuffer) inBuf.getData();
+ FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+ doCopyBuffer(inPos, globalVertIndex, outPos, compsForBuf[bufType]);
+ if (VertexBuffer.Type.Tangent.ordinal() == bufType) {
+ useTangents = true;
+ }
+ } else {
+ inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
+// for (int vert = 0; vert < geomVertCount; vert++) {
+// int curGlobalVertIndex = globalVertIndex + vert;
+// inBuf.copyElement(vert, outBuf, curGlobalVertIndex);
+// }
+ }
+ }
+
+ globalVertIndex += geomVertCount;
+ globalTriIndex += geomTriCount;
+ }
+ }
+
+ private void doTransforms(FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) {
+ TempVars vars = TempVars.get();
+ Vector3f pos = vars.vect1;
+ Vector3f norm = vars.vect2;
+
+ int length = (end - start) * 3;
+
+ // offset is given in element units
+ // convert to be in component units
+ int offset = start * 3;
+ bufPos.position(offset);
+ bufNorm.position(offset);
+ bufPos.get(tmpFloat, 0, length);
+ bufNorm.get(tmpFloatN, 0, length);
+ int index = 0;
+ while (index < length) {
+ pos.x = tmpFloat[index];
+ norm.x = tmpFloatN[index++];
+ pos.y = tmpFloat[index];
+ norm.y = tmpFloatN[index++];
+ pos.z = tmpFloat[index];
+ norm.z = tmpFloatN[index];
+
+ transform.mult(pos, pos);
+ transform.multNormal(norm, norm);
+
+ index -= 2;
+ tmpFloat[index] = pos.x;
+ tmpFloatN[index++] = norm.x;
+ tmpFloat[index] = pos.y;
+ tmpFloatN[index++] = norm.y;
+ tmpFloat[index] = pos.z;
+ tmpFloatN[index++] = norm.z;
+
+ }
+ vars.release();
+ bufPos.position(offset);
+ //using bulk put as it's faster
+ bufPos.put(tmpFloat, 0, length);
+ bufNorm.position(offset);
+ //using bulk put as it's faster
+ bufNorm.put(tmpFloatN, 0, length);
+ }
+
+ private void doTransformsTangents(FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
+ TempVars vars = TempVars.get();
+ Vector3f pos = vars.vect1;
+ Vector3f norm = vars.vect2;
+ Vector3f tan = vars.vect3;
+
+ int length = (end - start) * 3;
+ int tanLength = (end - start) * 4;
+
+ // offset is given in element units
+ // convert to be in component units
+ int offset = start * 3;
+ int tanOffset = start * 4;
+
+ bufPos.position(offset);
+ bufNorm.position(offset);
+ bufTangents.position(tanOffset);
+ bufPos.get(tmpFloat, 0, length);
+ bufNorm.get(tmpFloatN, 0, length);
+ bufTangents.get(tmpFloatT, 0, tanLength);
+
+ int index = 0;
+ int tanIndex = 0;
+ while (index < length) {
+ pos.x = tmpFloat[index];
+ norm.x = tmpFloatN[index++];
+ pos.y = tmpFloat[index];
+ norm.y = tmpFloatN[index++];
+ pos.z = tmpFloat[index];
+ norm.z = tmpFloatN[index];
+
+ tan.x = tmpFloatT[tanIndex++];
+ tan.y = tmpFloatT[tanIndex++];
+ tan.z = tmpFloatT[tanIndex++];
+
+ transform.mult(pos, pos);
+ transform.multNormal(norm, norm);
+ transform.multNormal(tan, tan);
+
+ index -= 2;
+ tanIndex -= 3;
+
+ tmpFloat[index] = pos.x;
+ tmpFloatN[index++] = norm.x;
+ tmpFloat[index] = pos.y;
+ tmpFloatN[index++] = norm.y;
+ tmpFloat[index] = pos.z;
+ tmpFloatN[index++] = norm.z;
+
+ tmpFloatT[tanIndex++] = tan.x;
+ tmpFloatT[tanIndex++] = tan.y;
+ tmpFloatT[tanIndex++] = tan.z;
+
+ //Skipping 4th element of tangent buffer (handedness)
+ tanIndex++;
+
+ }
+ vars.release();
+ bufPos.position(offset);
+ //using bulk put as it's faster
+ bufPos.put(tmpFloat, 0, length);
+ bufNorm.position(offset);
+ //using bulk put as it's faster
+ bufNorm.put(tmpFloatN, 0, length);
+ bufTangents.position(tanOffset);
+ //using bulk put as it's faster
+ bufTangents.put(tmpFloatT, 0, tanLength);
+ }
+
+ private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
+ TempVars vars = TempVars.get();
+ Vector3f pos = vars.vect1;
+
+ // offset is given in element units
+ // convert to be in component units
+ offset *= componentSize;
+
+ for (int i = 0; i < inBuf.capacity() / componentSize; i++) {
+ pos.x = inBuf.get(i * componentSize + 0);
+ pos.y = inBuf.get(i * componentSize + 1);
+ pos.z = inBuf.get(i * componentSize + 2);
+
+ outBuf.put(offset + i * componentSize + 0, pos.x);
+ outBuf.put(offset + i * componentSize + 1, pos.y);
+ outBuf.put(offset + i * componentSize + 2, pos.z);
+ }
+ vars.release();
+ }
+
+ protected class Batch {
+
+ Geometry geometry;
+ boolean needMeshUpdate = false;
+ }
+
+ protected void setNeedsFullRebatch(boolean needsFullRebatch) {
+ this.needsFullRebatch = needsFullRebatch;
+ }
+
+ public int getOffsetIndex(Geometry batchedGeometry){
+ return batchedGeometry.startIndex;
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/CameraNode.java b/engine/src/core/com/jme3/scene/CameraNode.java
new file mode 100644
index 0000000..c6bb48b
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/CameraNode.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.control.CameraControl;
+import com.jme3.scene.control.CameraControl.ControlDirection;
+
+/**
+ * <code>CameraNode</code> simply uses {@link CameraControl} to implement
+ * linking of camera and node data.
+ *
+ * @author Tim8Dev
+ */
+public class CameraNode extends Node {
+
+ private CameraControl camControl;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public CameraNode() {
+ }
+
+ public CameraNode(String name, Camera camera) {
+ this(name, new CameraControl(camera));
+ }
+
+ public CameraNode(String name, CameraControl control) {
+ super(name);
+ addControl(control);
+ camControl = control;
+ }
+
+ public void setEnabled(boolean enabled) {
+ camControl.setEnabled(enabled);
+ }
+
+ public boolean isEnabled() {
+ return camControl.isEnabled();
+ }
+
+ public void setControlDir(ControlDirection controlDir) {
+ camControl.setControlDir(controlDir);
+ }
+
+ public void setCamera(Camera camera) {
+ camControl.setCamera(camera);
+ }
+
+ public ControlDirection getControlDir() {
+ return camControl.getControlDir();
+ }
+
+ public Camera getCamera() {
+ return camControl.getCamera();
+ }
+
+// @Override
+// public void lookAt(Vector3f position, Vector3f upVector) {
+// this.lookAt(position, upVector);
+// camControl.getCamera().lookAt(position, upVector);
+// }
+}
diff --git a/engine/src/core/com/jme3/scene/CollisionData.java b/engine/src/core/com/jme3/scene/CollisionData.java
new file mode 100644
index 0000000..914bf24
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/CollisionData.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.export.Savable;
+import com.jme3.math.Matrix4f;
+
+/**
+ * <code>CollisionData</code> is an interface that can be used to
+ * do triangle-accurate collision with bounding volumes and rays.
+ *
+ * @author Kirill Vainer
+ */
+public interface CollisionData extends Savable, Cloneable {
+ public int collideWith(Collidable other,
+ Matrix4f worldMatrix,
+ BoundingVolume worldBound,
+ CollisionResults results);
+}
diff --git a/engine/src/core/com/jme3/scene/Geometry.java b/engine/src/core/com/jme3/scene/Geometry.java
new file mode 100644
index 0000000..b02196d
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/Geometry.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene;
+
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Transform;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.Queue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>Geometry</code> defines a leaf node of the scene graph. The leaf node
+ * contains the geometric data for rendering objects. It manages all rendering
+ * information such as a {@link Material} object to define how the surface
+ * should be shaded and the {@link Mesh} data to contain the actual geometry.
+ *
+ * @author Kirill Vainer
+ */
+public class Geometry extends Spatial {
+
+ // Version #1: removed shared meshes.
+ // models loaded with shared mesh will be automatically fixed.
+ public static final int SAVABLE_VERSION = 1;
+
+ private static final Logger logger = Logger.getLogger(Geometry.class.getName());
+ protected Mesh mesh;
+ protected transient int lodLevel = 0;
+ protected Material material;
+ /**
+ * When true, the geometry's transform will not be applied.
+ */
+ protected boolean ignoreTransform = false;
+ protected transient Matrix4f cachedWorldMat = new Matrix4f();
+ /**
+ * used when geometry is batched
+ */
+ protected BatchNode batchNode = null;
+ /**
+ * the start index of this geom's mesh in the batchNode mesh
+ */
+ protected int startIndex;
+ /**
+ * the previous transforms of the geometry used to compute world transforms
+ */
+ protected Transform prevBatchTransforms = null;
+ /**
+ * the cached offset matrix used when the geometry is batched
+ */
+ protected Matrix4f cachedOffsetMat = null;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Geometry() {
+ }
+
+ /**
+ * Create a geometry node without any mesh data.
+ * Both the mesh and the material are null, the geometry
+ * cannot be rendered until those are set.
+ *
+ * @param name The name of this geometry
+ */
+ public Geometry(String name) {
+ super(name);
+ }
+
+ /**
+ * Create a geometry node with mesh data.
+ * The material of the geometry is null, it cannot
+ * be rendered until it is set.
+ *
+ * @param name The name of this geometry
+ * @param mesh The mesh data for this geometry
+ */
+ public Geometry(String name, Mesh mesh) {
+ this(name);
+ if (mesh == null) {
+ throw new NullPointerException();
+ }
+
+ this.mesh = mesh;
+ }
+
+ /**
+ * @return If ignoreTransform mode is set.
+ *
+ * @see Geometry#setIgnoreTransform(boolean)
+ */
+ public boolean isIgnoreTransform() {
+ return ignoreTransform;
+ }
+
+ /**
+ * @param ignoreTransform If true, the geometry's transform will not be applied.
+ */
+ public void setIgnoreTransform(boolean ignoreTransform) {
+ this.ignoreTransform = ignoreTransform;
+ }
+
+ /**
+ * Sets the LOD level to use when rendering the mesh of this geometry.
+ * Level 0 indicates that the default index buffer should be used,
+ * levels [1, LodLevels + 1] represent the levels set on the mesh
+ * with {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
+ *
+ * @param lod The lod level to set
+ */
+ @Override
+ public void setLodLevel(int lod) {
+ if (mesh.getNumLodLevels() == 0) {
+ throw new IllegalStateException("LOD levels are not set on this mesh");
+ }
+
+ if (lod < 0 || lod >= mesh.getNumLodLevels()) {
+ throw new IllegalArgumentException("LOD level is out of range: " + lod);
+ }
+
+ lodLevel = lod;
+ }
+
+ /**
+ * Returns the LOD level set with {@link #setLodLevel(int) }.
+ *
+ * @return the LOD level set
+ */
+ public int getLodLevel() {
+ return lodLevel;
+ }
+
+ /**
+ * Returns this geometry's mesh vertex count.
+ *
+ * @return this geometry's mesh vertex count.
+ *
+ * @see Mesh#getVertexCount()
+ */
+ public int getVertexCount() {
+ return mesh.getVertexCount();
+ }
+
+ /**
+ * Returns this geometry's mesh triangle count.
+ *
+ * @return this geometry's mesh triangle count.
+ *
+ * @see Mesh#getTriangleCount()
+ */
+ public int getTriangleCount() {
+ return mesh.getTriangleCount();
+ }
+
+ /**
+ * Sets the mesh to use for this geometry when rendering.
+ *
+ * @param mesh the mesh to use for this geometry
+ *
+ * @throws IllegalArgumentException If mesh is null
+ */
+ public void setMesh(Mesh mesh) {
+ if (mesh == null) {
+ throw new IllegalArgumentException();
+ }
+ if (isBatched()) {
+ throw new UnsupportedOperationException("Cannot set the mesh of a batched geometry");
+ }
+
+ this.mesh = mesh;
+ setBoundRefresh();
+ }
+
+ /**
+ * Returns the mseh to use for this geometry
+ *
+ * @return the mseh to use for this geometry
+ *
+ * @see #setMesh(com.jme3.scene.Mesh)
+ */
+ public Mesh getMesh() {
+ return mesh;
+ }
+
+ /**
+ * Sets the material to use for this geometry.
+ *
+ * @param material the material to use for this geometry
+ */
+ @Override
+ public void setMaterial(Material material) {
+ if (isBatched()) {
+ throw new UnsupportedOperationException("Cannot set the material of a batched geometry, change the material of the parent BatchNode.");
+ }
+ this.material = material;
+ }
+
+ /**
+ * Returns the material that is used for this geometry.
+ *
+ * @return the material that is used for this geometry
+ *
+ * @see #setMaterial(com.jme3.material.Material)
+ */
+ public Material getMaterial() {
+ return material;
+ }
+
+ /**
+ * @return The bounding volume of the mesh, in model space.
+ */
+ public BoundingVolume getModelBound() {
+ return mesh.getBound();
+ }
+
+ /**
+ * Updates the bounding volume of the mesh. Should be called when the
+ * mesh has been modified.
+ */
+ public void updateModelBound() {
+ mesh.updateBound();
+ setBoundRefresh();
+ }
+
+ /**
+ * <code>updateWorldBound</code> updates the bounding volume that contains
+ * this geometry. The location of the geometry is based on the location of
+ * all this node's parents.
+ *
+ * @see Spatial#updateWorldBound()
+ */
+ @Override
+ protected void updateWorldBound() {
+ super.updateWorldBound();
+ if (mesh == null) {
+ throw new NullPointerException("Geometry: " + getName() + " has null mesh");
+ }
+
+ if (mesh.getBound() != null) {
+ if (ignoreTransform) {
+ // we do not transform the model bound by the world transform,
+ // just use the model bound as-is
+ worldBound = mesh.getBound().clone(worldBound);
+ } else {
+ worldBound = mesh.getBound().transform(worldTransform, worldBound);
+ }
+ }
+ }
+
+ @Override
+ protected void updateWorldTransforms() {
+
+ super.updateWorldTransforms();
+ computeWorldMatrix();
+
+ if (isBatched()) {
+ computeOffsetTransform();
+ batchNode.updateSubBatch(this);
+ prevBatchTransforms.set(batchNode.getTransforms(this));
+
+ }
+ // geometry requires lights to be sorted
+ worldLights.sort(true);
+ }
+
+ /**
+ * Batch this geometry, should only be called by the BatchNode.
+ * @param node the batchNode
+ * @param startIndex the starting index of this geometry in the batched mesh
+ */
+ protected void batch(BatchNode node, int startIndex) {
+ this.batchNode = node;
+ this.startIndex = startIndex;
+ prevBatchTransforms = new Transform();
+ cachedOffsetMat = new Matrix4f();
+ setCullHint(CullHint.Always);
+ }
+
+ /**
+ * unBatch this geometry.
+ */
+ protected void unBatch() {
+ this.startIndex = 0;
+ prevBatchTransforms = null;
+ cachedOffsetMat = null;
+ //once the geometry is removed from the screnegraph the batchNode needs to be rebatched.
+ this.batchNode.setNeedsFullRebatch(true);
+ this.batchNode = null;
+ setCullHint(CullHint.Dynamic);
+ }
+
+ @Override
+ public boolean removeFromParent() {
+ boolean removed = super.removeFromParent();
+ //if the geometry is batched we also have to unbatch it
+ if (isBatched()) {
+ unBatch();
+ }
+ return removed;
+ }
+
+ /**
+ * Recomputes the cached offset matrix used when the geometry is batched *
+ */
+ public void computeOffsetTransform() {
+ TempVars vars = TempVars.get();
+ Matrix4f tmpMat = vars.tempMat42;
+
+ // Compute the cached world matrix
+ cachedOffsetMat.loadIdentity();
+ cachedOffsetMat.setRotationQuaternion(prevBatchTransforms.getRotation());
+ cachedOffsetMat.setTranslation(prevBatchTransforms.getTranslation());
+
+
+ Matrix4f scaleMat = vars.tempMat4;
+ scaleMat.loadIdentity();
+ scaleMat.scale(prevBatchTransforms.getScale());
+ cachedOffsetMat.multLocal(scaleMat);
+ cachedOffsetMat.invertLocal();
+
+ tmpMat.loadIdentity();
+ tmpMat.setRotationQuaternion(batchNode.getTransforms(this).getRotation());
+ tmpMat.setTranslation(batchNode.getTransforms(this).getTranslation());
+ scaleMat.loadIdentity();
+ scaleMat.scale(batchNode.getTransforms(this).getScale());
+ tmpMat.multLocal(scaleMat);
+
+ tmpMat.mult(cachedOffsetMat, cachedOffsetMat);
+
+ vars.release();
+ }
+
+ /**
+ * Indicate that the transform of this spatial has changed and that
+ * a refresh is required.
+ */
+ @Override
+ protected void setTransformRefresh() {
+ refreshFlags |= RF_TRANSFORM;
+ setBoundRefresh();
+ }
+
+ /**
+ * Recomputes the matrix returned by {@link Geometry#getWorldMatrix() }.
+ * This will require a localized transform update for this geometry.
+ */
+ public void computeWorldMatrix() {
+ // Force a local update of the geometry's transform
+ checkDoTransformUpdate();
+
+ // Compute the cached world matrix
+ cachedWorldMat.loadIdentity();
+ cachedWorldMat.setRotationQuaternion(worldTransform.getRotation());
+ cachedWorldMat.setTranslation(worldTransform.getTranslation());
+
+ TempVars vars = TempVars.get();
+ Matrix4f scaleMat = vars.tempMat4;
+ scaleMat.loadIdentity();
+ scaleMat.scale(worldTransform.getScale());
+ cachedWorldMat.multLocal(scaleMat);
+ vars.release();
+ }
+
+ /**
+ * A {@link Matrix4f matrix} that transforms the {@link Geometry#getMesh() mesh}
+ * from model space to world space. This matrix is computed based on the
+ * {@link Geometry#getWorldTransform() world transform} of this geometry.
+ * In order to receive updated values, you must call {@link Geometry#computeWorldMatrix() }
+ * before using this method.
+ *
+ * @return Matrix to transform from local space to world space
+ */
+ public Matrix4f getWorldMatrix() {
+ return cachedWorldMat;
+ }
+
+ /**
+ * Sets the model bound to use for this geometry.
+ * This alters the bound used on the mesh as well via
+ * {@link Mesh#setBound(com.jme3.bounding.BoundingVolume) } and
+ * forces the world bounding volume to be recomputed.
+ *
+ * @param modelBound The model bound to set
+ */
+ @Override
+ public void setModelBound(BoundingVolume modelBound) {
+ this.worldBound = null;
+ mesh.setBound(modelBound);
+ setBoundRefresh();
+
+ // NOTE: Calling updateModelBound() would cause the mesh
+ // to recompute the bound based on the geometry thus making
+ // this call useless!
+ //updateModelBound();
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) {
+ // Force bound to update
+ checkDoBoundUpdate();
+ // Update transform, and compute cached world matrix
+ computeWorldMatrix();
+
+ assert (refreshFlags & (RF_BOUND | RF_TRANSFORM)) == 0;
+
+ if (mesh != null) {
+ // NOTE: BIHTree in mesh already checks collision with the
+ // mesh's bound
+ int prevSize = results.size();
+ int added = mesh.collideWith(other, cachedWorldMat, worldBound, results);
+ int newSize = results.size();
+ for (int i = prevSize; i < newSize; i++) {
+ results.getCollisionDirect(i).setGeometry(this);
+ }
+ return added;
+ }
+ return 0;
+ }
+
+ @Override
+ public void depthFirstTraversal(SceneGraphVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
+ }
+
+ public boolean isBatched() {
+ return batchNode != null;
+ }
+
+ /**
+ * This version of clone is a shallow clone, in other words, the
+ * same mesh is referenced as the original geometry.
+ * Exception: if the mesh is marked as being a software
+ * animated mesh, (bind pose is set) then the positions
+ * and normals are deep copied.
+ */
+ @Override
+ public Geometry clone(boolean cloneMaterial) {
+ Geometry geomClone = (Geometry) super.clone(cloneMaterial);
+ geomClone.cachedWorldMat = cachedWorldMat.clone();
+ if (material != null) {
+ if (cloneMaterial) {
+ geomClone.material = material.clone();
+ } else {
+ geomClone.material = material;
+ }
+ }
+
+ if (mesh != null && mesh.getBuffer(Type.BindPosePosition) != null) {
+ geomClone.mesh = mesh.cloneForAnim();
+ }
+
+ return geomClone;
+ }
+
+ /**
+ * This version of clone is a shallow clone, in other words, the
+ * same mesh is referenced as the original geometry.
+ * Exception: if the mesh is marked as being a software
+ * animated mesh, (bind pose is set) then the positions
+ * and normals are deep copied.
+ */
+ @Override
+ public Geometry clone() {
+ return clone(true);
+ }
+
+ /**
+ * Creates a deep clone of the geometry,
+ * this creates an identical copy of the mesh
+ * with the vertexbuffer data duplicated.
+ */
+ @Override
+ public Spatial deepClone() {
+ Geometry geomClone = clone(true);
+ geomClone.mesh = mesh.deepClone();
+ return geomClone;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(mesh, "mesh", null);
+ if (material != null) {
+ oc.write(material.getAssetName(), "materialName", null);
+ }
+ oc.write(material, "material", null);
+ oc.write(ignoreTransform, "ignoreTransform", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ mesh = (Mesh) ic.readSavable("mesh", null);
+
+ material = null;
+ String matName = ic.readString("materialName", null);
+ if (matName != null) {
+ // Material name is set,
+ // Attempt to load material via J3M
+ try {
+ material = im.getAssetManager().loadMaterial(matName);
+ } catch (AssetNotFoundException ex) {
+ // Cannot find J3M file.
+ logger.log(Level.FINE, "Cannot locate {0} for geometry {1}", new Object[]{matName, key});
+ }
+ }
+ // If material is NULL, try to load it from the geometry
+ if (material == null) {
+ material = (Material) ic.readSavable("material", null);
+ }
+ ignoreTransform = ic.readBoolean("ignoreTransform", false);
+
+ if (ic.getSavableVersion(Geometry.class) == 0){
+ // Fix shared mesh (if set)
+ Mesh sharedMesh = getUserData(UserData.JME_SHAREDMESH);
+ if (sharedMesh != null){
+ getMesh().extractVertexData(sharedMesh);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/LightNode.java b/engine/src/core/com/jme3/scene/LightNode.java
new file mode 100644
index 0000000..5e6dc76
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/LightNode.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene;
+
+import com.jme3.light.Light;
+import com.jme3.scene.control.LightControl;
+import com.jme3.scene.control.LightControl.ControlDirection;
+
+/**
+ * <code>LightNode</code> is used to link together a {@link Light} object
+ * with a {@link Node} object.
+ *
+ * @author Tim8Dev
+ */
+public class LightNode extends Node {
+
+ private LightControl lightControl;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public LightNode() {
+ }
+
+ public LightNode(String name, Light light) {
+ this(name, new LightControl(light));
+ }
+
+ public LightNode(String name, LightControl control) {
+ super(name);
+ addControl(control);
+ lightControl = control;
+ }
+
+ /**
+ * Enable or disable the <code>LightNode</code> functionality.
+ *
+ * @param enabled If false, the functionality of LightNode will
+ * be disabled.
+ */
+ public void setEnabled(boolean enabled) {
+ lightControl.setEnabled(enabled);
+ }
+
+ public boolean isEnabled() {
+ return lightControl.isEnabled();
+ }
+
+ public void setControlDir(ControlDirection controlDir) {
+ lightControl.setControlDir(controlDir);
+ }
+
+ public void setLight(Light light) {
+ lightControl.setLight(light);
+ }
+
+ public ControlDirection getControlDir() {
+ return lightControl.getControlDir();
+ }
+
+ public Light getLight() {
+ return lightControl.getLight();
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java
new file mode 100644
index 0000000..6c587f2
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/Mesh.java
@@ -0,0 +1,1315 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.bih.BIHTree;
+import com.jme3.export.*;
+import com.jme3.material.RenderState;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.mesh.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.SafeArrayList;
+import java.io.IOException;
+import java.nio.*;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <code>Mesh</code> is used to store rendering data.
+ * <p>
+ * All visible elements in a scene are represented by meshes.
+ * Meshes may contain three types of geometric primitives:
+ * <ul>
+ * <li>Points - Every vertex represents a single point in space,
+ * the size of each point is specified via {@link Mesh#setPointSize(float) }.
+ * Points can also be used for {@link RenderState#setPointSprite(boolean) point
+ * sprite} mode.</li>
+ * <li>Lines - 2 vertices represent a line segment, with the width specified
+ * via {@link Mesh#setLineWidth(float) }.</li>
+ * <li>Triangles - 3 vertices represent a solid triangle primitive. </li>
+ * </ul>
+ *
+ * @author Kirill Vainer
+ */
+public class Mesh implements Savable, Cloneable {
+
+ /**
+ * The mode of the Mesh specifies both the type of primitive represented
+ * by the mesh and how the data should be interpreted.
+ */
+ public enum Mode {
+ /**
+ * A primitive is a single point in space. The size of the points
+ * can be specified with {@link Mesh#setPointSize(float) }.
+ */
+ Points(true),
+
+ /**
+ * A primitive is a line segment. Every two vertices specify
+ * a single line. {@link Mesh#setLineWidth(float) } can be used
+ * to set the width of the lines.
+ */
+ Lines(true),
+
+ /**
+ * A primitive is a line segment. The first two vertices specify
+ * a single line, while subsequent vertices are combined with the
+ * previous vertex to make a line. {@link Mesh#setLineWidth(float) } can
+ * be used to set the width of the lines.
+ */
+ LineStrip(false),
+
+ /**
+ * Identical to {@link #LineStrip} except that at the end
+ * the last vertex is connected with the first to form a line.
+ * {@link Mesh#setLineWidth(float) } can be used
+ * to set the width of the lines.
+ */
+ LineLoop(false),
+
+ /**
+ * A primitive is a triangle. Each 3 vertices specify a single
+ * triangle.
+ */
+ Triangles(true),
+
+ /**
+ * Similar to {@link #Triangles}, the first 3 vertices
+ * specify a triangle, while subsequent vertices are combined with
+ * the previous two to form a triangle.
+ */
+ TriangleStrip(false),
+
+ /**
+ * Similar to {@link #Triangles}, the first 3 vertices
+ * specify a triangle, each 2 subsequent vertices are combined
+ * with the very first vertex to make a triangle.
+ */
+ TriangleFan(false),
+
+ /**
+ * A combination of various triangle modes. It is best to avoid
+ * using this mode as it may not be supported by all renderers.
+ * The {@link Mesh#setModeStart(int[]) mode start points} and
+ * {@link Mesh#setElementLengths(int[]) element lengths} must
+ * be specified for this mode.
+ */
+ Hybrid(false);
+
+ private boolean listMode = false;
+
+ private Mode(boolean listMode){
+ this.listMode = listMode;
+ }
+
+ /**
+ * Returns true if the specified mode is a list mode (meaning
+ * ,it specifies the indices as a linear list and not some special
+ * format).
+ * Will return true for the types {@link #Points}, {@link #Lines} and
+ * {@link #Triangles}.
+ *
+ * @return true if the mode is a list type mode
+ */
+ public boolean isListMode(){
+ return listMode;
+ }
+ }
+
+ /**
+ * The bounding volume that contains the mesh entirely.
+ * By default a BoundingBox (AABB).
+ */
+ private BoundingVolume meshBound = new BoundingBox();
+
+ private CollisionData collisionTree = null;
+
+ private SafeArrayList<VertexBuffer> buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
+ private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
+ private VertexBuffer[] lodLevels;
+ private float pointSize = 1;
+ private float lineWidth = 1;
+
+ private transient int vertexArrayID = -1;
+
+ private int vertCount = -1;
+ private int elementCount = -1;
+ private int maxNumWeights = -1; // only if using skeletal animation
+
+ private int[] elementLengths;
+ private int[] modeStart;
+
+ private Mode mode = Mode.Triangles;
+
+ /**
+ * Creates a new mesh with no {@link VertexBuffer vertex buffers}.
+ */
+ public Mesh(){
+ }
+
+ /**
+ * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex
+ * buffers} are shared between this and the clone mesh, the rest
+ * of the data is cloned.
+ *
+ * @return A shallow clone of the mesh
+ */
+ @Override
+ public Mesh clone() {
+ try {
+ Mesh clone = (Mesh) super.clone();
+ clone.meshBound = meshBound.clone();
+ clone.collisionTree = collisionTree != null ? collisionTree : null;
+ clone.buffers = buffers.clone();
+ clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class,buffersList);
+ clone.vertexArrayID = -1;
+ if (elementLengths != null) {
+ clone.elementLengths = elementLengths.clone();
+ }
+ if (modeStart != null) {
+ clone.modeStart = modeStart.clone();
+ }
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Creates a deep clone of this mesh.
+ * The {@link VertexBuffer vertex buffers} and the data inside them
+ * is cloned.
+ *
+ * @return a deep clone of this mesh.
+ */
+ public Mesh deepClone(){
+ try{
+ Mesh clone = (Mesh) super.clone();
+ clone.meshBound = meshBound != null ? meshBound.clone() : null;
+
+ // TODO: Collision tree cloning
+ //clone.collisionTree = collisionTree != null ? collisionTree : null;
+ clone.collisionTree = null; // it will get re-generated in any case
+
+ clone.buffers = new IntMap<VertexBuffer>();
+ clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
+ for (Entry<VertexBuffer> ent : buffers){
+ VertexBuffer bufClone = ent.getValue().clone();
+ clone.buffers.put(ent.getKey(), bufClone);
+ clone.buffersList.add(bufClone);
+ }
+
+ clone.vertexArrayID = -1;
+ clone.vertCount = -1;
+ clone.elementCount = -1;
+
+ // although this could change
+ // if the bone weight/index buffers are modified
+ clone.maxNumWeights = maxNumWeights;
+
+ clone.elementLengths = elementLengths != null ? elementLengths.clone() : null;
+ clone.modeStart = modeStart != null ? modeStart.clone() : null;
+ return clone;
+ }catch (CloneNotSupportedException ex){
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Clone the mesh for animation use.
+ * This creates a shallow clone of the mesh, sharing most
+ * of the {@link VertexBuffer vertex buffer} data, however the
+ * {@link Type#Position}, {@link Type#Normal}, and {@link Type#Tangent} buffers
+ * are deeply cloned.
+ *
+ * @return A clone of the mesh for animation use.
+ */
+ public Mesh cloneForAnim(){
+ Mesh clone = clone();
+ if (getBuffer(Type.BindPosePosition) != null){
+ VertexBuffer oldPos = getBuffer(Type.Position);
+
+ // NOTE: creates deep clone
+ VertexBuffer newPos = oldPos.clone();
+ clone.clearBuffer(Type.Position);
+ clone.setBuffer(newPos);
+
+ if (getBuffer(Type.BindPoseNormal) != null){
+ VertexBuffer oldNorm = getBuffer(Type.Normal);
+ VertexBuffer newNorm = oldNorm.clone();
+ clone.clearBuffer(Type.Normal);
+ clone.setBuffer(newNorm);
+
+ if (getBuffer(Type.BindPoseTangent) != null){
+ VertexBuffer oldTang = getBuffer(Type.Tangent);
+ VertexBuffer newTang = oldTang.clone();
+ clone.clearBuffer(Type.Tangent);
+ clone.setBuffer(newTang);
+ }
+ }
+ }
+ return clone;
+ }
+
+ /**
+ * Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal},
+ * and {@link Type#BindPoseTangent}
+ * buffers for this mesh by duplicating them based on the position and normal
+ * buffers already set on the mesh.
+ * This method does nothing if the mesh has no bone weight or index
+ * buffers.
+ *
+ * @param forSoftwareAnim Should be true if the bind pose is to be generated.
+ */
+ public void generateBindPose(boolean forSoftwareAnim){
+ if (forSoftwareAnim){
+ VertexBuffer pos = getBuffer(Type.Position);
+ if (pos == null || getBuffer(Type.BoneIndex) == null) {
+ // ignore, this mesh doesn't have positional data
+ // or it doesn't have bone-vertex assignments, so its not animated
+ return;
+ }
+
+ VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition);
+ bindPos.setupData(Usage.CpuOnly,
+ 3,
+ Format.Float,
+ BufferUtils.clone(pos.getData()));
+ setBuffer(bindPos);
+
+ // XXX: note that this method also sets stream mode
+ // so that animation is faster. this is not needed for hardware skinning
+ pos.setUsage(Usage.Stream);
+
+ VertexBuffer norm = getBuffer(Type.Normal);
+ if (norm != null) {
+ VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal);
+ bindNorm.setupData(Usage.CpuOnly,
+ 3,
+ Format.Float,
+ BufferUtils.clone(norm.getData()));
+ setBuffer(bindNorm);
+ norm.setUsage(Usage.Stream);
+ }
+
+ VertexBuffer tangents = getBuffer(Type.Tangent);
+ if (tangents != null) {
+ VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent);
+ bindTangents.setupData(Usage.CpuOnly,
+ 4,
+ Format.Float,
+ BufferUtils.clone(tangents.getData()));
+ setBuffer(bindTangents);
+ tangents.setUsage(Usage.Stream);
+ }
+ }
+ }
+
+ /**
+ * Prepares the mesh for software skinning by converting the bone index
+ * and weight buffers to heap buffers.
+ *
+ * @param forSoftwareAnim Should be true to enable the conversion.
+ */
+ public void prepareForAnim(boolean forSoftwareAnim){
+ if (forSoftwareAnim){
+ // convert indices
+ VertexBuffer indices = getBuffer(Type.BoneIndex);
+ ByteBuffer originalIndex = (ByteBuffer) indices.getData();
+ ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity());
+ originalIndex.clear();
+ arrayIndex.put(originalIndex);
+ indices.updateData(arrayIndex);
+
+ // convert weights
+ VertexBuffer weights = getBuffer(Type.BoneWeight);
+ FloatBuffer originalWeight = (FloatBuffer) weights.getData();
+ FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
+ originalWeight.clear();
+ arrayWeight.put(originalWeight);
+ weights.updateData(arrayWeight);
+ }
+ }
+
+ /**
+ * Set the LOD (level of detail) index buffers on this mesh.
+ *
+ * @param lodLevels The LOD levels to set
+ */
+ public void setLodLevels(VertexBuffer[] lodLevels){
+ this.lodLevels = lodLevels;
+ }
+
+ /**
+ * @return The number of LOD levels set on this mesh, including the main
+ * index buffer, returns zero if there are no lod levels.
+ */
+ public int getNumLodLevels(){
+ return lodLevels != null ? lodLevels.length : 0;
+ }
+
+ /**
+ * Returns the lod level at the given index.
+ *
+ * @param lod The lod level index, this does not include
+ * the main index buffer.
+ * @return The LOD index buffer at the index
+ *
+ * @throws IndexOutOfBoundsException If the index is outside of the
+ * range [0, {@link #getNumLodLevels()}].
+ *
+ * @see #setLodLevels(com.jme3.scene.VertexBuffer[])
+ */
+ public VertexBuffer getLodLevel(int lod){
+ return lodLevels[lod];
+ }
+
+ /**
+ * Get the element lengths for {@link Mode#Hybrid} mesh mode.
+ *
+ * @return element lengths
+ */
+ public int[] getElementLengths() {
+ return elementLengths;
+ }
+
+ /**
+ * Set the element lengths for {@link Mode#Hybrid} mesh mode.
+ *
+ * @param elementLengths The element lengths to set
+ */
+ public void setElementLengths(int[] elementLengths) {
+ this.elementLengths = elementLengths;
+ }
+
+ /**
+ * Set the mode start indices for {@link Mode#Hybrid} mesh mode.
+ *
+ * @return mode start indices
+ */
+ public int[] getModeStart() {
+ return modeStart;
+ }
+
+ /**
+ * Get the mode start indices for {@link Mode#Hybrid} mesh mode.
+ *
+ * @return mode start indices
+ */
+ public void setModeStart(int[] modeStart) {
+ this.modeStart = modeStart;
+ }
+
+ /**
+ * Returns the mesh mode
+ *
+ * @return the mesh mode
+ *
+ * @see #setMode(com.jme3.scene.Mesh.Mode)
+ */
+ public Mode getMode() {
+ return mode;
+ }
+
+ /**
+ * Change the Mesh's mode. By default the mode is {@link Mode#Triangles}.
+ *
+ * @param mode The new mode to set
+ *
+ * @see Mode
+ */
+ public void setMode(Mode mode) {
+ this.mode = mode;
+ updateCounts();
+ }
+
+ /**
+ * Returns the maximum number of weights per vertex on this mesh.
+ *
+ * @return maximum number of weights per vertex
+ *
+ * @see #setMaxNumWeights(int)
+ */
+ public int getMaxNumWeights() {
+ return maxNumWeights;
+ }
+
+ /**
+ * Set the maximum number of weights per vertex on this mesh.
+ * Only relevant if this mesh has bone index/weight buffers.
+ * This value should be between 0 and 4.
+ *
+ * @param maxNumWeights
+ */
+ public void setMaxNumWeights(int maxNumWeights) {
+ this.maxNumWeights = maxNumWeights;
+ }
+
+ /**
+ * Returns the size of points for point meshes
+ *
+ * @return the size of points
+ *
+ * @see #setPointSize(float)
+ */
+ public float getPointSize() {
+ return pointSize;
+ }
+
+ /**
+ * Set the size of points for meshes of mode {@link Mode#Points}.
+ * The point size is specified as on-screen pixels, the default
+ * value is 1.0. The point size
+ * does nothing if {@link RenderState#setPointSprite(boolean) point sprite}
+ * render state is enabled, in that case, the vertex shader must specify the
+ * point size by writing to <code>gl_PointSize</code>.
+ *
+ * @param pointSize The size of points
+ */
+ public void setPointSize(float pointSize) {
+ this.pointSize = pointSize;
+ }
+
+ /**
+ * Returns the line width for line meshes.
+ *
+ * @return the line width
+ */
+ public float getLineWidth() {
+ return lineWidth;
+ }
+
+ /**
+ * Specify the line width for meshes of the line modes, such
+ * as {@link Mode#Lines}. The line width is specified as on-screen pixels,
+ * the default value is 1.0.
+ *
+ * @param lineWidth The line width
+ */
+ public void setLineWidth(float lineWidth) {
+ this.lineWidth = lineWidth;
+ }
+
+ /**
+ * Indicates to the GPU that this mesh will not be modified (a hint).
+ * Sets the usage mode to {@link Usage#Static}
+ * for all {@link VertexBuffer vertex buffers} on this Mesh.
+ */
+ public void setStatic() {
+ for (Entry<VertexBuffer> entry : buffers){
+ entry.getValue().setUsage(Usage.Static);
+ }
+ }
+
+ /**
+ * Indicates to the GPU that this mesh will be modified occasionally (a hint).
+ * Sets the usage mode to {@link Usage#Dynamic}
+ * for all {@link VertexBuffer vertex buffers} on this Mesh.
+ */
+ public void setDynamic() {
+ for (Entry<VertexBuffer> entry : buffers){
+ entry.getValue().setUsage(Usage.Dynamic);
+ }
+ }
+
+ /**
+ * Indicates to the GPU that this mesh will be modified every frame (a hint).
+ * Sets the usage mode to {@link Usage#Stream}
+ * for all {@link VertexBuffer vertex buffers} on this Mesh.
+ */
+ public void setStreamed(){
+ for (Entry<VertexBuffer> entry : buffers){
+ entry.getValue().setUsage(Usage.Stream);
+ }
+ }
+
+ /**
+ * Interleaves the data in this mesh. This operation cannot be reversed.
+ * Some GPUs may prefer the data in this format, however it is a good idea
+ * to <em>avoid</em> using this method as it disables some engine features.
+ */
+ public void setInterleaved(){
+ ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
+ for (Entry<VertexBuffer> entry : buffers){
+ vbs.add(entry.getValue());
+ }
+// ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());
+ // index buffer not included when interleaving
+ vbs.remove(getBuffer(Type.Index));
+
+ int stride = 0; // aka bytes per vertex
+ for (int i = 0; i < vbs.size(); i++){
+ VertexBuffer vb = vbs.get(i);
+// if (vb.getFormat() != Format.Float){
+// throw new UnsupportedOperationException("Cannot interleave vertex buffer.\n" +
+// "Contains not-float data.");
+// }
+ stride += vb.componentsLength;
+ vb.getData().clear(); // reset position & limit (used later)
+ }
+
+ VertexBuffer allData = new VertexBuffer(Type.InterleavedData);
+ ByteBuffer dataBuf = BufferUtils.createByteBuffer(stride * getVertexCount());
+ allData.setupData(Usage.Static, 1, Format.UnsignedByte, dataBuf);
+
+ // adding buffer directly so that no update counts is forced
+ buffers.put(Type.InterleavedData.ordinal(), allData);
+ buffersList.add(allData);
+
+ for (int vert = 0; vert < getVertexCount(); vert++){
+ for (int i = 0; i < vbs.size(); i++){
+ VertexBuffer vb = vbs.get(i);
+ switch (vb.getFormat()){
+ case Float:
+ FloatBuffer fb = (FloatBuffer) vb.getData();
+ for (int comp = 0; comp < vb.components; comp++){
+ dataBuf.putFloat(fb.get());
+ }
+ break;
+ case Byte:
+ case UnsignedByte:
+ ByteBuffer bb = (ByteBuffer) vb.getData();
+ for (int comp = 0; comp < vb.components; comp++){
+ dataBuf.put(bb.get());
+ }
+ break;
+ case Half:
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sb = (ShortBuffer) vb.getData();
+ for (int comp = 0; comp < vb.components; comp++){
+ dataBuf.putShort(sb.get());
+ }
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer ib = (IntBuffer) vb.getData();
+ for (int comp = 0; comp < vb.components; comp++){
+ dataBuf.putInt(ib.get());
+ }
+ break;
+ case Double:
+ DoubleBuffer db = (DoubleBuffer) vb.getData();
+ for (int comp = 0; comp < vb.components; comp++){
+ dataBuf.putDouble(db.get());
+ }
+ break;
+ }
+ }
+ }
+
+ int offset = 0;
+ for (VertexBuffer vb : vbs){
+ vb.setOffset(offset);
+ vb.setStride(stride);
+
+ vb.updateData(null);
+ //vb.setupData(vb.usage, vb.components, vb.format, null);
+ offset += vb.componentsLength;
+ }
+ }
+
+ private int computeNumElements(int bufSize){
+ switch (mode){
+ case Triangles:
+ return bufSize / 3;
+ case TriangleFan:
+ case TriangleStrip:
+ return bufSize - 2;
+ case Points:
+ return bufSize;
+ case Lines:
+ return bufSize / 2;
+ case LineLoop:
+ return bufSize;
+ case LineStrip:
+ return bufSize - 1;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Update the {@link #getVertexCount() vertex} and
+ * {@link #getTriangleCount() triangle} counts for this mesh
+ * based on the current data. This method should be called
+ * after the {@link Buffer#capacity() capacities} of the mesh's
+ * {@link VertexBuffer vertex buffers} has been altered.
+ *
+ * @throws IllegalStateException If this mesh is in
+ * {@link #setInterleaved() interleaved} format.
+ */
+ public void updateCounts(){
+ if (getBuffer(Type.InterleavedData) != null)
+ throw new IllegalStateException("Should update counts before interleave");
+
+ VertexBuffer pb = getBuffer(Type.Position);
+ VertexBuffer ib = getBuffer(Type.Index);
+ if (pb != null){
+ vertCount = pb.getData().capacity() / pb.getNumComponents();
+ }
+ if (ib != null){
+ elementCount = computeNumElements(ib.getData().capacity());
+ }else{
+ elementCount = computeNumElements(vertCount);
+ }
+ }
+
+ /**
+ * Returns the triangle count for the given LOD level.
+ *
+ * @param lod The lod level to look up
+ * @return The triangle count for that LOD level
+ */
+ public int getTriangleCount(int lod){
+ if (lodLevels != null){
+ if (lod < 0)
+ throw new IllegalArgumentException("LOD level cannot be < 0");
+
+ if (lod >= lodLevels.length)
+ throw new IllegalArgumentException("LOD level "+lod+" does not exist!");
+
+ return computeNumElements(lodLevels[lod].getData().capacity());
+ }else if (lod == 0){
+ return elementCount;
+ }else{
+ throw new IllegalArgumentException("There are no LOD levels on the mesh!");
+ }
+ }
+
+ /**
+ * Returns how many triangles or elements are on this Mesh.
+ * This value is only updated when {@link #updateCounts() } is called.
+ * If the mesh mode is not a triangle mode, then this returns the
+ * number of elements/primitives, e.g. how many lines or how many points,
+ * instead of how many triangles.
+ *
+ * @return how many triangles/elements are on this Mesh.
+ */
+ public int getTriangleCount(){
+ return elementCount;
+ }
+
+ /**
+ * Returns the number of vertices on this mesh.
+ * The value is computed based on the position buffer, which
+ * must be set on all meshes.
+ *
+ * @return Number of vertices on the mesh
+ */
+ public int getVertexCount(){
+ return vertCount;
+ }
+
+ /**
+ * Gets the triangle vertex positions at the given triangle index
+ * and stores them into the v1, v2, v3 arguments.
+ *
+ * @param index The index of the triangle.
+ * Should be between 0 and {@link #getTriangleCount()}.
+ *
+ * @param v1 Vector to contain first vertex position
+ * @param v2 Vector to contain second vertex position
+ * @param v3 Vector to contain third vertex position
+ */
+ public void getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3){
+ VertexBuffer pb = getBuffer(Type.Position);
+ IndexBuffer ib = getIndicesAsList();
+ if (pb != null && pb.getFormat() == Format.Float && pb.getNumComponents() == 3){
+ FloatBuffer fpb = (FloatBuffer) pb.getData();
+
+ // aquire triangle's vertex indices
+ int vertIndex = index * 3;
+ int vert1 = ib.get(vertIndex);
+ int vert2 = ib.get(vertIndex+1);
+ int vert3 = ib.get(vertIndex+2);
+
+ BufferUtils.populateFromBuffer(v1, fpb, vert1);
+ BufferUtils.populateFromBuffer(v2, fpb, vert2);
+ BufferUtils.populateFromBuffer(v3, fpb, vert3);
+ }else{
+ throw new UnsupportedOperationException("Position buffer not set or "
+ + " has incompatible format");
+ }
+ }
+
+ /**
+ * Gets the triangle vertex positions at the given triangle index
+ * and stores them into the {@link Triangle} argument.
+ * Also sets the triangle index to the <code>index</code> argument.
+ *
+ * @param index The index of the triangle.
+ * Should be between 0 and {@link #getTriangleCount()}.
+ *
+ * @param tri The triangle to store the positions in
+ */
+ public void getTriangle(int index, Triangle tri){
+ getTriangle(index, tri.get1(), tri.get2(), tri.get3());
+ tri.setIndex(index);
+ tri.setNormal(null);
+ }
+
+ /**
+ * Gets the triangle vertex indices at the given triangle index
+ * and stores them into the given int array.
+ *
+ * @param index The index of the triangle.
+ * Should be between 0 and {@link #getTriangleCount()}.
+ *
+ * @param indices Indices of the triangle's vertices
+ */
+ public void getTriangle(int index, int[] indices){
+ IndexBuffer ib = getIndicesAsList();
+
+ // acquire triangle's vertex indices
+ int vertIndex = index * 3;
+ indices[0] = ib.get(vertIndex);
+ indices[1] = ib.get(vertIndex+1);
+ indices[2] = ib.get(vertIndex+2);
+ }
+
+ /**
+ * Returns the mesh's VAO ID. Internal use only.
+ */
+ public int getId(){
+ return vertexArrayID;
+ }
+
+ /**
+ * Sets the mesh's VAO ID. Internal use only.
+ */
+ public void setId(int id){
+ if (vertexArrayID != -1)
+ throw new IllegalStateException("ID has already been set.");
+
+ vertexArrayID = id;
+ }
+
+ /**
+ * Generates a collision tree for the mesh.
+ * Called automatically by {@link #collideWith(com.jme3.collision.Collidable,
+ * com.jme3.math.Matrix4f,
+ * com.jme3.bounding.BoundingVolume,
+ * com.jme3.collision.CollisionResults) }.
+ */
+ public void createCollisionData(){
+ BIHTree tree = new BIHTree(this);
+ tree.construct();
+ collisionTree = tree;
+ }
+
+ /**
+ * Handles collision detection, internal use only.
+ * User code should only use collideWith() on scene
+ * graph elements such as {@link Spatial}s.
+ */
+ public int collideWith(Collidable other,
+ Matrix4f worldMatrix,
+ BoundingVolume worldBound,
+ CollisionResults results){
+
+ if (collisionTree == null){
+ createCollisionData();
+ }
+
+ return collisionTree.collideWith(other, worldMatrix, worldBound, results);
+ }
+
+ /**
+ * Set a floating point {@link VertexBuffer} on the mesh.
+ *
+ * @param type The type of {@link VertexBuffer},
+ * e.g. {@link Type#Position}, {@link Type#Normal}, etc.
+ *
+ * @param components Number of components on the vertex buffer, should
+ * be between 1 and 4.
+ *
+ * @param buf The floating point data to contain
+ */
+ public void setBuffer(Type type, int components, FloatBuffer buf) {
+// VertexBuffer vb = buffers.get(type);
+ VertexBuffer vb = buffers.get(type.ordinal());
+ if (vb == null){
+ if (buf == null)
+ return;
+
+ vb = new VertexBuffer(type);
+ vb.setupData(Usage.Dynamic, components, Format.Float, buf);
+// buffers.put(type, vb);
+ buffers.put(type.ordinal(), vb);
+ buffersList.add(vb);
+ }else{
+ vb.setupData(Usage.Dynamic, components, Format.Float, buf);
+ }
+ updateCounts();
+ }
+
+ public void setBuffer(Type type, int components, float[] buf){
+ setBuffer(type, components, BufferUtils.createFloatBuffer(buf));
+ }
+
+ public void setBuffer(Type type, int components, IntBuffer buf) {
+ VertexBuffer vb = buffers.get(type.ordinal());
+ if (vb == null){
+ vb = new VertexBuffer(type);
+ vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf);
+ buffers.put(type.ordinal(), vb);
+ buffersList.add(vb);
+ updateCounts();
+ }
+ }
+
+ public void setBuffer(Type type, int components, int[] buf){
+ setBuffer(type, components, BufferUtils.createIntBuffer(buf));
+ }
+
+ public void setBuffer(Type type, int components, ShortBuffer buf) {
+ VertexBuffer vb = buffers.get(type.ordinal());
+ if (vb == null){
+ vb = new VertexBuffer(type);
+ vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf);
+ buffers.put(type.ordinal(), vb);
+ buffersList.add(vb);
+ updateCounts();
+ }
+ }
+
+ public void setBuffer(Type type, int components, byte[] buf){
+ setBuffer(type, components, BufferUtils.createByteBuffer(buf));
+ }
+
+ public void setBuffer(Type type, int components, ByteBuffer buf) {
+ VertexBuffer vb = buffers.get(type.ordinal());
+ if (vb == null){
+ vb = new VertexBuffer(type);
+ vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf);
+ buffers.put(type.ordinal(), vb);
+ buffersList.add(vb);
+ updateCounts();
+ }
+ }
+
+ public void setBuffer(VertexBuffer vb){
+ if (buffers.containsKey(vb.getBufferType().ordinal()))
+ throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
+
+ buffers.put(vb.getBufferType().ordinal(), vb);
+ buffersList.add(vb);
+ updateCounts();
+ }
+
+ /**
+ * Clears or unsets the {@link VertexBuffer} set on this mesh
+ * with the given type.
+ * Does nothing if the vertex buffer type is not set initially
+ *
+ * @param type The type to remove
+ */
+ public void clearBuffer(VertexBuffer.Type type){
+ VertexBuffer vb = buffers.remove(type.ordinal());
+ if (vb != null){
+ buffersList.remove(vb);
+ updateCounts();
+ }
+ }
+
+ public void setBuffer(Type type, int components, short[] buf){
+ setBuffer(type, components, BufferUtils.createShortBuffer(buf));
+ }
+
+ /**
+ * Get the {@link VertexBuffer} stored on this mesh with the given
+ * type.
+ *
+ * @param type The type of VertexBuffer
+ * @return the VertexBuffer data, or null if not set
+ */
+ public VertexBuffer getBuffer(Type type){
+ return buffers.get(type.ordinal());
+ }
+
+ /**
+ * Get the {@link VertexBuffer} data stored on this mesh in float
+ * format.
+ *
+ * @param type The type of VertexBuffer
+ * @return the VertexBuffer data, or null if not set
+ */
+ public FloatBuffer getFloatBuffer(Type type) {
+ VertexBuffer vb = getBuffer(type);
+ if (vb == null)
+ return null;
+
+ return (FloatBuffer) vb.getData();
+ }
+
+ /**
+ * Get the {@link VertexBuffer} data stored on this mesh in short
+ * format.
+ *
+ * @param type The type of VertexBuffer
+ * @return the VertexBuffer data, or null if not set
+ */
+ public ShortBuffer getShortBuffer(Type type) {
+ VertexBuffer vb = getBuffer(type);
+ if (vb == null)
+ return null;
+
+ return (ShortBuffer) vb.getData();
+ }
+
+ /**
+ * Acquires an index buffer that will read the vertices on the mesh
+ * as a list.
+ *
+ * @return A virtual or wrapped index buffer to read the data as a list
+ */
+ public IndexBuffer getIndicesAsList(){
+ if (mode == Mode.Hybrid)
+ throw new UnsupportedOperationException("Hybrid mode not supported");
+
+ IndexBuffer ib = getIndexBuffer();
+ if (ib != null){
+ if (mode.isListMode()){
+ // already in list mode
+ return ib;
+ }else{
+ // not in list mode but it does have an index buffer
+ // wrap it so the data is converted to list format
+ return new WrappedIndexBuffer(this);
+ }
+ }else{
+ // return a virtual index buffer that will supply
+ // "fake" indices in list format
+ return new VirtualIndexBuffer(vertCount, mode);
+ }
+ }
+
+ /**
+ * Get the index buffer for this mesh.
+ * Will return <code>null</code> if no index buffer is set.
+ *
+ * @return The index buffer of this mesh.
+ *
+ * @see Type#Index
+ */
+ public IndexBuffer getIndexBuffer() {
+ VertexBuffer vb = getBuffer(Type.Index);
+ if (vb == null)
+ return null;
+
+ Buffer buf = vb.getData();
+ if (buf instanceof ByteBuffer) {
+ return new IndexByteBuffer((ByteBuffer) buf);
+ } else if (buf instanceof ShortBuffer) {
+ return new IndexShortBuffer((ShortBuffer) buf);
+ } else if (buf instanceof IntBuffer) {
+ return new IndexIntBuffer((IntBuffer) buf);
+ } else {
+ throw new UnsupportedOperationException("Index buffer type unsupported: "+ buf.getClass());
+ }
+ }
+
+ /**
+ * Extracts the vertex attributes from the given mesh into
+ * this mesh, by using this mesh's {@link #getIndexBuffer() index buffer}
+ * to index into the attributes of the other mesh.
+ * Note that this will also change this mesh's index buffer so that
+ * the references to the vertex data match the new indices.
+ *
+ * @param other The mesh to extract the vertex data from
+ */
+ public void extractVertexData(Mesh other) {
+ // Determine the number of unique vertices need to
+ // be created. Also determine the mappings
+ // between old indices to new indices (since we avoid duplicating
+ // vertices, this is a map and not an array).
+ VertexBuffer oldIdxBuf = getBuffer(Type.Index);
+ IndexBuffer indexBuf = getIndexBuffer();
+ int numIndices = indexBuf.size();
+
+ IntMap<Integer> oldIndicesToNewIndices = new IntMap<Integer>(numIndices);
+ ArrayList<Integer> newIndicesToOldIndices = new ArrayList<Integer>();
+ int newIndex = 0;
+
+ for (int i = 0; i < numIndices; i++) {
+ int oldIndex = indexBuf.get(i);
+
+ if (!oldIndicesToNewIndices.containsKey(oldIndex)) {
+ // this vertex has not been added, so allocate a
+ // new index for it and add it to the map
+ oldIndicesToNewIndices.put(oldIndex, newIndex);
+ newIndicesToOldIndices.add(oldIndex);
+
+ // increment to have the next index
+ newIndex++;
+ }
+ }
+
+ // Number of unique verts to be created now available
+ int newNumVerts = newIndicesToOldIndices.size();
+
+ if (newIndex != newNumVerts) {
+ throw new AssertionError();
+ }
+
+ // Create the new index buffer.
+ // Do not overwrite the old one because we might be able to
+ // convert from int index buffer to short index buffer
+ IndexBuffer newIndexBuf;
+ if (newNumVerts >= 65536) {
+ newIndexBuf = new IndexIntBuffer(BufferUtils.createIntBuffer(numIndices));
+ } else {
+ newIndexBuf = new IndexShortBuffer(BufferUtils.createShortBuffer(numIndices));
+ }
+
+ for (int i = 0; i < numIndices; i++) {
+ // Map the old indices to the new indices
+ int oldIndex = indexBuf.get(i);
+ newIndex = oldIndicesToNewIndices.get(oldIndex);
+
+ newIndexBuf.put(i, newIndex);
+ }
+
+ VertexBuffer newIdxBuf = new VertexBuffer(Type.Index);
+ newIdxBuf.setupData(oldIdxBuf.getUsage(),
+ oldIdxBuf.getNumComponents(),
+ newIndexBuf instanceof IndexIntBuffer ? Format.UnsignedInt : Format.UnsignedShort,
+ newIndexBuf.getBuffer());
+ clearBuffer(Type.Index);
+ setBuffer(newIdxBuf);
+
+ // Now, create the vertex buffers
+ SafeArrayList<VertexBuffer> oldVertexData = other.getBufferList();
+ for (VertexBuffer oldVb : oldVertexData) {
+ if (oldVb.getBufferType() == VertexBuffer.Type.Index) {
+ // ignore the index buffer
+ continue;
+ }
+
+ // Create a new vertex buffer with similar configuration, but
+ // with the capacity of number of unique vertices
+ Buffer buffer = VertexBuffer.createBuffer(oldVb.getFormat(), oldVb.getNumComponents(), newNumVerts);
+
+ VertexBuffer newVb = new VertexBuffer(oldVb.getBufferType());
+ newVb.setNormalized(oldVb.isNormalized());
+ newVb.setupData(oldVb.getUsage(), oldVb.getNumComponents(), oldVb.getFormat(), buffer);
+
+ // Copy the vertex data from the old buffer into the new buffer
+ for (int i = 0; i < newNumVerts; i++) {
+ int oldIndex = newIndicesToOldIndices.get(i);
+
+ // Copy the vertex attribute from the old index
+ // to the new index
+ oldVb.copyElement(oldIndex, newVb, i);
+ }
+
+ // Set the buffer on the mesh
+ clearBuffer(newVb.getBufferType());
+ setBuffer(newVb);
+ }
+
+ // Copy max weights per vertex as well
+ setMaxNumWeights(other.getMaxNumWeights());
+
+ // The data has been copied over, update informations
+ updateCounts();
+ updateBound();
+ }
+
+ /**
+ * Scales the texture coordinate buffer on this mesh by the given
+ * scale factor.
+ * <p>
+ * Note that values above 1 will cause the
+ * texture to tile, while values below 1 will cause the texture
+ * to stretch.
+ * </p>
+ *
+ * @param scaleFactor The scale factor to scale by. Every texture
+ * coordinate is multiplied by this vector to get the result.
+ *
+ * @throws IllegalStateException If there's no texture coordinate
+ * buffer on the mesh
+ * @throws UnsupportedOperationException If the texture coordinate
+ * buffer is not in 2D float format.
+ */
+ public void scaleTextureCoordinates(Vector2f scaleFactor){
+ VertexBuffer tc = getBuffer(Type.TexCoord);
+ if (tc == null)
+ throw new IllegalStateException("The mesh has no texture coordinates");
+
+ if (tc.getFormat() != VertexBuffer.Format.Float)
+ throw new UnsupportedOperationException("Only float texture coord format is supported");
+
+ if (tc.getNumComponents() != 2)
+ throw new UnsupportedOperationException("Only 2D texture coords are supported");
+
+ FloatBuffer fb = (FloatBuffer) tc.getData();
+ fb.clear();
+ for (int i = 0; i < fb.capacity() / 2; i++){
+ float x = fb.get();
+ float y = fb.get();
+ fb.position(fb.position()-2);
+ x *= scaleFactor.getX();
+ y *= scaleFactor.getY();
+ fb.put(x).put(y);
+ }
+ fb.clear();
+ tc.updateData(fb);
+ }
+
+ /**
+ * Updates the bounding volume of this mesh.
+ * The method does nothing if the mesh has no {@link Type#Position} buffer.
+ * It is expected that the position buffer is a float buffer with 3 components.
+ */
+ public void updateBound(){
+ VertexBuffer posBuf = getBuffer(VertexBuffer.Type.Position);
+ if (meshBound != null && posBuf != null){
+ meshBound.computeFromPoints((FloatBuffer)posBuf.getData());
+ }
+ }
+
+ /**
+ * Returns the {@link BoundingVolume} of this Mesh.
+ * By default the bounding volume is a {@link BoundingBox}.
+ *
+ * @return the bounding volume of this mesh
+ */
+ public BoundingVolume getBound() {
+ return meshBound;
+ }
+
+ /**
+ * Sets the {@link BoundingVolume} for this Mesh.
+ * The bounding volume is recomputed by calling {@link #updateBound() }.
+ *
+ * @param modelBound The model bound to set
+ */
+ public void setBound(BoundingVolume modelBound) {
+ meshBound = modelBound;
+ }
+
+ /**
+ * Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh.
+ * The integer key for the map is the {@link Enum#ordinal() ordinal}
+ * of the vertex buffer's {@link Type}.
+ * Note that the returned map is a reference to the map used internally,
+ * modifying it will cause undefined results.
+ *
+ * @return map of vertex buffers on this mesh.
+ */
+ public IntMap<VertexBuffer> getBuffers(){
+ return buffers;
+ }
+
+ /**
+ * Returns a list of all {@link VertexBuffer vertex buffers} on this Mesh.
+ * Using a list instead an IntMap via the {@link #getBuffers() } method is
+ * better for iteration as there's no need to create an iterator instance.
+ * Note that the returned list is a reference to the list used internally,
+ * modifying it will cause undefined results.
+ *
+ * @return list of vertex buffers on this mesh.
+ */
+ public SafeArrayList<VertexBuffer> getBufferList(){
+ return buffersList;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule out = ex.getCapsule(this);
+
+// HashMap<String, VertexBuffer> map = new HashMap<String, VertexBuffer>();
+// for (Entry<VertexBuffer> buf : buffers){
+// if (buf.getValue() != null)
+// map.put(buf.getKey()+"a", buf.getValue());
+// }
+// out.writeStringSavableMap(map, "buffers", null);
+
+ out.write(meshBound, "modelBound", null);
+ out.write(vertCount, "vertCount", -1);
+ out.write(elementCount, "elementCount", -1);
+ out.write(maxNumWeights, "max_num_weights", -1);
+ out.write(mode, "mode", Mode.Triangles);
+ out.write(collisionTree, "collisionTree", null);
+ out.write(elementLengths, "elementLengths", null);
+ out.write(modeStart, "modeStart", null);
+ out.write(pointSize, "pointSize", 1f);
+
+ out.writeIntSavableMap(buffers, "buffers", null);
+ out.write(lodLevels, "lodLevels", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ meshBound = (BoundingVolume) in.readSavable("modelBound", null);
+ vertCount = in.readInt("vertCount", -1);
+ elementCount = in.readInt("elementCount", -1);
+ maxNumWeights = in.readInt("max_num_weights", -1);
+ mode = in.readEnum("mode", Mode.class, Mode.Triangles);
+ elementLengths = in.readIntArray("elementLengths", null);
+ modeStart = in.readIntArray("modeStart", null);
+ collisionTree = (BIHTree) in.readSavable("collisionTree", null);
+ elementLengths = in.readIntArray("elementLengths", null);
+ modeStart = in.readIntArray("modeStart", null);
+ pointSize = in.readFloat("pointSize", 1f);
+
+// in.readStringSavableMap("buffers", null);
+ buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null);
+ for (Entry<VertexBuffer> entry : buffers){
+ buffersList.add(entry.getValue());
+ }
+
+ Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null);
+ if (lodLevelsSavable != null) {
+ lodLevels = new VertexBuffer[lodLevelsSavable.length];
+ System.arraycopy( lodLevelsSavable, 0, lodLevels, 0, lodLevels.length);
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/Node.java b/engine/src/core/com/jme3/scene/Node.java
new file mode 100644
index 0000000..bb7534a
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/Node.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import com.jme3.material.Material;
+import com.jme3.util.SafeArrayList;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * <code>Node</code> defines an internal node of a scene graph. The internal
+ * node maintains a collection of children and handles merging said children
+ * into a single bound to allow for very fast culling of multiple nodes. Node
+ * allows for any number of children to be attached.
+ *
+ * @author Mark Powell
+ * @author Gregg Patton
+ * @author Joshua Slack
+ */
+public class Node extends Spatial implements Savable {
+
+ private static final Logger logger = Logger.getLogger(Node.class.getName());
+
+
+ /**
+ * This node's children.
+ */
+ protected SafeArrayList<Spatial> children = new SafeArrayList<Spatial>(Spatial.class);
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Node() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>Node</code> with a default empty
+ * list for containing children.
+ *
+ * @param name
+ * the name of the scene element. This is required for
+ * identification and comparision purposes.
+ */
+ public Node(String name) {
+ super(name);
+ }
+
+ /**
+ *
+ * <code>getQuantity</code> returns the number of children this node
+ * maintains.
+ *
+ * @return the number of children this node maintains.
+ */
+ public int getQuantity() {
+ return children.size();
+ }
+
+ @Override
+ protected void setTransformRefresh(){
+ super.setTransformRefresh();
+ for (Spatial child : children.getArray()){
+ if ((child.refreshFlags & RF_TRANSFORM) != 0)
+ continue;
+
+ child.setTransformRefresh();
+ }
+ }
+
+ @Override
+ protected void setLightListRefresh(){
+ super.setLightListRefresh();
+ for (Spatial child : children.getArray()){
+ if ((child.refreshFlags & RF_LIGHTLIST) != 0)
+ continue;
+
+ child.setLightListRefresh();
+ }
+ }
+
+ @Override
+ protected void updateWorldBound(){
+ super.updateWorldBound();
+
+ // for a node, the world bound is a combination of all it's children
+ // bounds
+ BoundingVolume resultBound = null;
+ for (Spatial child : children.getArray()) {
+ // child bound is assumed to be updated
+ assert (child.refreshFlags & RF_BOUND) == 0;
+ if (resultBound != null) {
+ // merge current world bound with child world bound
+ resultBound.mergeLocal(child.getWorldBound());
+ } else {
+ // set world bound to first non-null child world bound
+ if (child.getWorldBound() != null) {
+ resultBound = child.getWorldBound().clone(this.worldBound);
+ }
+ }
+ }
+ this.worldBound = resultBound;
+ }
+
+ @Override
+ public void updateLogicalState(float tpf){
+ super.updateLogicalState(tpf);
+
+ if (children.isEmpty()) {
+ return;
+ }
+
+ for (Spatial child : children.getArray()) {
+ child.updateLogicalState(tpf);
+ }
+ }
+
+ @Override
+ public void updateGeometricState(){
+ if ((refreshFlags & RF_LIGHTLIST) != 0){
+ updateWorldLightList();
+ }
+
+ if ((refreshFlags & RF_TRANSFORM) != 0){
+ // combine with parent transforms- same for all spatial
+ // subclasses.
+ updateWorldTransforms();
+ }
+
+ if (!children.isEmpty()) {
+ // the important part- make sure child geometric state is refreshed
+ // first before updating own world bound. This saves
+ // a round-trip later on.
+ // NOTE 9/19/09
+ // Although it does save a round trip,
+ for (Spatial child : children.getArray()) {
+ child.updateGeometricState();
+ }
+ }
+
+ if ((refreshFlags & RF_BOUND) != 0){
+ updateWorldBound();
+ }
+
+ assert refreshFlags == 0;
+ }
+
+ /**
+ * <code>getTriangleCount</code> returns the number of triangles contained
+ * in all sub-branches of this node that contain geometry.
+ *
+ * @return the triangle count of this branch.
+ */
+ @Override
+ public int getTriangleCount() {
+ int count = 0;
+ if(children != null) {
+ for(int i = 0; i < children.size(); i++) {
+ count += children.get(i).getTriangleCount();
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * <code>getVertexCount</code> returns the number of vertices contained
+ * in all sub-branches of this node that contain geometry.
+ *
+ * @return the vertex count of this branch.
+ */
+ @Override
+ public int getVertexCount() {
+ int count = 0;
+ if(children != null) {
+ for(int i = 0; i < children.size(); i++) {
+ count += children.get(i).getVertexCount();
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * <code>attachChild</code> attaches a child to this node. This node
+ * becomes the child's parent. The current number of children maintained is
+ * returned.
+ * <br>
+ * If the child already had a parent it is detached from that former parent.
+ *
+ * @param child
+ * the child to attach to this node.
+ * @return the number of children maintained by this node.
+ * @throws NullPointerException If child is null.
+ */
+ public int attachChild(Spatial child) {
+ if (child == null)
+ throw new NullPointerException();
+
+ if (child.getParent() != this && child != this) {
+ if (child.getParent() != null) {
+ child.getParent().detachChild(child);
+ }
+ child.setParent(this);
+ children.add(child);
+
+ // XXX: Not entirely correct? Forces bound update up the
+ // tree stemming from the attached child. Also forces
+ // transform update down the tree-
+ child.setTransformRefresh();
+ child.setLightListRefresh();
+ if (logger.isLoggable(Level.INFO)) {
+ logger.log(Level.INFO,"Child ({0}) attached to this node ({1})",
+ new Object[]{child.getName(), getName()});
+ }
+ }
+
+ return children.size();
+ }
+
+ /**
+ *
+ * <code>attachChildAt</code> attaches a child to this node at an index. This node
+ * becomes the child's parent. The current number of children maintained is
+ * returned.
+ * <br>
+ * If the child already had a parent it is detached from that former parent.
+ *
+ * @param child
+ * the child to attach to this node.
+ * @return the number of children maintained by this node.
+ * @throws NullPointerException if child is null.
+ */
+ public int attachChildAt(Spatial child, int index) {
+ if (child == null)
+ throw new NullPointerException();
+
+ if (child.getParent() != this && child != this) {
+ if (child.getParent() != null) {
+ child.getParent().detachChild(child);
+ }
+ child.setParent(this);
+ children.add(index, child);
+ child.setTransformRefresh();
+ child.setLightListRefresh();
+ if (logger.isLoggable(Level.INFO)) {
+ logger.log(Level.INFO,"Child ({0}) attached to this node ({1})",
+ new Object[]{child.getName(), getName()});
+ }
+ }
+
+ return children.size();
+ }
+
+ /**
+ * <code>detachChild</code> removes a given child from the node's list.
+ * This child will no longer be maintained.
+ *
+ * @param child
+ * the child to remove.
+ * @return the index the child was at. -1 if the child was not in the list.
+ */
+ public int detachChild(Spatial child) {
+ if (child == null)
+ throw new NullPointerException();
+
+ if (child.getParent() == this) {
+ int index = children.indexOf(child);
+ if (index != -1) {
+ detachChildAt(index);
+ }
+ return index;
+ }
+
+ return -1;
+ }
+
+ /**
+ * <code>detachChild</code> removes a given child from the node's list.
+ * This child will no longe be maintained. Only the first child with a
+ * matching name is removed.
+ *
+ * @param childName
+ * the child to remove.
+ * @return the index the child was at. -1 if the child was not in the list.
+ */
+ public int detachChildNamed(String childName) {
+ if (childName == null)
+ throw new NullPointerException();
+
+ for (int x = 0, max = children.size(); x < max; x++) {
+ Spatial child = children.get(x);
+ if (childName.equals(child.getName())) {
+ detachChildAt( x );
+ return x;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ *
+ * <code>detachChildAt</code> removes a child at a given index. That child
+ * is returned for saving purposes.
+ *
+ * @param index
+ * the index of the child to be removed.
+ * @return the child at the supplied index.
+ */
+ public Spatial detachChildAt(int index) {
+ Spatial child = children.remove(index);
+ if ( child != null ) {
+ child.setParent( null );
+ logger.log(Level.INFO, "{0}: Child removed.", this.toString());
+
+ // since a child with a bound was detached;
+ // our own bound will probably change.
+ setBoundRefresh();
+
+ // our world transform no longer influences the child.
+ // XXX: Not neccessary? Since child will have transform updated
+ // when attached anyway.
+ child.setTransformRefresh();
+ // lights are also inherited from parent
+ child.setLightListRefresh();
+ }
+ return child;
+ }
+
+ /**
+ *
+ * <code>detachAllChildren</code> removes all children attached to this
+ * node.
+ */
+ public void detachAllChildren() {
+ for ( int i = children.size() - 1; i >= 0; i-- ) {
+ detachChildAt(i);
+ }
+ logger.log(Level.INFO, "{0}: All children removed.", this.toString());
+ }
+
+ /**
+ * <code>getChildIndex</code> returns the index of the given spatial
+ * in this node's list of children.
+ * @param sp
+ * The spatial to look up
+ * @return
+ * The index of the spatial in the node's children, or -1
+ * if the spatial is not attached to this node
+ */
+ public int getChildIndex(Spatial sp) {
+ return children.indexOf(sp);
+ }
+
+ /**
+ * More efficient than e.g detaching and attaching as no updates are needed.
+ *
+ * @param index1 The index of the first child to swap
+ * @param index2 The index of the second child to swap
+ */
+ public void swapChildren(int index1, int index2) {
+ Spatial c2 = children.get(index2);
+ Spatial c1 = children.remove(index1);
+ children.add(index1, c2);
+ children.remove(index2);
+ children.add(index2, c1);
+ }
+
+ /**
+ *
+ * <code>getChild</code> returns a child at a given index.
+ *
+ * @param i
+ * the index to retrieve the child from.
+ * @return the child at a specified index.
+ */
+ public Spatial getChild(int i) {
+ return children.get(i);
+ }
+
+ /**
+ * <code>getChild</code> returns the first child found with exactly the
+ * given name (case sensitive.)
+ *
+ * @param name
+ * the name of the child to retrieve. If null, we'll return null.
+ * @return the child if found, or null.
+ */
+ public Spatial getChild(String name) {
+ if (name == null)
+ return null;
+
+ for (Spatial child : children.getArray()) {
+ if (name.equals(child.getName())) {
+ return child;
+ } else if(child instanceof Node) {
+ Spatial out = ((Node)child).getChild(name);
+ if(out != null) {
+ return out;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * determines if the provided Spatial is contained in the children list of
+ * this node.
+ *
+ * @param spat
+ * the child object to look for.
+ * @return true if the object is contained, false otherwise.
+ */
+ public boolean hasChild(Spatial spat) {
+ if (children.contains(spat))
+ return true;
+
+ for (Spatial child : children.getArray()) {
+ if (child instanceof Node && ((Node) child).hasChild(spat))
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns all children to this node. Note that modifying that given
+ * list is not allowed.
+ *
+ * @return a list containing all children to this node
+ */
+ public List<Spatial> getChildren() {
+ return children;
+ }
+
+ @Override
+ public void setMaterial(Material mat){
+ for (int i = 0; i < children.size(); i++){
+ children.get(i).setMaterial(mat);
+ }
+ }
+
+ @Override
+ public void setLodLevel(int lod){
+ super.setLodLevel(lod);
+ for (Spatial child : children.getArray()) {
+ child.setLodLevel(lod);
+ }
+ }
+
+ public int collideWith(Collidable other, CollisionResults results){
+ int total = 0;
+ for (Spatial child : children.getArray()){
+ total += child.collideWith(other, results);
+ }
+ return total;
+ }
+
+
+ /**
+ * Returns flat list of Spatials implementing the specified class AND
+ * with name matching the specified pattern.
+ * </P> <P>
+ * Note that we are <i>matching</i> the pattern, therefore the pattern
+ * must match the entire pattern (i.e. it behaves as if it is sandwiched
+ * between "^" and "$").
+ * You can set regex modes, like case insensitivity, by using the (?X)
+ * or (?X:Y) constructs.
+ * </P> <P>
+ * By design, it is always safe to code loops like:<CODE><PRE>
+ * for (Spatial spatial : node.descendantMatches(AClass.class, "regex"))
+ * </PRE></CODE>
+ * </P> <P>
+ * "Descendants" does not include self, per the definition of the word.
+ * To test for descendants AND self, you must do a
+ * <code>node.matches(aClass, aRegex)</code> +
+ * <code>node.descendantMatches(aClass, aRegex)</code>.
+ * <P>
+ *
+ * @param spatialSubclass Subclass which matching Spatials must implement.
+ * Null causes all Spatials to qualify.
+ * @param nameRegex Regular expression to match Spatial name against.
+ * Null causes all Names to qualify.
+ * @return Non-null, but possibly 0-element, list of matching Spatials (also Instances extending Spatials).
+ *
+ * @see java.util.regex.Pattern
+ * @see Spatial#matches(java.lang.Class, java.lang.String)
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Spatial>List<T> descendantMatches(
+ Class<T> spatialSubclass, String nameRegex) {
+ List<T> newList = new ArrayList<T>();
+ if (getQuantity() < 1) return newList;
+ for (Spatial child : getChildren()) {
+ if (child.matches(spatialSubclass, nameRegex))
+ newList.add((T)child);
+ if (child instanceof Node)
+ newList.addAll(((Node) child).descendantMatches(
+ spatialSubclass, nameRegex));
+ }
+ return newList;
+ }
+
+ /**
+ * Convenience wrapper.
+ *
+ * @see #descendantMatches(java.lang.Class, java.lang.String)
+ */
+ public <T extends Spatial>List<T> descendantMatches(
+ Class<T> spatialSubclass) {
+ return descendantMatches(spatialSubclass, null);
+ }
+
+ /**
+ * Convenience wrapper.
+ *
+ * @see #descendantMatches(java.lang.Class, java.lang.String)
+ */
+ public <T extends Spatial>List<T> descendantMatches(String nameRegex) {
+ return descendantMatches(null, nameRegex);
+ }
+
+ @Override
+ public Node clone(boolean cloneMaterials){
+ Node nodeClone = (Node) super.clone(cloneMaterials);
+// nodeClone.children = new ArrayList<Spatial>();
+// for (Spatial child : children){
+// Spatial childClone = child.clone();
+// childClone.parent = nodeClone;
+// nodeClone.children.add(childClone);
+// }
+ return nodeClone;
+ }
+
+ @Override
+ public Spatial deepClone(){
+ Node nodeClone = (Node) super.clone();
+ nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
+ for (Spatial child : children){
+ Spatial childClone = child.deepClone();
+ childClone.parent = nodeClone;
+ nodeClone.children.add(childClone);
+ }
+ return nodeClone;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ e.getCapsule(this).writeSavableArrayList(new ArrayList(children), "children", null);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ // XXX: Load children before loading itself!!
+ // This prevents empty children list if controls query
+ // it in Control.setSpatial().
+
+ children = new SafeArrayList( Spatial.class,
+ e.getCapsule(this).readSavableArrayList("children", null) );
+
+ // go through children and set parent to this node
+ if (children != null) {
+ for (Spatial child : children.getArray()) {
+ child.parent = this;
+ }
+ }
+
+ super.read(e);
+ }
+
+ @Override
+ public void setModelBound(BoundingVolume modelBound) {
+ if(children != null) {
+ for (Spatial child : children.getArray()) {
+ child.setModelBound(modelBound != null ? modelBound.clone(null) : null);
+ }
+ }
+ }
+
+ @Override
+ public void updateModelBound() {
+ if(children != null) {
+ for (Spatial child : children.getArray()) {
+ child.updateModelBound();
+ }
+ }
+ }
+
+ @Override
+ public void depthFirstTraversal(SceneGraphVisitor visitor) {
+ for (Spatial child : children.getArray()) {
+ child.depthFirstTraversal(visitor);
+ }
+ visitor.visit(this);
+ }
+
+ @Override
+ protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
+ queue.addAll(children);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/SceneGraphVisitor.java b/engine/src/core/com/jme3/scene/SceneGraphVisitor.java
new file mode 100644
index 0000000..35cc115
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/SceneGraphVisitor.java
@@ -0,0 +1,16 @@
+package com.jme3.scene;
+
+/**
+ * <code>SceneGraphVisitorAdapter</code> is used to traverse the scene
+ * graph tree.
+ * Use by calling {@link Spatial#depthFirstTraversal(com.jme3.scene.SceneGraphVisitor) }
+ * or {@link Spatial#breadthFirstTraversal(com.jme3.scene.SceneGraphVisitor)}.
+ */
+public interface SceneGraphVisitor {
+ /**
+ * Called when a spatial is visited in the scene graph.
+ *
+ * @param spatial The visited spatial
+ */
+ public void visit(Spatial spatial);
+}
diff --git a/engine/src/core/com/jme3/scene/SceneGraphVisitorAdapter.java b/engine/src/core/com/jme3/scene/SceneGraphVisitorAdapter.java
new file mode 100644
index 0000000..b91a8fb
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/SceneGraphVisitorAdapter.java
@@ -0,0 +1,35 @@
+package com.jme3.scene;
+
+/**
+ * <code>SceneGraphVisitorAdapter</code> is used to traverse the scene
+ * graph tree. The adapter version of the interface simply separates
+ * between the {@link Geometry geometries} and the {@link Node nodes} by
+ * supplying visit methods that take them.
+ * Use by calling {@link Spatial#depthFirstTraversal(com.jme3.scene.SceneGraphVisitor) }
+ * or {@link Spatial#breadthFirstTraversal(com.jme3.scene.SceneGraphVisitor)}.
+ */
+public class SceneGraphVisitorAdapter implements SceneGraphVisitor {
+
+ /**
+ * Called when a {@link Geometry} is visited.
+ *
+ * @param geom The visited geometry
+ */
+ public void visit(Geometry geom) {}
+
+ /**
+ * Called when a {@link visit} is visited.
+ *
+ * @param geom The visited node
+ */
+ public void visit(Node geom) {}
+
+ @Override
+ public final void visit(Spatial spatial) {
+ if (spatial instanceof Geometry) {
+ visit((Geometry)spatial);
+ } else {
+ visit((Node)spatial);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/SimpleBatchNode.java b/engine/src/core/com/jme3/scene/SimpleBatchNode.java
new file mode 100644
index 0000000..0f1ed29
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/SimpleBatchNode.java
@@ -0,0 +1,56 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.scene;
+
+import com.jme3.math.Transform;
+
+/**
+ *
+ * SimpleBatchNode comes with some restrictions, but can yield better performances.
+ * Geometries to be batched has to be attached directly to the BatchNode
+ * You can't attach a Node to a SimpleBatchNode
+ * SimpleBatchNode is recommended when you have a large number of geometries using the same material that does not require a complex scene graph structure.
+ * @see BatchNode
+ * @author Nehon
+ */
+public class SimpleBatchNode extends BatchNode {
+
+ public SimpleBatchNode() {
+ super();
+ }
+
+ public SimpleBatchNode(String name) {
+ super(name);
+ }
+
+ @Override
+ public int attachChild(Spatial child) {
+
+ if (!(child instanceof Geometry)) {
+ throw new UnsupportedOperationException("BatchNode is BatchMode.Simple only support child of type Geometry, use BatchMode.Complex to use a complex structure");
+ }
+
+ return super.attachChild(child);
+ }
+
+ @Override
+ protected void setTransformRefresh() {
+
+ refreshFlags |= RF_TRANSFORM;
+ setBoundRefresh();
+ for (Batch batch : batches.values()) {
+ batch.geometry.setTransformRefresh();
+ }
+ }
+
+ protected Transform getTransforms(Geometry geom){
+ return geom.getLocalTransform();
+ }
+
+ @Override
+ public void batch() {
+ doBatch();
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/Spatial.java b/engine/src/core/com/jme3/scene/Spatial.java
new file mode 100644
index 0000000..3785d6e
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/Spatial.java
@@ -0,0 +1,1478 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene;
+
+import com.jme3.asset.Asset;
+import com.jme3.asset.AssetKey;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.export.*;
+import com.jme3.light.Light;
+import com.jme3.light.LightList;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.control.Control;
+import com.jme3.util.SafeArrayList;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Logger;
+
+/**
+ * <code>Spatial</code> defines the base class for scene graph nodes. It
+ * maintains a link to a parent, it's local transforms and the world's
+ * transforms. All other scene graph elements, such as {@link Node} and
+ * {@link Geometry} are subclasses of <code>Spatial</code>.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ * @version $Revision: 4075 $, $Data$
+ */
+public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
+
+ private static final Logger logger = Logger.getLogger(Spatial.class.getName());
+
+ /**
+ * Specifies how frustum culling should be handled by
+ * this spatial.
+ */
+ public enum CullHint {
+
+ /**
+ * Do whatever our parent does. If no parent, default to {@link #Dynamic}.
+ */
+ Inherit,
+ /**
+ * Do not draw if we are not at least partially within the view frustum
+ * of the camera. This is determined via the defined
+ * Camera planes whether or not this Spatial should be culled.
+ */
+ Dynamic,
+ /**
+ * Always cull this from the view, throwing away this object
+ * and any children from rendering commands.
+ */
+ Always,
+ /**
+ * Never cull this from view, always draw it.
+ * Note that we will still get culled if our parent is culled.
+ */
+ Never;
+ }
+
+ /**
+ * Specifies if this spatial should be batched
+ */
+ public enum BatchHint {
+
+ /**
+ * Do whatever our parent does. If no parent, default to {@link #Always}.
+ */
+ Inherit,
+ /**
+ * This spatial will always be batched when attached to a BatchNode.
+ */
+ Always,
+ /**
+ * This spatial will never be batched when attached to a BatchNode.
+ */
+ Never;
+ }
+ /**
+ * Refresh flag types
+ */
+ protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
+ RF_BOUND = 0x02,
+ RF_LIGHTLIST = 0x04; // changes in light lists
+ protected CullHint cullHint = CullHint.Inherit;
+ protected BatchHint batchHint = BatchHint.Inherit;
+ /**
+ * Spatial's bounding volume relative to the world.
+ */
+ protected BoundingVolume worldBound;
+ /**
+ * LightList
+ */
+ protected LightList localLights;
+ protected transient LightList worldLights;
+ /**
+ * This spatial's name.
+ */
+ protected String name;
+ // scale values
+ protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects;
+ protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit;
+ protected ShadowMode shadowMode = RenderQueue.ShadowMode.Inherit;
+ public transient float queueDistance = Float.NEGATIVE_INFINITY;
+ protected Transform localTransform;
+ protected Transform worldTransform;
+ protected SafeArrayList<Control> controls = new SafeArrayList<Control>(Control.class);
+ protected HashMap<String, Savable> userData = null;
+ /**
+ * Used for smart asset caching
+ *
+ * @see AssetKey#useSmartCache()
+ */
+ protected AssetKey key;
+ /**
+ * Spatial's parent, or null if it has none.
+ */
+ protected transient Node parent;
+ /**
+ * Refresh flags. Indicate what data of the spatial need to be
+ * updated to reflect the correct state.
+ */
+ protected transient int refreshFlags = 0;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Spatial() {
+ localTransform = new Transform();
+ worldTransform = new Transform();
+
+ localLights = new LightList(this);
+ worldLights = new LightList(this);
+
+ refreshFlags |= RF_BOUND;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Spatial</code> object setting the
+ * rotation, translation and scale value to defaults.
+ *
+ * @param name
+ * the name of the scene element. This is required for
+ * identification and comparison purposes.
+ */
+ public Spatial(String name) {
+ this();
+ this.name = name;
+ }
+
+ public void setKey(AssetKey key) {
+ this.key = key;
+ }
+
+ public AssetKey getKey() {
+ return key;
+ }
+
+ /**
+ * Indicate that the transform of this spatial has changed and that
+ * a refresh is required.
+ */
+ protected void setTransformRefresh() {
+ refreshFlags |= RF_TRANSFORM;
+ setBoundRefresh();
+ }
+
+ protected void setLightListRefresh() {
+ refreshFlags |= RF_LIGHTLIST;
+ }
+
+ /**
+ * Indicate that the bounding of this spatial has changed and that
+ * a refresh is required.
+ */
+ protected void setBoundRefresh() {
+ refreshFlags |= RF_BOUND;
+
+ // XXX: Replace with a recursive call?
+ Spatial p = parent;
+ while (p != null) {
+ if ((p.refreshFlags & RF_BOUND) != 0) {
+ return;
+ }
+
+ p.refreshFlags |= RF_BOUND;
+ p = p.parent;
+ }
+ }
+
+ /**
+ * <code>checkCulling</code> checks the spatial with the camera to see if it
+ * should be culled.
+ * <p>
+ * This method is called by the renderer. Usually it should not be called
+ * directly.
+ *
+ * @param cam The camera to check against.
+ * @return true if inside or intersecting camera frustum
+ * (should be rendered), false if outside.
+ */
+ public boolean checkCulling(Camera cam) {
+ if (refreshFlags != 0) {
+ throw new IllegalStateException("Scene graph is not properly updated for rendering.\n"
+ + "State was changed after rootNode.updateGeometricState() call. \n"
+ + "Make sure you do not modify the scene from another thread!\n"
+ + "Problem spatial name: " + getName());
+ }
+
+ CullHint cm = getCullHint();
+ assert cm != CullHint.Inherit;
+ if (cm == Spatial.CullHint.Always) {
+ setLastFrustumIntersection(Camera.FrustumIntersect.Outside);
+ return false;
+ } else if (cm == Spatial.CullHint.Never) {
+ setLastFrustumIntersection(Camera.FrustumIntersect.Intersects);
+ return true;
+ }
+
+ // check to see if we can cull this node
+ frustrumIntersects = (parent != null ? parent.frustrumIntersects
+ : Camera.FrustumIntersect.Intersects);
+
+ if (frustrumIntersects == Camera.FrustumIntersect.Intersects) {
+ if (getQueueBucket() == Bucket.Gui) {
+ return cam.containsGui(getWorldBound());
+ } else {
+ frustrumIntersects = cam.contains(getWorldBound());
+ }
+ }
+
+ return frustrumIntersects != Camera.FrustumIntersect.Outside;
+ }
+
+ /**
+ * Sets the name of this spatial.
+ *
+ * @param name
+ * The spatial's new name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of this spatial.
+ *
+ * @return This spatial's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the local {@link LightList}, which are the lights
+ * that were directly attached to this <code>Spatial</code> through the
+ * {@link #addLight(com.jme3.light.Light) } and
+ * {@link #removeLight(com.jme3.light.Light) } methods.
+ *
+ * @return The local light list
+ */
+ public LightList getLocalLightList() {
+ return localLights;
+ }
+
+ /**
+ * Returns the world {@link LightList}, containing the lights
+ * combined from all this <code>Spatial's</code> parents up to and including
+ * this <code>Spatial</code>'s lights.
+ *
+ * @return The combined world light list
+ */
+ public LightList getWorldLightList() {
+ return worldLights;
+ }
+
+ /**
+ * <code>getWorldRotation</code> retrieves the absolute rotation of the
+ * Spatial.
+ *
+ * @return the Spatial's world rotation quaternion.
+ */
+ public Quaternion getWorldRotation() {
+ checkDoTransformUpdate();
+ return worldTransform.getRotation();
+ }
+
+ /**
+ * <code>getWorldTranslation</code> retrieves the absolute translation of
+ * the spatial.
+ *
+ * @return the Spatial's world tranlsation vector.
+ */
+ public Vector3f getWorldTranslation() {
+ checkDoTransformUpdate();
+ return worldTransform.getTranslation();
+ }
+
+ /**
+ * <code>getWorldScale</code> retrieves the absolute scale factor of the
+ * spatial.
+ *
+ * @return the Spatial's world scale factor.
+ */
+ public Vector3f getWorldScale() {
+ checkDoTransformUpdate();
+ return worldTransform.getScale();
+ }
+
+ /**
+ * <code>getWorldTransform</code> retrieves the world transformation
+ * of the spatial.
+ *
+ * @return the world transform.
+ */
+ public Transform getWorldTransform() {
+ checkDoTransformUpdate();
+ return worldTransform;
+ }
+
+ /**
+ * <code>rotateUpTo</code> is a utility function that alters the
+ * local rotation to point the Y axis in the direction given by newUp.
+ *
+ * @param newUp
+ * the up vector to use - assumed to be a unit vector.
+ */
+ public void rotateUpTo(Vector3f newUp) {
+ TempVars vars = TempVars.get();
+
+ Vector3f compVecA = vars.vect1;
+ Quaternion q = vars.quat1;
+
+ // First figure out the current up vector.
+ Vector3f upY = compVecA.set(Vector3f.UNIT_Y);
+ Quaternion rot = localTransform.getRotation();
+ rot.multLocal(upY);
+
+ // get angle between vectors
+ float angle = upY.angleBetween(newUp);
+
+ // figure out rotation axis by taking cross product
+ Vector3f rotAxis = upY.crossLocal(newUp).normalizeLocal();
+
+ // Build a rotation quat and apply current local rotation.
+ q.fromAngleNormalAxis(angle, rotAxis);
+ q.mult(rot, rot);
+
+ vars.release();
+
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>lookAt</code> is a convenience method for auto-setting the local
+ * rotation based on a position and an up vector. It computes the rotation
+ * to transform the z-axis to point onto 'position' and the y-axis to 'up'.
+ * Unlike {@link Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) }
+ * this method takes a world position to look at and not a relative direction.
+ *
+ * @param position
+ * where to look at in terms of world coordinates
+ * @param upVector
+ * a vector indicating the (local) up direction. (typically {0,
+ * 1, 0} in jME.)
+ */
+ public void lookAt(Vector3f position, Vector3f upVector) {
+ Vector3f worldTranslation = getWorldTranslation();
+
+ TempVars vars = TempVars.get();
+
+ Vector3f compVecA = vars.vect4;
+ vars.release();
+
+ compVecA.set(position).subtractLocal(worldTranslation);
+ getLocalRotation().lookAt(compVecA, upVector);
+
+ setTransformRefresh();
+ }
+
+ /**
+ * Should be overridden by Node and Geometry.
+ */
+ protected void updateWorldBound() {
+ // the world bound of a leaf is the same as it's model bound
+ // for a node, the world bound is a combination of all it's children
+ // bounds
+ // -> handled by subclass
+ refreshFlags &= ~RF_BOUND;
+ }
+
+ protected void updateWorldLightList() {
+ if (parent == null) {
+ worldLights.update(localLights, null);
+ refreshFlags &= ~RF_LIGHTLIST;
+ } else {
+ if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
+ worldLights.update(localLights, parent.worldLights);
+ refreshFlags &= ~RF_LIGHTLIST;
+ } else {
+ assert false;
+ }
+ }
+ }
+
+ /**
+ * Should only be called from updateGeometricState().
+ * In most cases should not be subclassed.
+ */
+ protected void updateWorldTransforms() {
+ if (parent == null) {
+ worldTransform.set(localTransform);
+ refreshFlags &= ~RF_TRANSFORM;
+ } else {
+ // check if transform for parent is updated
+ assert ((parent.refreshFlags & RF_TRANSFORM) == 0);
+ worldTransform.set(localTransform);
+ worldTransform.combineWithParent(parent.worldTransform);
+ refreshFlags &= ~RF_TRANSFORM;
+ }
+ }
+
+ /**
+ * Computes the world transform of this Spatial in the most
+ * efficient manner possible.
+ */
+ void checkDoTransformUpdate() {
+ if ((refreshFlags & RF_TRANSFORM) == 0) {
+ return;
+ }
+
+ if (parent == null) {
+ worldTransform.set(localTransform);
+ refreshFlags &= ~RF_TRANSFORM;
+ } else {
+ TempVars vars = TempVars.get();
+
+ Spatial[] stack = vars.spatialStack;
+ Spatial rootNode = this;
+ int i = 0;
+ while (true) {
+ Spatial hisParent = rootNode.parent;
+ if (hisParent == null) {
+ rootNode.worldTransform.set(rootNode.localTransform);
+ rootNode.refreshFlags &= ~RF_TRANSFORM;
+ i--;
+ break;
+ }
+
+ stack[i] = rootNode;
+
+ if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) {
+ break;
+ }
+
+ rootNode = hisParent;
+ i++;
+ }
+
+ vars.release();
+
+ for (int j = i; j >= 0; j--) {
+ rootNode = stack[j];
+ //rootNode.worldTransform.set(rootNode.localTransform);
+ //rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform);
+ //rootNode.refreshFlags &= ~RF_TRANSFORM;
+ rootNode.updateWorldTransforms();
+ }
+ }
+ }
+
+ /**
+ * Computes this Spatial's world bounding volume in the most efficient
+ * manner possible.
+ */
+ void checkDoBoundUpdate() {
+ if ((refreshFlags & RF_BOUND) == 0) {
+ return;
+ }
+
+ checkDoTransformUpdate();
+
+ // Go to children recursively and update their bound
+ if (this instanceof Node) {
+ Node node = (Node) this;
+ int len = node.getQuantity();
+ for (int i = 0; i < len; i++) {
+ Spatial child = node.getChild(i);
+ child.checkDoBoundUpdate();
+ }
+ }
+
+ // All children's bounds have been updated. Update my own now.
+ updateWorldBound();
+ }
+
+ private void runControlUpdate(float tpf) {
+ if (controls.isEmpty()) {
+ return;
+ }
+
+ for (Control c : controls.getArray()) {
+ c.update(tpf);
+ }
+ }
+
+ /**
+ * Called when the Spatial is about to be rendered, to notify
+ * controls attached to this Spatial using the Control.render() method.
+ *
+ * @param rm The RenderManager rendering the Spatial.
+ * @param vp The ViewPort to which the Spatial is being rendered to.
+ *
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ * @see Spatial#getControl(java.lang.Class)
+ */
+ public void runControlRender(RenderManager rm, ViewPort vp) {
+ if (controls.isEmpty()) {
+ return;
+ }
+
+ for (Control c : controls.getArray()) {
+ c.render(rm, vp);
+ }
+ }
+
+ /**
+ * Add a control to the list of controls.
+ * @param control The control to add.
+ *
+ * @see Spatial#removeControl(java.lang.Class)
+ */
+ public void addControl(Control control) {
+ controls.add(control);
+ control.setSpatial(this);
+ }
+
+ /**
+ * Removes the first control that is an instance of the given class.
+ *
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ */
+ public void removeControl(Class<? extends Control> controlType) {
+ for (int i = 0; i < controls.size(); i++) {
+ if (controlType.isAssignableFrom(controls.get(i).getClass())) {
+ Control control = controls.remove(i);
+ control.setSpatial(null);
+ }
+ }
+ }
+
+ /**
+ * Removes the given control from this spatial's controls.
+ *
+ * @param control The control to remove
+ * @return True if the control was successfuly removed. False if
+ * the control is not assigned to this spatial.
+ *
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ */
+ public boolean removeControl(Control control) {
+ boolean result = controls.remove(control);
+ if (result) {
+ control.setSpatial(null);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the first control that is an instance of the given class,
+ * or null if no such control exists.
+ *
+ * @param controlType The superclass of the control to look for.
+ * @return The first instance in the list of the controlType class, or null.
+ *
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ */
+ public <T extends Control> T getControl(Class<T> controlType) {
+ for (Control c : controls.getArray()) {
+ if (controlType.isAssignableFrom(c.getClass())) {
+ return (T) c;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the control at the given index in the list.
+ *
+ * @param index The index of the control in the list to find.
+ * @return The control at the given index.
+ *
+ * @throws IndexOutOfBoundsException
+ * If the index is outside the range [0, getNumControls()-1]
+ *
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ */
+ public Control getControl(int index) {
+ return controls.get(index);
+ }
+
+ /**
+ * @return The number of controls attached to this Spatial.
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ * @see Spatial#removeControl(java.lang.Class)
+ */
+ public int getNumControls() {
+ return controls.size();
+ }
+
+ /**
+ * <code>updateLogicalState</code> calls the <code>update()</code> method
+ * for all controls attached to this Spatial.
+ *
+ * @param tpf Time per frame.
+ *
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
+ */
+ public void updateLogicalState(float tpf) {
+ runControlUpdate(tpf);
+ }
+
+ /**
+ * <code>updateGeometricState</code> updates the lightlist,
+ * computes the world transforms, and computes the world bounds
+ * for this Spatial.
+ * Calling this when the Spatial is attached to a node
+ * will cause undefined results. User code should only call this
+ * method on Spatials having no parent.
+ *
+ * @see Spatial#getWorldLightList()
+ * @see Spatial#getWorldTransform()
+ * @see Spatial#getWorldBound()
+ */
+ public void updateGeometricState() {
+ // assume that this Spatial is a leaf, a proper implementation
+ // for this method should be provided by Node.
+
+ // NOTE: Update world transforms first because
+ // bound transform depends on them.
+ if ((refreshFlags & RF_LIGHTLIST) != 0) {
+ updateWorldLightList();
+ }
+ if ((refreshFlags & RF_TRANSFORM) != 0) {
+ updateWorldTransforms();
+ }
+ if ((refreshFlags & RF_BOUND) != 0) {
+ updateWorldBound();
+ }
+
+ assert refreshFlags == 0;
+ }
+
+ /**
+ * Convert a vector (in) from this spatials' local coordinate space to world
+ * coordinate space.
+ *
+ * @param in
+ * vector to read from
+ * @param store
+ * where to write the result (null to create a new vector, may be
+ * same as in)
+ * @return the result (store)
+ */
+ public Vector3f localToWorld(final Vector3f in, Vector3f store) {
+ checkDoTransformUpdate();
+ return worldTransform.transformVector(in, store);
+ }
+
+ /**
+ * Convert a vector (in) from world coordinate space to this spatials' local
+ * coordinate space.
+ *
+ * @param in
+ * vector to read from
+ * @param store
+ * where to write the result
+ * @return the result (store)
+ */
+ public Vector3f worldToLocal(final Vector3f in, final Vector3f store) {
+ checkDoTransformUpdate();
+ return worldTransform.transformInverseVector(in, store);
+ }
+
+ /**
+ * <code>getParent</code> retrieves this node's parent. If the parent is
+ * null this is the root node.
+ *
+ * @return the parent of this node.
+ */
+ public Node getParent() {
+ return parent;
+ }
+
+ /**
+ * Called by {@link Node#attachChild(Spatial)} and
+ * {@link Node#detachChild(Spatial)} - don't call directly.
+ * <code>setParent</code> sets the parent of this node.
+ *
+ * @param parent
+ * the parent of this node.
+ */
+ protected void setParent(Node parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * <code>removeFromParent</code> removes this Spatial from it's parent.
+ *
+ * @return true if it has a parent and performed the remove.
+ */
+ public boolean removeFromParent() {
+ if (parent != null) {
+ parent.detachChild(this);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * determines if the provided Node is the parent, or parent's parent, etc. of this Spatial.
+ *
+ * @param ancestor
+ * the ancestor object to look for.
+ * @return true if the ancestor is found, false otherwise.
+ */
+ public boolean hasAncestor(Node ancestor) {
+ if (parent == null) {
+ return false;
+ } else if (parent.equals(ancestor)) {
+ return true;
+ } else {
+ return parent.hasAncestor(ancestor);
+ }
+ }
+
+ /**
+ * <code>getLocalRotation</code> retrieves the local rotation of this
+ * node.
+ *
+ * @return the local rotation of this node.
+ */
+ public Quaternion getLocalRotation() {
+ return localTransform.getRotation();
+ }
+
+ /**
+ * <code>setLocalRotation</code> sets the local rotation of this node
+ * by using a {@link Matrix3f}.
+ *
+ * @param rotation
+ * the new local rotation.
+ */
+ public void setLocalRotation(Matrix3f rotation) {
+ localTransform.getRotation().fromRotationMatrix(rotation);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>setLocalRotation</code> sets the local rotation of this node.
+ *
+ * @param quaternion
+ * the new local rotation.
+ */
+ public void setLocalRotation(Quaternion quaternion) {
+ localTransform.setRotation(quaternion);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>getLocalScale</code> retrieves the local scale of this node.
+ *
+ * @return the local scale of this node.
+ */
+ public Vector3f getLocalScale() {
+ return localTransform.getScale();
+ }
+
+ /**
+ * <code>setLocalScale</code> sets the local scale of this node.
+ *
+ * @param localScale
+ * the new local scale, applied to x, y and z
+ */
+ public void setLocalScale(float localScale) {
+ localTransform.setScale(localScale);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>setLocalScale</code> sets the local scale of this node.
+ */
+ public void setLocalScale(float x, float y, float z) {
+ localTransform.setScale(x, y, z);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>setLocalScale</code> sets the local scale of this node.
+ *
+ * @param localScale
+ * the new local scale.
+ */
+ public void setLocalScale(Vector3f localScale) {
+ localTransform.setScale(localScale);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>getLocalTranslation</code> retrieves the local translation of
+ * this node.
+ *
+ * @return the local translation of this node.
+ */
+ public Vector3f getLocalTranslation() {
+ return localTransform.getTranslation();
+ }
+
+ /**
+ * <code>setLocalTranslation</code> sets the local translation of this
+ * spatial.
+ *
+ * @param localTranslation
+ * the local translation of this spatial.
+ */
+ public void setLocalTranslation(Vector3f localTranslation) {
+ this.localTransform.setTranslation(localTranslation);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>setLocalTranslation</code> sets the local translation of this
+ * spatial.
+ */
+ public void setLocalTranslation(float x, float y, float z) {
+ this.localTransform.setTranslation(x, y, z);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>setLocalTransform</code> sets the local transform of this
+ * spatial.
+ */
+ public void setLocalTransform(Transform t) {
+ this.localTransform.set(t);
+ setTransformRefresh();
+ }
+
+ /**
+ * <code>getLocalTransform</code> retrieves the local transform of
+ * this spatial.
+ *
+ * @return the local transform of this spatial.
+ */
+ public Transform getLocalTransform() {
+ return localTransform;
+ }
+
+ /**
+ * Applies the given material to the Spatial, this will propagate the
+ * material down to the geometries in the scene graph.
+ *
+ * @param material The material to set.
+ */
+ public void setMaterial(Material material) {
+ }
+
+ /**
+ * <code>addLight</code> adds the given light to the Spatial; causing
+ * all child Spatials to be effected by it.
+ *
+ * @param light The light to add.
+ */
+ public void addLight(Light light) {
+ localLights.add(light);
+ setLightListRefresh();
+ }
+
+ /**
+ * <code>removeLight</code> removes the given light from the Spatial.
+ *
+ * @param light The light to remove.
+ * @see Spatial#addLight(com.jme3.light.Light)
+ */
+ public void removeLight(Light light) {
+ localLights.remove(light);
+ setLightListRefresh();
+ }
+
+ /**
+ * Translates the spatial by the given translation vector.
+ *
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial move(float x, float y, float z) {
+ this.localTransform.getTranslation().addLocal(x, y, z);
+ setTransformRefresh();
+
+ return this;
+ }
+
+ /**
+ * Translates the spatial by the given translation vector.
+ *
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial move(Vector3f offset) {
+ this.localTransform.getTranslation().addLocal(offset);
+ setTransformRefresh();
+
+ return this;
+ }
+
+ /**
+ * Scales the spatial by the given value
+ *
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial scale(float s) {
+ return scale(s, s, s);
+ }
+
+ /**
+ * Scales the spatial by the given scale vector.
+ *
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial scale(float x, float y, float z) {
+ this.localTransform.getScale().multLocal(x, y, z);
+ setTransformRefresh();
+
+ return this;
+ }
+
+ /**
+ * Rotates the spatial by the given rotation.
+ *
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial rotate(Quaternion rot) {
+ this.localTransform.getRotation().multLocal(rot);
+ setTransformRefresh();
+
+ return this;
+ }
+
+ /**
+ * Rotates the spatial by the yaw, roll and pitch angles (in radians),
+ * in the local coordinate space.
+ *
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial rotate(float yaw, float roll, float pitch) {
+ TempVars vars = TempVars.get();
+ Quaternion q = vars.quat1;
+ q.fromAngles(yaw, roll, pitch);
+ rotate(q);
+ vars.release();
+
+ return this;
+ }
+
+ /**
+ * Centers the spatial in the origin of the world bound.
+ * @return The spatial on which this method is called, e.g <code>this</code>.
+ */
+ public Spatial center() {
+ Vector3f worldTrans = getWorldTranslation();
+ Vector3f worldCenter = getWorldBound().getCenter();
+
+ Vector3f absTrans = worldTrans.subtract(worldCenter);
+ setLocalTranslation(absTrans);
+
+ return this;
+ }
+
+ /**
+ * @see #setCullHint(CullHint)
+ * @return the cull mode of this spatial, or if set to CullHint.Inherit,
+ * the cullmode of it's parent.
+ */
+ public CullHint getCullHint() {
+ if (cullHint != CullHint.Inherit) {
+ return cullHint;
+ } else if (parent != null) {
+ return parent.getCullHint();
+ } else {
+ return CullHint.Dynamic;
+ }
+ }
+
+ public BatchHint getBatchHint() {
+ if (batchHint != BatchHint.Inherit) {
+ return batchHint;
+ } else if (parent != null) {
+ return parent.getBatchHint();
+ } else {
+ return BatchHint.Always;
+ }
+ }
+
+ /**
+ * Returns this spatial's renderqueue bucket. If the mode is set to inherit,
+ * then the spatial gets its renderqueue bucket from its parent.
+ *
+ * @return The spatial's current renderqueue mode.
+ */
+ public RenderQueue.Bucket getQueueBucket() {
+ if (queueBucket != RenderQueue.Bucket.Inherit) {
+ return queueBucket;
+ } else if (parent != null) {
+ return parent.getQueueBucket();
+ } else {
+ return RenderQueue.Bucket.Opaque;
+ }
+ }
+
+ /**
+ * @return The shadow mode of this spatial, if the local shadow
+ * mode is set to inherit, then the parent's shadow mode is returned.
+ *
+ * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode)
+ * @see ShadowMode
+ */
+ public RenderQueue.ShadowMode getShadowMode() {
+ if (shadowMode != RenderQueue.ShadowMode.Inherit) {
+ return shadowMode;
+ } else if (parent != null) {
+ return parent.getShadowMode();
+ } else {
+ return ShadowMode.Off;
+ }
+ }
+
+ /**
+ * Sets the level of detail to use when rendering this Spatial,
+ * this call propagates to all geometries under this Spatial.
+ *
+ * @param lod The lod level to set.
+ */
+ public void setLodLevel(int lod) {
+ }
+
+ /**
+ * <code>updateModelBound</code> recalculates the bounding object for this
+ * Spatial.
+ */
+ public abstract void updateModelBound();
+
+ /**
+ * <code>setModelBound</code> sets the bounding object for this Spatial.
+ *
+ * @param modelBound
+ * the bounding object for this spatial.
+ */
+ public abstract void setModelBound(BoundingVolume modelBound);
+
+ /**
+ * @return The sum of all verticies under this Spatial.
+ */
+ public abstract int getVertexCount();
+
+ /**
+ * @return The sum of all triangles under this Spatial.
+ */
+ public abstract int getTriangleCount();
+
+ /**
+ * @return A clone of this Spatial, the scene graph in its entirety
+ * is cloned and can be altered independently of the original scene graph.
+ *
+ * Note that meshes of geometries are not cloned explicitly, they
+ * are shared if static, or specially cloned if animated.
+ *
+ * All controls will be cloned using the Control.cloneForSpatial method
+ * on the clone.
+ *
+ * @see Mesh#cloneForAnim()
+ */
+ public Spatial clone(boolean cloneMaterial) {
+ try {
+ Spatial clone = (Spatial) super.clone();
+ if (worldBound != null) {
+ clone.worldBound = worldBound.clone();
+ }
+ clone.worldLights = worldLights.clone();
+ clone.localLights = localLights.clone();
+
+ // Set the new owner of the light lists
+ clone.localLights.setOwner(clone);
+ clone.worldLights.setOwner(clone);
+
+ // No need to force cloned to update.
+ // This node already has the refresh flags
+ // set below so it will have to update anyway.
+ clone.worldTransform = worldTransform.clone();
+ clone.localTransform = localTransform.clone();
+
+ if (clone instanceof Node) {
+ Node node = (Node) this;
+ Node nodeClone = (Node) clone;
+ nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
+ for (Spatial child : node.children) {
+ Spatial childClone = child.clone(cloneMaterial);
+ childClone.parent = nodeClone;
+ nodeClone.children.add(childClone);
+ }
+ }
+
+ clone.parent = null;
+ clone.setBoundRefresh();
+ clone.setTransformRefresh();
+ clone.setLightListRefresh();
+
+ clone.controls = new SafeArrayList<Control>(Control.class);
+ for (int i = 0; i < controls.size(); i++) {
+ clone.controls.add(controls.get(i).cloneForSpatial(clone));
+ }
+
+ if (userData != null) {
+ clone.userData = (HashMap<String, Savable>) userData.clone();
+ }
+
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * @return A clone of this Spatial, the scene graph in its entirety
+ * is cloned and can be altered independently of the original scene graph.
+ *
+ * Note that meshes of geometries are not cloned explicitly, they
+ * are shared if static, or specially cloned if animated.
+ *
+ * All controls will be cloned using the Control.cloneForSpatial method
+ * on the clone.
+ *
+ * @see Mesh#cloneForAnim()
+ */
+ @Override
+ public Spatial clone() {
+ return clone(true);
+ }
+
+ /**
+ * @return Similar to Spatial.clone() except will create a deep clone
+ * of all geometry's meshes, normally this method shouldn't be used
+ * instead use Spatial.clone()
+ *
+ * @see Spatial#clone()
+ */
+ public abstract Spatial deepClone();
+
+ public void setUserData(String key, Object data) {
+ if (userData == null) {
+ userData = new HashMap<String, Savable>();
+ }
+
+ if (data instanceof Savable) {
+ userData.put(key, (Savable) data);
+ } else {
+ userData.put(key, new UserData(UserData.getObjectType(data), data));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getUserData(String key) {
+ if (userData == null) {
+ return null;
+ }
+
+ Savable s = userData.get(key);
+ if (s instanceof UserData) {
+ return (T) ((UserData) s).getValue();
+ } else {
+ return (T) s;
+ }
+ }
+
+ public Collection<String> getUserDataKeys() {
+ if (userData != null) {
+ return userData.keySet();
+ }
+
+ return Collections.EMPTY_SET;
+ }
+
+ /**
+ * Note that we are <i>matching</i> the pattern, therefore the pattern
+ * must match the entire pattern (i.e. it behaves as if it is sandwiched
+ * between "^" and "$").
+ * You can set regex modes, like case insensitivity, by using the (?X)
+ * or (?X:Y) constructs.
+ *
+ * @param spatialSubclass Subclass which this must implement.
+ * Null causes all Spatials to qualify.
+ * @param nameRegex Regular expression to match this name against.
+ * Null causes all Names to qualify.
+ * @return true if this implements the specified class and this's name
+ * matches the specified pattern.
+ *
+ * @see java.util.regex.Pattern
+ */
+ public boolean matches(Class<? extends Spatial> spatialSubclass,
+ String nameRegex) {
+ if (spatialSubclass != null && !spatialSubclass.isInstance(this)) {
+ return false;
+ }
+
+ if (nameRegex != null && (name == null || !name.matches(nameRegex))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(name, "name", null);
+ capsule.write(worldBound, "world_bound", null);
+ capsule.write(cullHint, "cull_mode", CullHint.Inherit);
+ capsule.write(batchHint, "batch_hint", BatchHint.Inherit);
+ capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit);
+ capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
+ capsule.write(localTransform, "transform", Transform.IDENTITY);
+ capsule.write(localLights, "lights", null);
+
+ // Shallow clone the controls array to convert its type.
+ capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
+ capsule.writeStringSavableMap(userData, "user_data", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+
+ name = ic.readString("name", null);
+ worldBound = (BoundingVolume) ic.readSavable("world_bound", null);
+ cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit);
+ batchHint = ic.readEnum("batch_hint", BatchHint.class, BatchHint.Inherit);
+ queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class,
+ RenderQueue.Bucket.Inherit);
+ shadowMode = ic.readEnum("shadow_mode", ShadowMode.class,
+ ShadowMode.Inherit);
+
+ localTransform = (Transform) ic.readSavable("transform", Transform.IDENTITY);
+
+ localLights = (LightList) ic.readSavable("lights", null);
+ localLights.setOwner(this);
+
+ //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
+ //the AnimControl creates the SkeletonControl for old files and add it to the spatial.
+ //The SkeletonControl must be the last in the stack so we add the list of all other control before it.
+ //When backward compatibility won't be needed anymore this can be replaced by :
+ //controls = ic.readSavableArrayList("controlsList", null));
+ controls.addAll(0, ic.readSavableArrayList("controlsList", null));
+
+ userData = (HashMap<String, Savable>) ic.readStringSavableMap("user_data", null);
+ }
+
+ /**
+ * <code>getWorldBound</code> retrieves the world bound at this node
+ * level.
+ *
+ * @return the world bound at this level.
+ */
+ public BoundingVolume getWorldBound() {
+ checkDoBoundUpdate();
+ return worldBound;
+ }
+
+ /**
+ * <code>setCullHint</code> sets how scene culling should work on this
+ * spatial during drawing. NOTE: You must set this AFTER attaching to a
+ * parent or it will be reset with the parent's cullMode value.
+ *
+ * @param hint
+ * one of CullHint.Dynamic, CullHint.Always, CullHint.Inherit or
+ * CullHint.Never
+ */
+ public void setCullHint(CullHint hint) {
+ cullHint = hint;
+ }
+
+ /**
+ * <code>setBatchHint</code> sets how batching should work on this
+ * spatial. NOTE: You must set this AFTER attaching to a
+ * parent or it will be reset with the parent's cullMode value.
+ *
+ * @param hint
+ * one of BatchHint.Never, BatchHint.Always, BatchHint.Inherit
+ */
+ public void setBatchHint(BatchHint hint) {
+ batchHint = hint;
+ }
+
+ /**
+ * @return the cullmode set on this Spatial
+ */
+ public CullHint getLocalCullHint() {
+ return cullHint;
+ }
+
+ /**
+ * @return the batchHint set on this Spatial
+ */
+ public BatchHint getLocalBatchHint() {
+ return batchHint;
+ }
+
+ /**
+ * <code>setQueueBucket</code> determines at what phase of the
+ * rendering process this Spatial will rendered. See the
+ * {@link Bucket} enum for an explanation of the various
+ * render queue buckets.
+ *
+ * @param queueBucket
+ * The bucket to use for this Spatial.
+ */
+ public void setQueueBucket(RenderQueue.Bucket queueBucket) {
+ this.queueBucket = queueBucket;
+ }
+
+ /**
+ * Sets the shadow mode of the spatial
+ * The shadow mode determines how the spatial should be shadowed,
+ * when a shadowing technique is used. See the
+ * documentation for the class {@link ShadowMode} for more information.
+ *
+ * @see ShadowMode
+ *
+ * @param shadowMode The local shadow mode to set.
+ */
+ public void setShadowMode(RenderQueue.ShadowMode shadowMode) {
+ this.shadowMode = shadowMode;
+ }
+
+ /**
+ * @return The locally set queue bucket mode
+ *
+ * @see Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket)
+ */
+ public RenderQueue.Bucket getLocalQueueBucket() {
+ return queueBucket;
+ }
+
+ /**
+ * @return The locally set shadow mode
+ *
+ * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode)
+ */
+ public RenderQueue.ShadowMode getLocalShadowMode() {
+ return shadowMode;
+ }
+
+ /**
+ * Returns this spatial's last frustum intersection result. This int is set
+ * when a check is made to determine if the bounds of the object fall inside
+ * a camera's frustum. If a parent is found to fall outside the frustum, the
+ * value for this spatial will not be updated.
+ *
+ * @return The spatial's last frustum intersection result.
+ */
+ public Camera.FrustumIntersect getLastFrustumIntersection() {
+ return frustrumIntersects;
+ }
+
+ /**
+ * Overrides the last intersection result. This is useful for operations
+ * that want to start rendering at the middle of a scene tree and don't want
+ * the parent of that node to influence culling.
+ *
+ * @param intersects
+ * the new value
+ */
+ public void setLastFrustumIntersection(Camera.FrustumIntersect intersects) {
+ frustrumIntersects = intersects;
+ }
+
+ /**
+ * Returns the Spatial's name followed by the class of the spatial <br>
+ * Example: "MyNode (com.jme3.scene.Spatial)
+ *
+ * @return Spatial's name followed by the class of the Spatial
+ */
+ @Override
+ public String toString() {
+ return name + " (" + this.getClass().getSimpleName() + ')';
+ }
+
+ /**
+ * Creates a transform matrix that will convert from this spatials'
+ * local coordinate space to the world coordinate space
+ * based on the world transform.
+ *
+ * @param store Matrix where to store the result, if null, a new one
+ * will be created and returned.
+ *
+ * @return store if not null, otherwise, a new matrix containing the result.
+ *
+ * @see Spatial#getWorldTransform()
+ */
+ public Matrix4f getLocalToWorldMatrix(Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ } else {
+ store.loadIdentity();
+ }
+ // multiply with scale first, then rotate, finally translate (cf.
+ // Eberly)
+ store.scale(getWorldScale());
+ store.multLocal(getWorldRotation());
+ store.setTranslation(getWorldTranslation());
+ return store;
+ }
+
+ /**
+ * Visit each scene graph element ordered by DFS
+ * @param visitor
+ */
+ public abstract void depthFirstTraversal(SceneGraphVisitor visitor);
+
+ /**
+ * Visit each scene graph element ordered by BFS
+ * @param visitor
+ */
+ public void breadthFirstTraversal(SceneGraphVisitor visitor) {
+ Queue<Spatial> queue = new LinkedList<Spatial>();
+ queue.add(this);
+
+ while (!queue.isEmpty()) {
+ Spatial s = queue.poll();
+ visitor.visit(s);
+ s.breadthFirstTraversal(visitor, queue);
+ }
+ }
+
+ protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue);
+}
diff --git a/engine/src/core/com/jme3/scene/UserData.java b/engine/src/core/com/jme3/scene/UserData.java
new file mode 100644
index 0000000..855a31c
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/UserData.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+/**
+ * <code>UserData</code> is used to contain user data objects
+ * set on spatials (primarily primitives) that do not implement
+ * the {@link Savable} interface. Note that attempting
+ * to export any models which have non-savable objects
+ * attached to them will fail.
+ */
+public final class UserData implements Savable {
+
+ /**
+ * Boolean type on Geometries to indicate that physics collision
+ * shape generation should ignore them.
+ */
+ public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore";
+
+ /**
+ * For geometries using shared mesh, this will specify the shared
+ * mesh reference.
+ */
+ public static final String JME_SHAREDMESH = "JmeSharedMesh";
+
+ protected byte type;
+ protected Object value;
+
+ public UserData() {
+ }
+
+ /**
+ * Creates a new <code>UserData</code> with the given
+ * type and value.
+ *
+ * @param type Type of data, should be between 0 and 4.
+ * @param value Value of the data
+ */
+ public UserData(byte type, Object value) {
+ assert type >= 0 && type <= 4;
+ this.type = type;
+ this.value = value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+
+ public static byte getObjectType(Object type) {
+ if (type instanceof Integer) {
+ return 0;
+ } else if (type instanceof Float) {
+ return 1;
+ } else if (type instanceof Boolean) {
+ return 2;
+ } else if (type instanceof String) {
+ return 3;
+ } else if (type instanceof Long) {
+ return 4;
+ } else {
+ throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName());
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(type, "type", (byte)0);
+
+ switch (type) {
+ case 0:
+ int i = (Integer) value;
+ oc.write(i, "intVal", 0);
+ break;
+ case 1:
+ float f = (Float) value;
+ oc.write(f, "floatVal", 0f);
+ break;
+ case 2:
+ boolean b = (Boolean) value;
+ oc.write(b, "boolVal", false);
+ break;
+ case 3:
+ String s = (String) value;
+ oc.write(s, "strVal", null);
+ break;
+ case 4:
+ Long l = (Long) value;
+ oc.write(l, "longVal", 0l);
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ type = ic.readByte("type", (byte) 0);
+
+ switch (type) {
+ case 0:
+ value = ic.readInt("intVal", 0);
+ break;
+ case 1:
+ value = ic.readFloat("floatVal", 0f);
+ break;
+ case 2:
+ value = ic.readBoolean("boolVal", false);
+ break;
+ case 3:
+ value = ic.readString("strVal", null);
+ break;
+ case 4:
+ value = ic.readLong("longVal", 0l);
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/VertexBuffer.java b/engine/src/core/com/jme3/scene/VertexBuffer.java
new file mode 100644
index 0000000..1a844bb
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/VertexBuffer.java
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene;
+
+import com.jme3.export.*;
+import com.jme3.math.FastMath;
+import com.jme3.renderer.Renderer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.NativeObject;
+import java.io.IOException;
+import java.nio.*;
+
+/**
+ * A <code>VertexBuffer</code> contains a particular type of geometry
+ * data used by {@link Mesh}es. Every VertexBuffer set on a <code>Mesh</code>
+ * is sent as an attribute to the vertex shader to be processed.
+ * <p>
+ * Several terms are used throughout the javadoc for this class, explanation:
+ * <ul>
+ * <li>Element - A single element is the largest individual object
+ * inside a VertexBuffer. E.g. if the VertexBuffer is used to store 3D position
+ * data, then an element will be a single 3D vector.</li>
+ * <li>Component - A component represents the parts inside an element.
+ * For a 3D vector, a single component is one of the dimensions, X, Y or Z.</li>
+ * </ul>
+ */
+public class VertexBuffer extends NativeObject implements Savable, Cloneable {
+
+ /**
+ * Type of buffer. Specifies the actual attribute it defines.
+ */
+ public static enum Type {
+ /**
+ * Position of the vertex (3 floats)
+ */
+ Position,
+
+ /**
+ * The size of the point when using point buffers (float).
+ */
+ Size,
+
+ /**
+ * Normal vector, normalized (3 floats).
+ */
+ Normal,
+
+ /**
+ * Texture coordinate (2 float)
+ */
+ TexCoord,
+
+ /**
+ * Color and Alpha (4 floats)
+ */
+ Color,
+
+ /**
+ * Tangent vector, normalized (4 floats) (x,y,z,w)
+ * the w component is called the binormal parity, is not normalized and is either 1f or -1f
+ * It's used to compuste the direction on the binormal verctor on the GPU at render time.
+ */
+ Tangent,
+
+ /**
+ * Binormal vector, normalized (3 floats, optional)
+ */
+ Binormal,
+
+ /**
+ * Specifies the source data for various vertex buffers
+ * when interleaving is used. By default the format is
+ * byte.
+ */
+ InterleavedData,
+
+ /**
+ * Do not use.
+ */
+ @Deprecated
+ MiscAttrib,
+
+ /**
+ * Specifies the index buffer, must contain integer data
+ * (ubyte, ushort, or uint).
+ */
+ Index,
+
+ /**
+ * Initial vertex position, used with animation.
+ * Should have the same format and size as {@link Type#Position}.
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BindPosePosition,
+
+ /**
+ * Initial vertex normals, used with animation.
+ * Should have the same format and size as {@link Type#Normal}.
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BindPoseNormal,
+
+ /**
+ * Bone weights, used with animation (4 floats).
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BoneWeight,
+
+ /**
+ * Bone indices, used with animation (4 ubytes).
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BoneIndex,
+
+ /**
+ * Texture coordinate #2
+ */
+ TexCoord2,
+
+ /**
+ * Texture coordinate #3
+ */
+ TexCoord3,
+
+ /**
+ * Texture coordinate #4
+ */
+ TexCoord4,
+
+ /**
+ * Texture coordinate #5
+ */
+ TexCoord5,
+
+ /**
+ * Texture coordinate #6
+ */
+ TexCoord6,
+
+ /**
+ * Texture coordinate #7
+ */
+ TexCoord7,
+
+ /**
+ * Texture coordinate #8
+ */
+ TexCoord8,
+
+ /**
+ * Initial vertex tangents, used with animation.
+ * Should have the same format and size as {@link Type#Tangent}.
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BindPoseTangent,
+ }
+
+ /**
+ * The usage of the VertexBuffer, specifies how often the buffer
+ * is used. This can determine if a vertex buffer is placed in VRAM
+ * or held in video memory, but no guarantees are made- it's only a hint.
+ */
+ public static enum Usage {
+
+ /**
+ * Mesh data is sent once and very rarely updated.
+ */
+ Static,
+
+ /**
+ * Mesh data is updated occasionally (once per frame or less).
+ */
+ Dynamic,
+
+ /**
+ * Mesh data is updated every frame.
+ */
+ Stream,
+
+ /**
+ * Mesh data is <em>not</em> sent to GPU at all. It is only
+ * used by the CPU.
+ */
+ CpuOnly;
+ }
+
+ /**
+ * Specifies format of the data stored in the buffer.
+ * This should directly correspond to the buffer's class, for example,
+ * an {@link Format#UnsignedShort} formatted buffer should use the
+ * class {@link ShortBuffer} (e.g. the closest resembling type).
+ * For the {@link Format#Half} type, {@link ByteBuffer}s should
+ * be used.
+ */
+ public static enum Format {
+ /**
+ * Half precision floating point.
+ * 2 bytes, signed.
+ */
+ Half(2),
+
+ /**
+ * Single precision floating point.
+ * 4 bytes, signed
+ */
+ Float(4),
+
+ /**
+ * Double precision floating point.
+ * 8 bytes, signed. May not
+ * be supported by all GPUs.
+ */
+ Double(8),
+
+ /**
+ * 1 byte integer, signed.
+ */
+ Byte(1),
+
+ /**
+ * 1 byte integer, unsigned.
+ */
+ UnsignedByte(1),
+
+ /**
+ * 2 byte integer, signed.
+ */
+ Short(2),
+
+ /**
+ * 2 byte integer, unsigned.
+ */
+ UnsignedShort(2),
+
+ /**
+ * 4 byte integer, signed.
+ */
+ Int(4),
+
+ /**
+ * 4 byte integer, unsigned.
+ */
+ UnsignedInt(4);
+
+ private int componentSize = 0;
+
+ Format(int componentSize){
+ this.componentSize = componentSize;
+ }
+
+ /**
+ * Returns the size in bytes of this data type.
+ *
+ * @return Size in bytes of this data type.
+ */
+ public int getComponentSize(){
+ return componentSize;
+ }
+ }
+
+ protected int offset = 0;
+ protected int lastLimit = 0;
+ protected int stride = 0;
+ protected int components = 0;
+
+ /**
+ * derived from components * format.getComponentSize()
+ */
+ protected transient int componentsLength = 0;
+ protected Buffer data = null;
+ protected Usage usage;
+ protected Type bufType;
+ protected Format format;
+ protected boolean normalized = false;
+ protected transient boolean dataSizeChanged = false;
+
+ /**
+ * Creates an empty, uninitialized buffer.
+ * Must call setupData() to initialize.
+ */
+ public VertexBuffer(Type type){
+ super(VertexBuffer.class);
+ this.bufType = type;
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public VertexBuffer(){
+ super(VertexBuffer.class);
+ }
+
+ protected VertexBuffer(int id){
+ super(VertexBuffer.class, id);
+ }
+
+ /**
+ * @return The offset after which the data is sent to the GPU.
+ *
+ * @see #setOffset(int)
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * @param offset Specify the offset (in bytes) from the start of the buffer
+ * after which the data is sent to the GPU.
+ */
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * @return The stride (in bytes) for the data.
+ *
+ * @see #setStride(int)
+ */
+ public int getStride() {
+ return stride;
+ }
+
+ /**
+ * Set the stride (in bytes) for the data.
+ * <p>
+ * If the data is packed in the buffer, then stride is 0, if there's other
+ * data that is between the current component and the next component in the
+ * buffer, then this specifies the size in bytes of that additional data.
+ *
+ * @param stride the stride (in bytes) for the data
+ */
+ public void setStride(int stride) {
+ this.stride = stride;
+ }
+
+ /**
+ * Returns the raw internal data buffer used by this VertexBuffer.
+ * This buffer is not safe to call from multiple threads since buffers
+ * have their own internal position state that cannot be shared.
+ * Call getData().duplicate(), getData().asReadOnlyBuffer(), or
+ * the more convenient getDataReadOnly() if the buffer may be accessed
+ * from multiple threads.
+ *
+ * @return A native buffer, in the specified {@link Format format}.
+ */
+ public Buffer getData(){
+ return data;
+ }
+
+ /**
+ * Returns a safe read-only version of this VertexBuffer's data. The
+ * contents of the buffer will reflect whatever changes are made on
+ * other threads (eventually) but these should not be used in that way.
+ * This method provides a read-only buffer that is safe to _read_ from
+ * a separate thread since it has its own book-keeping state (position, limit, etc.)
+ *
+ * @return A rewound native buffer in the specified {@link Format format}
+ * that is safe to read from a separate thread from other readers.
+ */
+ public Buffer getDataReadOnly() {
+
+ if (data == null) {
+ return null;
+ }
+
+ // Create a read-only duplicate(). Note: this does not copy
+ // the underlying memory, it just creates a new read-only wrapper
+ // with its own buffer position state.
+
+ // Unfortunately, this is not 100% straight forward since Buffer
+ // does not have an asReadOnlyBuffer() method.
+ Buffer result;
+ if( data instanceof ByteBuffer ) {
+ result = ((ByteBuffer)data).asReadOnlyBuffer();
+ } else if( data instanceof FloatBuffer ) {
+ result = ((FloatBuffer)data).asReadOnlyBuffer();
+ } else if( data instanceof ShortBuffer ) {
+ result = ((ShortBuffer)data).asReadOnlyBuffer();
+ } else if( data instanceof IntBuffer ) {
+ result = ((IntBuffer)data).asReadOnlyBuffer();
+ } else {
+ throw new UnsupportedOperationException( "Cannot get read-only view of buffer type:" + data );
+ }
+
+ // Make sure the caller gets a consistent view since we may
+ // have grabbed this buffer while another thread was reading
+ // the raw data.
+ result.rewind();
+
+ return result;
+ }
+
+ /**
+ * @return The usage of this buffer. See {@link Usage} for more
+ * information.
+ */
+ public Usage getUsage(){
+ return usage;
+ }
+
+ /**
+ * @param usage The usage of this buffer. See {@link Usage} for more
+ * information.
+ */
+ public void setUsage(Usage usage){
+// if (id != -1)
+// throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
+
+ this.usage = usage;
+ }
+
+ /**
+ * @param normalized Set to true if integer components should be converted
+ * from their maximal range into the range 0.0 - 1.0 when converted to
+ * a floating-point value for the shader.
+ * E.g. if the {@link Format} is {@link Format#UnsignedInt}, then
+ * the components will be converted to the range 0.0 - 1.0 by dividing
+ * every integer by 2^32.
+ */
+ public void setNormalized(boolean normalized){
+ this.normalized = normalized;
+ }
+
+ /**
+ * @return True if integer components should be converted to the range 0-1.
+ * @see VertexBuffer#setNormalized(boolean)
+ */
+ public boolean isNormalized(){
+ return normalized;
+ }
+
+ /**
+ * @return The type of information that this buffer has.
+ */
+ public Type getBufferType(){
+ return bufType;
+ }
+
+ /**
+ * @return The {@link Format format}, or data type of the data.
+ */
+ public Format getFormat(){
+ return format;
+ }
+
+ /**
+ * @return The number of components of the given {@link Format format} per
+ * element.
+ */
+ public int getNumComponents(){
+ return components;
+ }
+
+ /**
+ * @return The total number of data elements in the data buffer.
+ */
+ public int getNumElements(){
+ int elements = data.capacity() / components;
+ if (format == Format.Half)
+ elements /= 2;
+ return elements;
+ }
+
+ /**
+ * Called to initialize the data in the <code>VertexBuffer</code>. Must only
+ * be called once.
+ *
+ * @param usage The usage for the data, or how often will the data
+ * be updated per frame. See the {@link Usage} enum.
+ * @param components The number of components per element.
+ * @param format The {@link Format format}, or data-type of a single
+ * component.
+ * @param data A native buffer, the format of which matches the {@link Format}
+ * argument.
+ */
+ public void setupData(Usage usage, int components, Format format, Buffer data){
+ if (id != -1)
+ throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
+
+ if (usage == null || format == null || data == null)
+ throw new IllegalArgumentException("None of the arguments can be null");
+
+ if (data.isReadOnly())
+ throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
+
+ if (components < 1 || components > 4)
+ throw new IllegalArgumentException("components must be between 1 and 4");
+
+ this.data = data;
+ this.components = components;
+ this.usage = usage;
+ this.format = format;
+ this.componentsLength = components * format.getComponentSize();
+ this.lastLimit = data.limit();
+ setUpdateNeeded();
+ }
+
+ /**
+ * Called to update the data in the buffer with new data. Can only
+ * be called after {@link VertexBuffer#setupData(com.jme3.scene.VertexBuffer.Usage, int, com.jme3.scene.VertexBuffer.Format, java.nio.Buffer) }
+ * has been called. Note that it is fine to call this method on the
+ * data already set, e.g. vb.updateData(vb.getData()), this will just
+ * set the proper update flag indicating the data should be sent to the GPU
+ * again.
+ * It is allowed to specify a buffer with different capacity than the
+ * originally set buffer.
+ *
+ * @param data The data buffer to set
+ */
+ public void updateData(Buffer data){
+ if (id != -1){
+ // request to update data is okay
+ }
+
+ // Check if the data buffer is read-only which is a sign
+ // of a bug on the part of the caller
+ if (data != null && data.isReadOnly()) {
+ throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
+ }
+
+ // will force renderer to call glBufferData again
+ if (data != null && (this.data.getClass() != data.getClass() || data.limit() != lastLimit)){
+ dataSizeChanged = true;
+ lastLimit = data.limit();
+ }
+
+ this.data = data;
+ setUpdateNeeded();
+ }
+
+ /**
+ * Returns true if the data size of the VertexBuffer has changed.
+ * Internal use only.
+ * @return true if the data size has changed
+ */
+ public boolean hasDataSizeChanged() {
+ return dataSizeChanged;
+ }
+
+ @Override
+ public void clearUpdateNeeded(){
+ super.clearUpdateNeeded();
+ dataSizeChanged = false;
+ }
+
+ /**
+ * Converts single floating-point data to {@link Format#Half half} floating-point data.
+ */
+ public void convertToHalf(){
+ if (id != -1)
+ throw new UnsupportedOperationException("Data has already been sent.");
+
+ if (format != Format.Float)
+ throw new IllegalStateException("Format must be float!");
+
+ int numElements = data.capacity() / components;
+ format = Format.Half;
+ this.componentsLength = components * format.getComponentSize();
+
+ ByteBuffer halfData = BufferUtils.createByteBuffer(componentsLength * numElements);
+ halfData.rewind();
+
+ FloatBuffer floatData = (FloatBuffer) data;
+ floatData.rewind();
+
+ for (int i = 0; i < floatData.capacity(); i++){
+ float f = floatData.get(i);
+ short half = FastMath.convertFloatToHalf(f);
+ halfData.putShort(half);
+ }
+ this.data = halfData;
+ setUpdateNeeded();
+ dataSizeChanged = true;
+ }
+
+ /**
+ * Reduces the capacity of the buffer to the given amount
+ * of elements, any elements at the end of the buffer are truncated
+ * as necessary.
+ *
+ * @param numElements The number of elements to reduce to.
+ */
+ public void compact(int numElements){
+ int total = components * numElements;
+ data.clear();
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bbuf = (ByteBuffer) data;
+ bbuf.limit(total);
+ ByteBuffer bnewBuf = BufferUtils.createByteBuffer(total);
+ bnewBuf.put(bbuf);
+ data = bnewBuf;
+ break;
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sbuf = (ShortBuffer) data;
+ sbuf.limit(total);
+ ShortBuffer snewBuf = BufferUtils.createShortBuffer(total);
+ snewBuf.put(sbuf);
+ data = snewBuf;
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer ibuf = (IntBuffer) data;
+ ibuf.limit(total);
+ IntBuffer inewBuf = BufferUtils.createIntBuffer(total);
+ inewBuf.put(ibuf);
+ data = inewBuf;
+ break;
+ case Float:
+ FloatBuffer fbuf = (FloatBuffer) data;
+ fbuf.limit(total);
+ FloatBuffer fnewBuf = BufferUtils.createFloatBuffer(total);
+ fnewBuf.put(fbuf);
+ data = fnewBuf;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+ data.clear();
+ setUpdateNeeded();
+ dataSizeChanged = true;
+ }
+
+ /**
+ * Modify a component inside an element.
+ * The <code>val</code> parameter must be in the buffer's format:
+ * {@link Format}.
+ *
+ * @param elementIndex The element index to modify
+ * @param componentIndex The component index to modify
+ * @param val The value to set, either byte, short, int or float depending
+ * on the {@link Format}.
+ */
+ public void setElementComponent(int elementIndex, int componentIndex, Object val){
+ int inPos = elementIndex * components;
+ int elementPos = componentIndex;
+
+ if (format == Format.Half){
+ inPos *= 2;
+ elementPos *= 2;
+ }
+
+ data.clear();
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bin = (ByteBuffer) data;
+ bin.put(inPos + elementPos, (Byte)val);
+ break;
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sin = (ShortBuffer) data;
+ sin.put(inPos + elementPos, (Short)val);
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer iin = (IntBuffer) data;
+ iin.put(inPos + elementPos, (Integer)val);
+ break;
+ case Float:
+ FloatBuffer fin = (FloatBuffer) data;
+ fin.put(inPos + elementPos, (Float)val);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+ }
+
+ /**
+ * Get the component inside an element.
+ *
+ * @param elementIndex The element index
+ * @param componentIndex The component index
+ * @return The component, as one of the primitive types, byte, short,
+ * int or float.
+ */
+ public Object getElementComponent(int elementIndex, int componentIndex){
+ int inPos = elementIndex * components;
+ int elementPos = componentIndex;
+
+ if (format == Format.Half){
+ inPos *= 2;
+ elementPos *= 2;
+ }
+
+ Buffer srcData = getDataReadOnly();
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bin = (ByteBuffer) srcData;
+ return bin.get(inPos + elementPos);
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sin = (ShortBuffer) srcData;
+ return sin.get(inPos + elementPos);
+ case Int:
+ case UnsignedInt:
+ IntBuffer iin = (IntBuffer) srcData;
+ return iin.get(inPos + elementPos);
+ case Float:
+ FloatBuffer fin = (FloatBuffer) srcData;
+ return fin.get(inPos + elementPos);
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+ }
+
+ /**
+ * Copies a single element of data from this <code>VertexBuffer</code>
+ * to the given output VertexBuffer.
+ *
+ * @param inIndex The input element index
+ * @param outVb The buffer to copy to
+ * @param outIndex The output element index
+ *
+ * @throws IllegalArgumentException If the formats of the buffers do not
+ * match.
+ */
+ public void copyElement(int inIndex, VertexBuffer outVb, int outIndex){
+ copyElements(inIndex, outVb, outIndex, 1);
+ }
+
+ /**
+ * Copies a sequence of elements of data from this <code>VertexBuffer</code>
+ * to the given output VertexBuffer.
+ *
+ * @param inIndex The input element index
+ * @param outVb The buffer to copy to
+ * @param outIndex The output element index
+ * @param len The number of elements to copy
+ *
+ * @throws IllegalArgumentException If the formats of the buffers do not
+ * match.
+ */
+ public void copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len){
+ if (outVb.format != format || outVb.components != components)
+ throw new IllegalArgumentException("Buffer format mismatch. Cannot copy");
+
+ int inPos = inIndex * components;
+ int outPos = outIndex * components;
+ int elementSz = components;
+ if (format == Format.Half){
+ // because half is stored as bytebuf but its 2 bytes long
+ inPos *= 2;
+ outPos *= 2;
+ elementSz *= 2;
+ }
+
+ // Make sure to grab a read-only copy in case some other
+ // thread is also accessing the buffer and messing with its
+ // position()
+ Buffer srcData = getDataReadOnly();
+ outVb.data.clear();
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bin = (ByteBuffer) srcData;
+ ByteBuffer bout = (ByteBuffer) outVb.data;
+ bin.position(inPos).limit(inPos + elementSz * len);
+ bout.position(outPos).limit(outPos + elementSz * len);
+ bout.put(bin);
+ break;
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sin = (ShortBuffer) srcData;
+ ShortBuffer sout = (ShortBuffer) outVb.data;
+ sin.position(inPos).limit(inPos + elementSz * len);
+ sout.position(outPos).limit(outPos + elementSz * len);
+ sout.put(sin);
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer iin = (IntBuffer) srcData;
+ IntBuffer iout = (IntBuffer) outVb.data;
+ iin.position(inPos).limit(inPos + elementSz * len);
+ iout.position(outPos).limit(outPos + elementSz * len);
+ iout.put(iin);
+ break;
+ case Float:
+ FloatBuffer fin = (FloatBuffer) srcData;
+ FloatBuffer fout = (FloatBuffer) outVb.data;
+ fin.position(inPos).limit(inPos + elementSz * len);
+ fout.position(outPos).limit(outPos + elementSz * len);
+ fout.put(fin);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+
+ // Clear the output buffer to rewind it and reset its
+ // limit from where we shortened it above.
+ outVb.data.clear();
+ }
+
+ /**
+ * Creates a {@link Buffer} that satisfies the given type and size requirements
+ * of the parameters. The buffer will be of the type specified by
+ * {@link Format format} and would be able to contain the given number
+ * of elements with the given number of components in each element.
+ */
+ public static Buffer createBuffer(Format format, int components, int numElements){
+ if (components < 1 || components > 4)
+ throw new IllegalArgumentException("Num components must be between 1 and 4");
+
+ int total = numElements * components;
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ return BufferUtils.createByteBuffer(total);
+ case Half:
+ return BufferUtils.createByteBuffer(total * 2);
+ case Short:
+ case UnsignedShort:
+ return BufferUtils.createShortBuffer(total);
+ case Int:
+ case UnsignedInt:
+ return BufferUtils.createIntBuffer(total);
+ case Float:
+ return BufferUtils.createFloatBuffer(total);
+ case Double:
+ return BufferUtils.createDoubleBuffer(total);
+ default:
+ throw new UnsupportedOperationException("Unrecoginized buffer format: "+format);
+ }
+ }
+
+ /**
+ * Creates a deep clone of the {@link VertexBuffer}.
+ *
+ * @return Deep clone of this buffer
+ */
+ @Override
+ public VertexBuffer clone(){
+ // NOTE: Superclass GLObject automatically creates shallow clone
+ // e.g re-use ID.
+ VertexBuffer vb = (VertexBuffer) super.clone();
+ vb.handleRef = new Object();
+ vb.id = -1;
+ if (data != null) {
+ // Make sure to pass a read-only buffer to clone so that
+ // the position information doesn't get clobbered by another
+ // reading thread during cloning (and vice versa) since this is
+ // a purely read-only operation.
+ vb.updateData(BufferUtils.clone(getDataReadOnly()));
+ }
+
+ return vb;
+ }
+
+ /**
+ * Creates a deep clone of this VertexBuffer but overrides the
+ * {@link Type}.
+ *
+ * @param overrideType The type of the cloned VertexBuffer
+ * @return A deep clone of the buffer
+ */
+ public VertexBuffer clone(Type overrideType){
+ VertexBuffer vb = new VertexBuffer(overrideType);
+ vb.components = components;
+ vb.componentsLength = componentsLength;
+
+ // Make sure to pass a read-only buffer to clone so that
+ // the position information doesn't get clobbered by another
+ // reading thread during cloning (and vice versa) since this is
+ // a purely read-only operation.
+ vb.data = BufferUtils.clone(getDataReadOnly());
+ vb.format = format;
+ vb.handleRef = new Object();
+ vb.id = -1;
+ vb.normalized = normalized;
+ vb.offset = offset;
+ vb.stride = stride;
+ vb.updateNeeded = true;
+ vb.usage = usage;
+ return vb;
+ }
+
+ @Override
+ public String toString(){
+ String dataTxt = null;
+ if (data != null){
+ dataTxt = ", elements="+data.capacity();
+ }
+ return getClass().getSimpleName() + "[fmt="+format.name()
+ +", type="+bufType.name()
+ +", usage="+usage.name()
+ +dataTxt+"]";
+ }
+
+ @Override
+ public void resetObject() {
+// assert this.id != -1;
+ this.id = -1;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((Renderer)rendererObject).deleteBuffer(this);
+ }
+
+ @Override
+ public NativeObject createDestructableClone(){
+ return new VertexBuffer(id);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(components, "components", 0);
+ oc.write(usage, "usage", Usage.Dynamic);
+ oc.write(bufType, "buffer_type", null);
+ oc.write(format, "format", Format.Float);
+ oc.write(normalized, "normalized", false);
+ oc.write(offset, "offset", 0);
+ oc.write(stride, "stride", 0);
+
+ String dataName = "data" + format.name();
+ Buffer roData = getDataReadOnly();
+ switch (format){
+ case Float:
+ oc.write((FloatBuffer) roData, dataName, null);
+ break;
+ case Short:
+ case UnsignedShort:
+ oc.write((ShortBuffer) roData, dataName, null);
+ break;
+ case UnsignedByte:
+ case Byte:
+ case Half:
+ oc.write((ByteBuffer) roData, dataName, null);
+ break;
+ case Int:
+ case UnsignedInt:
+ oc.write((IntBuffer) roData, dataName, null);
+ break;
+ default:
+ throw new IOException("Unsupported export buffer format: "+format);
+ }
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ components = ic.readInt("components", 0);
+ usage = ic.readEnum("usage", Usage.class, Usage.Dynamic);
+ bufType = ic.readEnum("buffer_type", Type.class, null);
+ format = ic.readEnum("format", Format.class, Format.Float);
+ normalized = ic.readBoolean("normalized", false);
+ offset = ic.readInt("offset", 0);
+ stride = ic.readInt("stride", 0);
+ componentsLength = components * format.getComponentSize();
+
+ String dataName = "data" + format.name();
+ switch (format){
+ case Float:
+ data = ic.readFloatBuffer(dataName, null);
+ break;
+ case Short:
+ case UnsignedShort:
+ data = ic.readShortBuffer(dataName, null);
+ break;
+ case UnsignedByte:
+ case Byte:
+ case Half:
+ data = ic.readByteBuffer(dataName, null);
+ break;
+ case Int:
+ case UnsignedInt:
+ data = ic.readIntBuffer(dataName, null);
+ break;
+ default:
+ throw new IOException("Unsupported import buffer format: "+format);
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/control/AbstractControl.java b/engine/src/core/com/jme3/scene/control/AbstractControl.java
new file mode 100644
index 0000000..2887ec9
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/AbstractControl.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.control;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * An abstract implementation of the Control interface.
+ *
+ * @author Kirill Vainer
+ */
+public abstract class AbstractControl implements Control {
+
+ protected boolean enabled = true;
+ protected Spatial spatial;
+
+ public AbstractControl(){
+ }
+
+ public void setSpatial(Spatial spatial) {
+ if (this.spatial != null && spatial != null) {
+ throw new IllegalStateException("This control has already been added to a Spatial");
+ }
+ this.spatial = spatial;
+ }
+
+ public Spatial getSpatial(){
+ return spatial;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * To be implemented in subclass.
+ */
+ protected abstract void controlUpdate(float tpf);
+
+ /**
+ * To be implemented in subclass.
+ */
+ protected abstract void controlRender(RenderManager rm, ViewPort vp);
+
+ public void update(float tpf) {
+ if (!enabled)
+ return;
+
+ controlUpdate(tpf);
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ if (!enabled)
+ return;
+
+ controlRender(rm, vp);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(enabled, "enabled", true);
+ oc.write(spatial, "spatial", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ enabled = ic.readBoolean("enabled", true);
+ spatial = (Spatial) ic.readSavable("spatial", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/control/AreaUtils.java b/engine/src/core/com/jme3/scene/control/AreaUtils.java
new file mode 100644
index 0000000..47e7c5f
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/AreaUtils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.control;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.math.FastMath;
+
+/**
+ * <code>AreaUtils</code> is used to calculate the area of various objects, such as bounding volumes. These
+ * functions are very loose approximations.
+ * @author Joshua Slack
+ * @version $Id: AreaUtils.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ */
+
+public class AreaUtils {
+
+ /**
+ * calcScreenArea -- in Pixels
+ * Aproximates the screen area of a bounding volume. If the volume isn't a
+ * BoundingSphere, BoundingBox, or OrientedBoundingBox 0 is returned.
+ *
+ * @param bound The bounds to calculate the volume from.
+ * @param distance The distance from camera to object.
+ * @param screenWidth The width of the screen.
+ * @return The area in pixels on the screen of the bounding volume.
+ */
+ public static float calcScreenArea(BoundingVolume bound, float distance, float screenWidth) {
+ if (bound.getType() == BoundingVolume.Type.Sphere){
+ return calcScreenArea((BoundingSphere) bound, distance, screenWidth);
+ }else if (bound.getType() == BoundingVolume.Type.AABB){
+ return calcScreenArea((BoundingBox) bound, distance, screenWidth);
+ }
+ return 0.0f;
+ }
+
+ private static float calcScreenArea(BoundingSphere bound, float distance, float screenWidth) {
+ // Where is the center point and a radius point that lies in a plan parallel to the view plane?
+// // Calc radius based on these two points and plug into circle area formula.
+// Vector2f centerSP = null;
+// Vector2f outerSP = null;
+// float radiusSq = centerSP.subtract(outerSP).lengthSquared();
+ float radius = (bound.getRadius() * screenWidth) / (distance * 2);
+ return radius * radius * FastMath.PI;
+ }
+
+ private static float calcScreenArea(BoundingBox bound, float distance, float screenWidth) {
+ // Calc as if we are a BoundingSphere for now...
+ float radiusSquare = bound.getXExtent() * bound.getXExtent()
+ + bound.getYExtent() * bound.getYExtent()
+ + bound.getZExtent() * bound.getZExtent();
+ return ((radiusSquare * screenWidth * screenWidth) / (distance * distance * 4)) * FastMath.PI;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/control/BillboardControl.java b/engine/src/core/com/jme3/scene/control/BillboardControl.java
new file mode 100644
index 0000000..40c2fea
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/BillboardControl.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.control;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+public class BillboardControl extends AbstractControl {
+
+ private Matrix3f orient;
+ private Vector3f look;
+ private Vector3f left;
+ private Alignment alignment;
+
+ /**
+ * Determines how the billboard is aligned to the screen/camera.
+ */
+ public enum Alignment {
+ /**
+ * Aligns this Billboard to the screen.
+ */
+ Screen,
+
+ /**
+ * Aligns this Billboard to the camera position.
+ */
+ Camera,
+
+ /**
+ * Aligns this Billboard to the screen, but keeps the Y axis fixed.
+ */
+ AxialY,
+
+ /**
+ * Aligns this Billboard to the screen, but keeps the Z axis fixed.
+ */
+ AxialZ;
+ }
+
+ public BillboardControl() {
+ super();
+ orient = new Matrix3f();
+ look = new Vector3f();
+ left = new Vector3f();
+ alignment = Alignment.Screen;
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ BillboardControl control = new BillboardControl();
+ control.alignment = this.alignment;
+ control.setSpatial(spatial);
+ return control;
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ Camera cam = vp.getCamera();
+ rotateBillboard(cam);
+ }
+
+ private void fixRefreshFlags(){
+ // force transforms to update below this node
+ spatial.updateGeometricState();
+
+ // force world bound to update
+ Spatial rootNode = spatial;
+ while (rootNode.getParent() != null){
+ rootNode = rootNode.getParent();
+ }
+ rootNode.getWorldBound();
+ }
+
+ /**
+ * rotate the billboard based on the type set
+ *
+ * @param cam
+ * Camera
+ */
+ private void rotateBillboard(Camera cam) {
+ switch (alignment) {
+ case AxialY:
+ rotateAxial(cam, Vector3f.UNIT_Y);
+ break;
+ case AxialZ:
+ rotateAxial(cam, Vector3f.UNIT_Z);
+ break;
+ case Screen:
+ rotateScreenAligned(cam);
+ break;
+ case Camera:
+ rotateCameraAligned(cam);
+ break;
+ }
+ }
+
+ /**
+ * Aligns this Billboard so that it points to the camera position.
+ *
+ * @param camera
+ * Camera
+ */
+ private void rotateCameraAligned(Camera camera) {
+ look.set(camera.getLocation()).subtractLocal(
+ spatial.getWorldTranslation());
+ // coopt left for our own purposes.
+ Vector3f xzp = left;
+ // The xzp vector is the projection of the look vector on the xz plane
+ xzp.set(look.x, 0, look.z);
+
+ // check for undefined rotation...
+ if (xzp.equals(Vector3f.ZERO)) {
+ return;
+ }
+
+ look.normalizeLocal();
+ xzp.normalizeLocal();
+ float cosp = look.dot(xzp);
+
+ // compute the local orientation matrix for the billboard
+ orient.set(0, 0, xzp.z);
+ orient.set(0, 1, xzp.x * -look.y);
+ orient.set(0, 2, xzp.x * cosp);
+ orient.set(1, 0, 0);
+ orient.set(1, 1, cosp);
+ orient.set(1, 2, look.y);
+ orient.set(2, 0, -xzp.x);
+ orient.set(2, 1, xzp.z * -look.y);
+ orient.set(2, 2, xzp.z * cosp);
+
+ // The billboard must be oriented to face the camera before it is
+ // transformed into the world.
+ spatial.setLocalRotation(orient);
+ fixRefreshFlags();
+ }
+
+ /**
+ * Rotate the billboard so it points directly opposite the direction the
+ * camera's facing
+ *
+ * @param camera
+ * Camera
+ */
+ private void rotateScreenAligned(Camera camera) {
+ // coopt diff for our in direction:
+ look.set(camera.getDirection()).negateLocal();
+ // coopt loc for our left direction:
+ left.set(camera.getLeft()).negateLocal();
+ orient.fromAxes(left, camera.getUp(), look);
+ Node parent = spatial.getParent();
+ Quaternion rot=new Quaternion().fromRotationMatrix(orient);
+ if ( parent != null ) {
+ rot = parent.getWorldRotation().inverse().multLocal(rot);
+ rot.normalizeLocal();
+ }
+ spatial.setLocalRotation(rot);
+ fixRefreshFlags();
+ }
+
+ /**
+ * Rotate the billboard towards the camera, but keeping a given axis fixed.
+ *
+ * @param camera
+ * Camera
+ */
+ private void rotateAxial(Camera camera, Vector3f axis) {
+ // Compute the additional rotation required for the billboard to face
+ // the camera. To do this, the camera must be inverse-transformed into
+ // the model space of the billboard.
+ look.set(camera.getLocation()).subtractLocal(
+ spatial.getWorldTranslation());
+ spatial.getParent().getWorldRotation().mult(look, left); // coopt left for our own
+ // purposes.
+ left.x *= 1.0f / spatial.getWorldScale().x;
+ left.y *= 1.0f / spatial.getWorldScale().y;
+ left.z *= 1.0f / spatial.getWorldScale().z;
+
+ // squared length of the camera projection in the xz-plane
+ float lengthSquared = left.x * left.x + left.z * left.z;
+ if (lengthSquared < FastMath.FLT_EPSILON) {
+ // camera on the billboard axis, rotation not defined
+ return;
+ }
+
+ // unitize the projection
+ float invLength = FastMath.invSqrt(lengthSquared);
+ if (axis.y == 1) {
+ left.x *= invLength;
+ left.y = 0.0f;
+ left.z *= invLength;
+
+ // compute the local orientation matrix for the billboard
+ orient.set(0, 0, left.z);
+ orient.set(0, 1, 0);
+ orient.set(0, 2, left.x);
+ orient.set(1, 0, 0);
+ orient.set(1, 1, 1);
+ orient.set(1, 2, 0);
+ orient.set(2, 0, -left.x);
+ orient.set(2, 1, 0);
+ orient.set(2, 2, left.z);
+ } else if (axis.z == 1) {
+ left.x *= invLength;
+ left.y *= invLength;
+ left.z = 0.0f;
+
+ // compute the local orientation matrix for the billboard
+ orient.set(0, 0, left.y);
+ orient.set(0, 1, left.x);
+ orient.set(0, 2, 0);
+ orient.set(1, 0, -left.y);
+ orient.set(1, 1, left.x);
+ orient.set(1, 2, 0);
+ orient.set(2, 0, 0);
+ orient.set(2, 1, 0);
+ orient.set(2, 2, 1);
+ }
+
+ // The billboard must be oriented to face the camera before it is
+ // transformed into the world.
+ spatial.setLocalRotation(orient);
+ fixRefreshFlags();
+ }
+
+ /**
+ * Returns the alignment this Billboard is set too.
+ *
+ * @return The alignment of rotation, AxialY, AxialZ, Camera or Screen.
+ */
+ public Alignment getAlignment() {
+ return alignment;
+ }
+
+ /**
+ * Sets the type of rotation this Billboard will have. The alignment can
+ * be Camera, Screen, AxialY, or AxialZ. Invalid alignments will
+ * assume no billboard rotation.
+ */
+ public void setAlignment(Alignment alignment) {
+ this.alignment = alignment;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(orient, "orient", null);
+ capsule.write(look, "look", null);
+ capsule.write(left, "left", null);
+ capsule.write(alignment, "alignment", Alignment.Screen);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ orient = (Matrix3f) capsule.readSavable("orient", null);
+ look = (Vector3f) capsule.readSavable("look", null);
+ left = (Vector3f) capsule.readSavable("left", null);
+ alignment = capsule.readEnum("alignment", Alignment.class, Alignment.Screen);
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/control/CameraControl.java b/engine/src/core/com/jme3/scene/control/CameraControl.java
new file mode 100644
index 0000000..5dd559c
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/CameraControl.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.control;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * This Control maintains a reference to a Camera,
+ * which will be synched with the position (worldTranslation)
+ * of the current spatial.
+ * @author tim
+ */
+public class CameraControl extends AbstractControl {
+
+ public static enum ControlDirection {
+
+ /**
+ * Means, that the Camera's transform is "copied"
+ * to the Transform of the Spatial.
+ */
+ CameraToSpatial,
+ /**
+ * Means, that the Spatial's transform is "copied"
+ * to the Transform of the Camera.
+ */
+ SpatialToCamera;
+ }
+ private Camera camera;
+ private ControlDirection controlDir = ControlDirection.CameraToSpatial;
+
+ /**
+ * Constructor used for Serialization.
+ */
+ public CameraControl() {
+ }
+
+ /**
+ * @param camera The Camera to be synced.
+ */
+ public CameraControl(Camera camera) {
+ this.camera = camera;
+ }
+
+ /**
+ * @param camera The Camera to be synced.
+ */
+ public CameraControl(Camera camera, ControlDirection controlDir) {
+ this.camera = camera;
+ this.controlDir = controlDir;
+ }
+
+ public Camera getCamera() {
+ return camera;
+ }
+
+ public void setCamera(Camera camera) {
+ this.camera = camera;
+ }
+
+ public ControlDirection getControlDir() {
+ return controlDir;
+ }
+
+ public void setControlDir(ControlDirection controlDir) {
+ this.controlDir = controlDir;
+ }
+
+ // fields used, when inversing ControlDirection:
+ @Override
+ protected void controlUpdate(float tpf) {
+ if (spatial != null && camera != null) {
+ switch (controlDir) {
+ case SpatialToCamera:
+ camera.setLocation(spatial.getWorldTranslation());
+ camera.setRotation(spatial.getWorldRotation());
+ break;
+ case CameraToSpatial:
+ // set the localtransform, so that the worldtransform would be equal to the camera's transform.
+ // Location:
+ TempVars vars = TempVars.get();
+
+ Vector3f vecDiff = vars.vect1.set(camera.getLocation()).subtractLocal(spatial.getWorldTranslation());
+ spatial.setLocalTranslation(vecDiff.addLocal(spatial.getLocalTranslation()));
+
+ // Rotation:
+ Quaternion worldDiff = vars.quat1.set(camera.getRotation()).subtractLocal(spatial.getWorldRotation());
+ spatial.setLocalRotation(worldDiff.addLocal(spatial.getLocalRotation()));
+ vars.release();
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ // nothing to do
+ }
+
+ @Override
+ public Control cloneForSpatial(Spatial newSpatial) {
+ CameraControl control = new CameraControl(camera, controlDir);
+ control.setSpatial(newSpatial);
+ control.setEnabled(isEnabled());
+ return control;
+ }
+ private static final String CONTROL_DIR_NAME = "controlDir";
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ im.getCapsule(this).readEnum(CONTROL_DIR_NAME,
+ ControlDirection.class, ControlDirection.SpatialToCamera);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ ex.getCapsule(this).write(controlDir, CONTROL_DIR_NAME,
+ ControlDirection.SpatialToCamera);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/control/Control.java b/engine/src/core/com/jme3/scene/control/Control.java
new file mode 100644
index 0000000..b5b0057
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/Control.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.control;
+
+import com.jme3.export.Savable;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+
+/**
+ * An interface for scene-graph controls.
+ * <p>
+ * <code>Control</code>s are used to specify certain update and render logic
+ * for a {@link Spatial}.
+ *
+ * @author Kirill Vainer
+ */
+public interface Control extends Savable {
+
+ /**
+ * Creates a clone of the Control, the given Spatial is the cloned
+ * version of the spatial to which this control is attached to.
+ * @param spatial
+ * @return A clone of this control for the spatial
+ */
+ public Control cloneForSpatial(Spatial spatial);
+
+ /**
+ * @param spatial the spatial to be controlled. This should not be called
+ * from user code.
+ */
+ public void setSpatial(Spatial spatial);
+
+ /**
+ * @param enabled Enable or disable the control. If disabled, update()
+ * should do nothing.
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * @return True if enabled, false otherwise.
+ * @see Control#setEnabled(boolean)
+ */
+ public boolean isEnabled();
+
+ /**
+ * Updates the control. This should not be called from user code.
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf);
+
+ /**
+ * Should be called prior to queuing the spatial by the RenderManager. This
+ * should not be called from user code.
+ *
+ * @param rm
+ * @param vp
+ */
+ public void render(RenderManager rm, ViewPort vp);
+}
diff --git a/engine/src/core/com/jme3/scene/control/LightControl.java b/engine/src/core/com/jme3/scene/control/LightControl.java
new file mode 100644
index 0000000..2859c9b
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/LightControl.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.control;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.PointLight;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * This Control maintains a reference to a Camera,
+ * which will be synched with the position (worldTranslation)
+ * of the current spatial.
+ * @author tim
+ */
+public class LightControl extends AbstractControl {
+
+ public static enum ControlDirection {
+
+ /**
+ * Means, that the Light's transform is "copied"
+ * to the Transform of the Spatial.
+ */
+ LightToSpatial,
+ /**
+ * Means, that the Spatial's transform is "copied"
+ * to the Transform of the light.
+ */
+ SpatialToLight;
+ }
+ private Light light;
+ private ControlDirection controlDir = ControlDirection.SpatialToLight;
+
+ /**
+ * Constructor used for Serialization.
+ */
+ public LightControl() {
+ }
+
+ /**
+ * @param light The light to be synced.
+ */
+ public LightControl(Light light) {
+ this.light = light;
+ }
+
+ /**
+ * @param light The light to be synced.
+ */
+ public LightControl(Light light, ControlDirection controlDir) {
+ this.light = light;
+ this.controlDir = controlDir;
+ }
+
+ public Light getLight() {
+ return light;
+ }
+
+ public void setLight(Light light) {
+ this.light = light;
+ }
+
+ public ControlDirection getControlDir() {
+ return controlDir;
+ }
+
+ public void setControlDir(ControlDirection controlDir) {
+ this.controlDir = controlDir;
+ }
+
+ // fields used, when inversing ControlDirection:
+ @Override
+ protected void controlUpdate(float tpf) {
+ if (spatial != null && light != null) {
+ switch (controlDir) {
+ case SpatialToLight:
+ spatialTolight(light);
+ break;
+ case LightToSpatial:
+ lightToSpatial(light);
+ break;
+ }
+ }
+ }
+
+ private void spatialTolight(Light light) {
+ if (light instanceof PointLight) {
+ ((PointLight) light).setPosition(spatial.getWorldTranslation());
+ }
+ TempVars vars = TempVars.get();
+
+ if (light instanceof DirectionalLight) {
+ ((DirectionalLight) light).setDirection(vars.vect1.set(spatial.getWorldTranslation()).multLocal(-1.0f));
+ }
+ vars.release();
+ //TODO add code for Spot light here when it's done
+// if( light instanceof SpotLight){
+// ((SpotLight)light).setPosition(spatial.getWorldTranslation());
+// ((SpotLight)light).setRotation(spatial.getWorldRotation());
+// }
+
+ }
+
+ private void lightToSpatial(Light light) {
+ TempVars vars = TempVars.get();
+ if (light instanceof PointLight) {
+
+ PointLight pLight = (PointLight) light;
+
+ Vector3f vecDiff = vars.vect1.set(pLight.getPosition()).subtractLocal(spatial.getWorldTranslation());
+ spatial.setLocalTranslation(vecDiff.addLocal(spatial.getLocalTranslation()));
+ }
+
+ if (light instanceof DirectionalLight) {
+ DirectionalLight dLight = (DirectionalLight) light;
+ vars.vect1.set(dLight.getDirection()).multLocal(-1.0f);
+ Vector3f vecDiff = vars.vect1.subtractLocal(spatial.getWorldTranslation());
+ spatial.setLocalTranslation(vecDiff.addLocal(spatial.getLocalTranslation()));
+ }
+ vars.release();
+ //TODO add code for Spot light here when it's done
+
+
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ // nothing to do
+ }
+
+ @Override
+ public Control cloneForSpatial(Spatial newSpatial) {
+ LightControl control = new LightControl(light, controlDir);
+ control.setSpatial(newSpatial);
+ control.setEnabled(isEnabled());
+ return control;
+ }
+ private static final String CONTROL_DIR_NAME = "controlDir";
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ im.getCapsule(this).readEnum(CONTROL_DIR_NAME,
+ ControlDirection.class, ControlDirection.SpatialToLight);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ ex.getCapsule(this).write(controlDir, CONTROL_DIR_NAME,
+ ControlDirection.SpatialToLight);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/control/LodControl.java b/engine/src/core/com/jme3/scene/control/LodControl.java
new file mode 100644
index 0000000..6cbfefb
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/LodControl.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.control;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * Determines what Level of Detail a spatial should be, based on how many pixels
+ * on the screen the spatial is taking up. The more pixels covered, the more detailed
+ * the spatial should be.
+ * It calculates the area of the screen that the spatial covers by using its bounding box.
+ * When initializing, it will ask the spatial for how many triangles it has for each LOD.
+ * It then uses that, along with the trisPerPixel value to determine what LOD it should be at.
+ * It requires the camera to do this.
+ * The controlRender method is called each frame and will update the spatial's LOD
+ * if the camera has moved by a specified amount.
+ */
+public class LodControl extends AbstractControl implements Cloneable {
+
+ private float trisPerPixel = 1f;
+ private float distTolerance = 1f;
+ private float lastDistance = 0f;
+ private int lastLevel = 0;
+ private int numLevels;
+ private int[] numTris;
+
+ /**
+ * Creates a new <code>LodControl</code>.
+ */
+ public LodControl(){
+ }
+
+ /**
+ * Returns the distance tolerance for changing LOD.
+ *
+ * @return the distance tolerance for changing LOD.
+ *
+ * @see #setDistTolerance(float)
+ */
+ public float getDistTolerance() {
+ return distTolerance;
+ }
+
+ /**
+ * Specifies the distance tolerance for changing the LOD level on the geometry.
+ * The LOD level will only get changed if the geometry has moved this
+ * distance beyond the current LOD level.
+ *
+ * @param distTolerance distance tolerance for changing LOD
+ */
+ public void setDistTolerance(float distTolerance) {
+ this.distTolerance = distTolerance;
+ }
+
+ /**
+ * Returns the triangles per pixel value.
+ *
+ * @return the triangles per pixel value.
+ *
+ * @see #setTrisPerPixel(float)
+ */
+ public float getTrisPerPixel() {
+ return trisPerPixel;
+ }
+
+ /**
+ * Sets the triangles per pixel value.
+ * The <code>LodControl</code> will use this value as an error metric
+ * to determine which LOD level to use based on the geometry's
+ * area on the screen.
+ *
+ * @param trisPerPixel triangles per pixel
+ */
+ public void setTrisPerPixel(float trisPerPixel) {
+ this.trisPerPixel = trisPerPixel;
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial){
+ if (!(spatial instanceof Geometry))
+ throw new IllegalArgumentException("LodControl can only be attached to Geometry!");
+
+ super.setSpatial(spatial);
+ Geometry geom = (Geometry) spatial;
+ Mesh mesh = geom.getMesh();
+ numLevels = mesh.getNumLodLevels();
+ numTris = new int[numLevels];
+ for (int i = numLevels - 1; i >= 0; i--)
+ numTris[i] = mesh.getTriangleCount(i);
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ try {
+ LodControl clone = (LodControl) super.clone();
+ clone.lastDistance = 0;
+ clone.lastLevel = 0;
+ clone.numTris = numTris != null ? numTris.clone() : null;
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ }
+
+ protected void controlRender(RenderManager rm, ViewPort vp){
+ BoundingVolume bv = spatial.getWorldBound();
+
+ Camera cam = vp.getCamera();
+ float atanNH = FastMath.atan(cam.getFrustumNear() * cam.getFrustumTop());
+ float ratio = (FastMath.PI / (8f * atanNH));
+ float newDistance = bv.distanceTo(vp.getCamera().getLocation()) / ratio;
+ int level;
+
+ if (Math.abs(newDistance - lastDistance) <= distTolerance)
+ level = lastLevel; // we haven't moved relative to the model, send the old measurement back.
+ else if (lastDistance > newDistance && lastLevel == 0)
+ level = lastLevel; // we're already at the lowest setting and we just got closer to the model, no need to keep trying.
+ else if (lastDistance < newDistance && lastLevel == numLevels - 1)
+ level = lastLevel; // we're already at the highest setting and we just got further from the model, no need to keep trying.
+ else{
+ lastDistance = newDistance;
+
+ // estimate area of polygon via bounding volume
+ float area = AreaUtils.calcScreenArea(bv, lastDistance, cam.getWidth());
+ float trisToDraw = area * trisPerPixel;
+ level = numLevels - 1;
+ for (int i = numLevels; --i >= 0;){
+ if (trisToDraw - numTris[i] < 0){
+ break;
+ }
+ level = i;
+ }
+ lastLevel = level;
+ }
+
+ spatial.setLodLevel(level);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(trisPerPixel, "trisPerPixel", 1f);
+ oc.write(distTolerance, "distTolerance", 1f);
+ oc.write(numLevels, "numLevels", 0);
+ oc.write(numTris, "numTris", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ trisPerPixel = ic.readFloat("trisPerPixel", 1f);
+ distTolerance = ic.readFloat("distTolerance", 1f);
+ numLevels = ic.readInt("numLevels", 0);
+ numTris = ic.readIntArray("numTris", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/control/UpdateControl.java b/engine/src/core/com/jme3/scene/control/UpdateControl.java
new file mode 100644
index 0000000..7fbca01
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/UpdateControl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.control;
+
+import com.jme3.app.AppTask;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+
+/**
+ * Allows for enqueueing tasks onto the update loop / rendering thread.
+ *
+ * Usage:
+ * mySpatial.addControl(new UpdateControl()); // add it once
+ * mySpatial.getControl(UpdateControl.class).enqueue(new Callable() {
+ * public Object call() throws Exception {
+ * // do stuff here
+ * return null;
+ * }
+ * });
+ *
+ * @author Brent Owens
+ */
+public class UpdateControl extends AbstractControl {
+
+ private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
+
+ /**
+ * Enqueues a task/callable object to execute in the jME3
+ * rendering thread.
+ */
+ public <V> Future<V> enqueue(Callable<V> callable) {
+ AppTask<V> task = new AppTask<V>(callable);
+ taskQueue.add(task);
+ return task;
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ AppTask<?> task = taskQueue.poll();
+ toploop: do {
+ if (task == null) break;
+ while (task.isCancelled()) {
+ task = taskQueue.poll();
+ if (task == null) break toploop;
+ }
+ task.invoke();
+ } while (((task = taskQueue.poll()) != null));
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+
+ }
+
+ public Control cloneForSpatial(Spatial newSpatial) {
+ UpdateControl control = new UpdateControl();
+ control.setSpatial(newSpatial);
+ control.setEnabled(isEnabled());
+ control.taskQueue.addAll(taskQueue);
+ return control;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/control/package.html b/engine/src/core/com/jme3/scene/control/package.html
new file mode 100644
index 0000000..a387840
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/control/package.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.control</code> package provides
+{@link com.jme3.scene.control.Control controls}.
+Controls represent the "logical" programming of scene graph elements, containing
+callbacks for when a {@link com.jme3.scene.Spatial} is rendered or updated
+by the engine.
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/scene/debug/Arrow.java b/engine/src/core/com/jme3/scene/debug/Arrow.java
new file mode 100644
index 0000000..1c3dd94
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/Arrow.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.debug;
+
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import java.nio.FloatBuffer;
+
+/**
+ * The <code>Arrow</code> debug shape represents an arrow.
+ * An arrow is simply a line going from the original toward an extent
+ * and at the tip there will be triangle-like shape.
+ *
+ * @author Kirill Vainer
+ */
+public class Arrow extends Mesh {
+
+ private Quaternion tempQuat = new Quaternion();
+ private Vector3f tempVec = new Vector3f();
+
+ private static final float[] positions = new float[]{
+ 0, 0, 0,
+ 0, 0, 1, // tip
+ 0.05f, 0, 0.9f, // tip right
+ -0.05f, 0, 0.9f, // tip left
+ 0, 0.05f, 0.9f, // tip top
+ 0, -0.05f, 0.9f, // tip buttom
+ };
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Arrow() {
+ }
+
+ /**
+ * Creates an arrow mesh with the given extent.
+ * The arrow will start at the origin (0,0,0) and finish
+ * at the given extent.
+ *
+ * @param extent Extent of the arrow from origin
+ */
+ public Arrow(Vector3f extent) {
+ float len = extent.length();
+ Vector3f dir = extent.normalize();
+
+ tempQuat.lookAt(dir, Vector3f.UNIT_Y);
+ tempQuat.normalizeLocal();
+
+ float[] newPositions = new float[positions.length];
+ for (int i = 0; i < positions.length; i += 3) {
+ Vector3f vec = tempVec.set(positions[i],
+ positions[i + 1],
+ positions[i + 2]);
+ vec.multLocal(len);
+ tempQuat.mult(vec, vec);
+
+ newPositions[i] = vec.getX();
+ newPositions[i + 1] = vec.getY();
+ newPositions[i + 2] = vec.getZ();
+ }
+
+ setBuffer(Type.Position, 3, newPositions);
+ setBuffer(Type.Index, 2,
+ new short[]{
+ 0, 1,
+ 1, 2,
+ 1, 3,
+ 1, 4,
+ 1, 5,});
+ setMode(Mode.Lines);
+
+ updateBound();
+ updateCounts();
+ }
+
+ /**
+ * Sets the arrow's extent.
+ * This will modify the buffers on the mesh.
+ *
+ * @param extent the arrow's extent.
+ */
+ public void setArrowExtent(Vector3f extent) {
+ float len = extent.length();
+// Vector3f dir = extent.normalize();
+
+ tempQuat.lookAt(extent, Vector3f.UNIT_Y);
+ tempQuat.normalizeLocal();
+
+ VertexBuffer pvb = getBuffer(Type.Position);
+ FloatBuffer buffer = (FloatBuffer)pvb.getData();
+ buffer.rewind();
+ for (int i = 0; i < positions.length; i += 3) {
+ Vector3f vec = tempVec.set(positions[i],
+ positions[i + 1],
+ positions[i + 2]);
+ vec.multLocal(len);
+ tempQuat.mult(vec, vec);
+
+ buffer.put(vec.x);
+ buffer.put(vec.y);
+ buffer.put(vec.z);
+ }
+
+ pvb.updateData(buffer);
+
+ updateBound();
+ updateCounts();
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/debug/Grid.java b/engine/src/core/com/jme3/scene/debug/Grid.java
new file mode 100644
index 0000000..ea6225c
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/Grid.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.debug;
+
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * Simple grid shape.
+ *
+ * @author Kirill Vainer
+ */
+public class Grid extends Mesh {
+
+ /**
+ * Creates a grid debug shape.
+ * @param xLines
+ * @param yLines
+ * @param lineDist
+ */
+ public Grid(int xLines, int yLines, float lineDist){
+ xLines -= 2;
+ yLines -= 2;
+ int lineCount = xLines + yLines + 4;
+
+ FloatBuffer fpb = BufferUtils.createFloatBuffer(6 * lineCount);
+ ShortBuffer sib = BufferUtils.createShortBuffer(2 * lineCount);
+
+ float xLineLen = (yLines + 1) * lineDist;
+ float yLineLen = (xLines + 1) * lineDist;
+ int curIndex = 0;
+
+ // add lines along X
+ for (int i = 0; i < xLines + 2; i++){
+ float y = (i) * lineDist;
+
+ // positions
+ fpb.put(0) .put(0).put(y);
+ fpb.put(xLineLen).put(0).put(y);
+
+ // indices
+ sib.put( (short) (curIndex++) );
+ sib.put( (short) (curIndex++) );
+ }
+
+ // add lines along Y
+ for (int i = 0; i < yLines + 2; i++){
+ float x = (i) * lineDist;
+
+ // positions
+ fpb.put(x).put(0).put(0);
+ fpb.put(x).put(0).put(yLineLen);
+
+ // indices
+ sib.put( (short) (curIndex++) );
+ sib.put( (short) (curIndex++) );
+ }
+
+ fpb.flip();
+ sib.flip();
+
+ setBuffer(Type.Position, 3, fpb);
+ setBuffer(Type.Index, 2, sib);
+
+ setMode(Mode.Lines);
+
+ updateBound();
+ updateCounts();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/debug/SkeletonDebugger.java b/engine/src/core/com/jme3/scene/debug/SkeletonDebugger.java
new file mode 100644
index 0000000..07efdc9
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/SkeletonDebugger.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.debug;
+
+import com.jme3.animation.Skeleton;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+
+public class SkeletonDebugger extends Node {
+
+ private SkeletonWire wires;
+ private SkeletonPoints points;
+ private Skeleton skeleton;
+
+ public SkeletonDebugger(String name, Skeleton skeleton){
+ super(name);
+
+ this.skeleton = skeleton;
+ wires = new SkeletonWire(skeleton);
+ points = new SkeletonPoints(skeleton);
+
+ attachChild(new Geometry(name+"_wires", wires));
+ attachChild(new Geometry(name+"_points", points));
+
+ setQueueBucket(Bucket.Transparent);
+ }
+
+ public SkeletonDebugger(){
+ }
+
+ @Override
+ public void updateLogicalState(float tpf){
+ super.updateLogicalState(tpf);
+
+// skeleton.resetAndUpdate();
+ wires.updateGeometry();
+ points.updateGeometry();
+ }
+
+ public SkeletonPoints getPoints() {
+ return points;
+ }
+
+ public SkeletonWire getWires() {
+ return wires;
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/scene/debug/SkeletonPoints.java b/engine/src/core/com/jme3/scene/debug/SkeletonPoints.java
new file mode 100644
index 0000000..2e49ce5
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/SkeletonPoints.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.debug;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+
+public class SkeletonPoints extends Mesh {
+
+ private Skeleton skeleton;
+
+ public SkeletonPoints(Skeleton skeleton){
+ this.skeleton = skeleton;
+
+ setMode(Mode.Points);
+
+ VertexBuffer pb = new VertexBuffer(Type.Position);
+ FloatBuffer fpb = BufferUtils.createFloatBuffer(skeleton.getBoneCount() * 3);
+ pb.setupData(Usage.Stream, 3, Format.Float, fpb);
+ setBuffer(pb);
+
+ setPointSize(7);
+
+ updateCounts();
+ }
+
+ public void updateGeometry(){
+ VertexBuffer vb = getBuffer(Type.Position);
+ FloatBuffer posBuf = getFloatBuffer(Type.Position);
+ posBuf.clear();
+ for (int i = 0; i < skeleton.getBoneCount(); i++){
+ Bone bone = skeleton.getBone(i);
+ Vector3f bonePos = bone.getModelSpacePosition();
+
+ posBuf.put(bonePos.getX()).put(bonePos.getY()).put(bonePos.getZ());
+ }
+ posBuf.flip();
+ vb.updateData(posBuf);
+
+ updateBound();
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/debug/SkeletonWire.java b/engine/src/core/com/jme3/scene/debug/SkeletonWire.java
new file mode 100644
index 0000000..0796334
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/SkeletonWire.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.debug;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+public class SkeletonWire extends Mesh {
+
+ private int numConnections = 0;
+ private Skeleton skeleton;
+
+ private void countConnections(Bone bone){
+ for (Bone child : bone.getChildren()){
+ numConnections ++;
+ countConnections(child);
+ }
+ }
+
+ private void writeConnections(ShortBuffer indexBuf, Bone bone){
+ for (Bone child : bone.getChildren()){
+ // write myself
+ indexBuf.put( (short) skeleton.getBoneIndex(bone) );
+ // write the child
+ indexBuf.put( (short) skeleton.getBoneIndex(child) );
+
+ writeConnections(indexBuf, child);
+ }
+ }
+
+ public SkeletonWire(Skeleton skeleton){
+ this.skeleton = skeleton;
+ for (Bone bone : skeleton.getRoots())
+ countConnections(bone);
+
+ setMode(Mode.Lines);
+
+ VertexBuffer pb = new VertexBuffer(Type.Position);
+ FloatBuffer fpb = BufferUtils.createFloatBuffer(skeleton.getBoneCount() * 3);
+ pb.setupData(Usage.Stream, 3, Format.Float, fpb);
+ setBuffer(pb);
+
+ VertexBuffer ib = new VertexBuffer(Type.Index);
+ ShortBuffer sib = BufferUtils.createShortBuffer(numConnections * 2);
+ ib.setupData(Usage.Static, 2, Format.UnsignedShort, sib);
+ setBuffer(ib);
+
+ for (Bone bone : skeleton.getRoots())
+ writeConnections(sib, bone);
+ sib.flip();
+
+ updateCounts();
+ }
+
+ public void updateGeometry(){
+ VertexBuffer vb = getBuffer(Type.Position);
+ FloatBuffer posBuf = getFloatBuffer(Type.Position);
+ posBuf.clear();
+ for (int i = 0; i < skeleton.getBoneCount(); i++){
+ Bone bone = skeleton.getBone(i);
+ Vector3f bonePos = bone.getModelSpacePosition();
+
+ posBuf.put(bonePos.getX()).put(bonePos.getY()).put(bonePos.getZ());
+ }
+ posBuf.flip();
+ vb.updateData(posBuf);
+
+ updateBound();
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/debug/WireBox.java b/engine/src/core/com/jme3/scene/debug/WireBox.java
new file mode 100644
index 0000000..50af28a
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/WireBox.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.debug;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+
+public class WireBox extends Mesh {
+
+ public WireBox(){
+ this(1,1,1);
+ }
+
+ public WireBox(float xExt, float yExt, float zExt){
+ updatePositions(xExt,yExt,zExt);
+ setBuffer(Type.Index, 2,
+ new short[]{
+ 0, 1,
+ 1, 2,
+ 2, 3,
+ 3, 0,
+
+ 4, 5,
+ 5, 6,
+ 6, 7,
+ 7, 4,
+
+ 0, 4,
+ 1, 5,
+ 2, 6,
+ 3, 7,
+ }
+ );
+ setMode(Mode.Lines);
+
+ updateCounts();
+ }
+
+ public void updatePositions(float xExt, float yExt, float zExt){
+ VertexBuffer pvb = getBuffer(Type.Position);
+ FloatBuffer pb;
+ if (pvb == null){
+ pvb = new VertexBuffer(Type.Position);
+ pb = BufferUtils.createVector3Buffer(8);
+ pvb.setupData(Usage.Dynamic, 3, Format.Float, pb);
+ setBuffer(pvb);
+ }else{
+ pb = (FloatBuffer) pvb.getData();
+ pvb.updateData(pb);
+ }
+ pb.rewind();
+ pb.put(
+ new float[]{
+ -xExt, -yExt, zExt,
+ xExt, -yExt, zExt,
+ xExt, yExt, zExt,
+ -xExt, yExt, zExt,
+
+ -xExt, -yExt, -zExt,
+ xExt, -yExt, -zExt,
+ xExt, yExt, -zExt,
+ -xExt, yExt, -zExt,
+ }
+ );
+ updateBound();
+ }
+
+ public void fromBoundingBox(BoundingBox bbox){
+ updatePositions(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/debug/WireFrustum.java b/engine/src/core/com/jme3/scene/debug/WireFrustum.java
new file mode 100644
index 0000000..7a86d90
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/WireFrustum.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.debug;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+
+public class WireFrustum extends Mesh {
+
+ public WireFrustum(Vector3f[] points){
+ if (points != null)
+ setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points));
+
+ setBuffer(Type.Index, 2,
+ new short[]{
+ 0, 1,
+ 1, 2,
+ 2, 3,
+ 3, 0,
+
+ 4, 5,
+ 5, 6,
+ 6, 7,
+ 7, 4,
+
+ 0, 4,
+ 1, 5,
+ 2, 6,
+ 3, 7,
+ }
+ );
+ setMode(Mode.Lines);
+ }
+
+ public void update(Vector3f[] points){
+ VertexBuffer vb = getBuffer(Type.Position);
+ if (vb == null){
+ setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points));
+ return;
+ }
+
+ FloatBuffer b = BufferUtils.createFloatBuffer(points);
+ FloatBuffer a = (FloatBuffer) vb.getData();
+ b.rewind();
+ a.rewind();
+ a.put(b);
+ a.rewind();
+
+ vb.updateData(a);
+
+ updateBound();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/debug/WireSphere.java b/engine/src/core/com/jme3/scene/debug/WireSphere.java
new file mode 100644
index 0000000..df863e2
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/debug/WireSphere.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.debug;
+
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.math.FastMath;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+public class WireSphere extends Mesh {
+
+ private static final int samples = 30;
+ private static final int zSamples = 10;
+
+ public WireSphere() {
+ this(1);
+ }
+
+ public WireSphere(float radius) {
+ updatePositions(radius);
+ ShortBuffer ib = BufferUtils.createShortBuffer(samples * 2 * 2 + zSamples * samples * 2 /*+ 3 * 2*/);
+ setBuffer(Type.Index, 2, ib);
+
+// ib.put(new byte[]{
+// (byte) 0, (byte) 1,
+// (byte) 2, (byte) 3,
+// (byte) 4, (byte) 5,
+// });
+
+// int curNum = 3 * 2;
+ int curNum = 0;
+ for (int j = 0; j < 2 + zSamples; j++) {
+ for (int i = curNum; i < curNum + samples - 1; i++) {
+ ib.put((short) i).put((short) (i + 1));
+ }
+ ib.put((short) (curNum + samples - 1)).put((short) curNum);
+ curNum += samples;
+ }
+
+ setMode(Mode.Lines);
+
+ updateBound();
+ updateCounts();
+ }
+
+ public void updatePositions(float radius) {
+ VertexBuffer pvb = getBuffer(Type.Position);
+ FloatBuffer pb;
+
+ if (pvb == null) {
+ pvb = new VertexBuffer(Type.Position);
+ pb = BufferUtils.createVector3Buffer(samples * 2 + samples * zSamples /*+ 6 * 3*/);
+ pvb.setupData(Usage.Dynamic, 3, Format.Float, pb);
+ setBuffer(pvb);
+ } else {
+ pb = (FloatBuffer) pvb.getData();
+ }
+
+ pb.rewind();
+
+ // X axis
+// pb.put(radius).put(0).put(0);
+// pb.put(-radius).put(0).put(0);
+//
+// // Y axis
+// pb.put(0).put(radius).put(0);
+// pb.put(0).put(-radius).put(0);
+//
+// // Z axis
+// pb.put(0).put(0).put(radius);
+// pb.put(0).put(0).put(-radius);
+
+ float rate = FastMath.TWO_PI / (float) samples;
+ float angle = 0;
+ for (int i = 0; i < samples; i++) {
+ float x = radius * FastMath.cos(angle);
+ float y = radius * FastMath.sin(angle);
+ pb.put(x).put(y).put(0);
+ angle += rate;
+ }
+
+ angle = 0;
+ for (int i = 0; i < samples; i++) {
+ float x = radius * FastMath.cos(angle);
+ float y = radius * FastMath.sin(angle);
+ pb.put(0).put(x).put(y);
+ angle += rate;
+ }
+
+ float zRate = (radius * 2) / (float) (zSamples);
+ float zHeight = -radius + (zRate / 2f);
+
+
+ float rb = 1f / zSamples;
+ float b = rb / 2f;
+
+ for (int k = 0; k < zSamples; k++) {
+ angle = 0;
+ float scale = FastMath.sin(b * FastMath.PI);
+ for (int i = 0; i < samples; i++) {
+ float x = radius * FastMath.cos(angle);
+ float y = radius * FastMath.sin(angle);
+
+ pb.put(x * scale).put(zHeight).put(y * scale);
+
+ angle += rate;
+ }
+ zHeight += zRate;
+ b += rb;
+ }
+ }
+
+ /**
+ * Create a WireSphere from a BoundingSphere
+ *
+ * @param bsph
+ * BoundingSphere used to create the WireSphere
+ *
+ */
+ public void fromBoundingSphere(BoundingSphere bsph) {
+ updatePositions(bsph.getRadius());
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/IndexBuffer.java b/engine/src/core/com/jme3/scene/mesh/IndexBuffer.java
new file mode 100644
index 0000000..5c41c04
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/IndexBuffer.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.mesh;
+
+import com.jme3.util.BufferUtils;
+import java.nio.Buffer;
+
+/**
+ * <code>IndexBuffer</code> is an abstraction for integer index buffers,
+ * it is used to retrieve indices without knowing in which format they
+ * are stored (ushort or uint).
+ *
+ * @author lex
+ */
+public abstract class IndexBuffer {
+
+ /**
+ * Creates an index buffer that can contain the given amount
+ * of vertices.
+ * Returns {@link IndexShortBuffer}
+ *
+ * @param vertexCount The amount of vertices to contain
+ * @param indexCount The amount of indices
+ * to contain.
+ * @return A new index buffer
+ */
+ public static IndexBuffer createIndexBuffer(int vertexCount, int indexCount){
+ if (vertexCount > 65535){
+ return new IndexIntBuffer(BufferUtils.createIntBuffer(indexCount));
+ }else{
+ return new IndexShortBuffer(BufferUtils.createShortBuffer(indexCount));
+ }
+ }
+
+ /**
+ * Returns the vertex index for the given index in the index buffer.
+ *
+ * @param i The index inside the index buffer
+ * @return
+ */
+ public abstract int get(int i);
+
+ /**
+ * Puts the vertex index at the index buffer's index.
+ * Implementations may throw an {@link UnsupportedOperationException}
+ * if modifying the IndexBuffer is not supported (e.g. virtual index
+ * buffers).
+ */
+ public abstract void put(int i, int value);
+
+ /**
+ * Returns the size of the index buffer.
+ *
+ * @return the size of the index buffer.
+ */
+ public abstract int size();
+
+ /**
+ * Returns the underlying data-type specific {@link Buffer}.
+ * Implementations may return null if there's no underlying
+ * buffer.
+ *
+ * @return the underlying {@link Buffer}.
+ */
+ public abstract Buffer getBuffer();
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/IndexByteBuffer.java b/engine/src/core/com/jme3/scene/mesh/IndexByteBuffer.java
new file mode 100644
index 0000000..fd3bec4
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/IndexByteBuffer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.mesh;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+/**
+ * IndexBuffer implementation for {@link ByteBuffer}s.
+ *
+ * @author lex
+ */
+public class IndexByteBuffer extends IndexBuffer {
+
+ private ByteBuffer buf;
+
+ public IndexByteBuffer(ByteBuffer buffer) {
+ this.buf = buffer;
+ }
+
+ @Override
+ public int get(int i) {
+ return buf.get(i) & 0x000000FF;
+ }
+
+ @Override
+ public void put(int i, int value) {
+ buf.put(i, (byte) value);
+ }
+
+ @Override
+ public int size() {
+ return buf.limit();
+ }
+
+ @Override
+ public Buffer getBuffer() {
+ return buf;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/IndexIntBuffer.java b/engine/src/core/com/jme3/scene/mesh/IndexIntBuffer.java
new file mode 100644
index 0000000..3369301
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/IndexIntBuffer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.mesh;
+
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+
+/**
+ * IndexBuffer implementation for {@link IntBuffer}s.
+ *
+ * @author lex
+ */
+public class IndexIntBuffer extends IndexBuffer {
+
+ private IntBuffer buf;
+
+ public IndexIntBuffer(IntBuffer buffer) {
+ this.buf = buffer;
+ }
+
+ @Override
+ public int get(int i) {
+ return buf.get(i);
+ }
+
+ @Override
+ public void put(int i, int value) {
+ buf.put(i, value);
+ }
+
+ @Override
+ public int size() {
+ return buf.limit();
+ }
+
+ @Override
+ public Buffer getBuffer() {
+ return buf;
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/IndexShortBuffer.java b/engine/src/core/com/jme3/scene/mesh/IndexShortBuffer.java
new file mode 100644
index 0000000..5017e6f
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/IndexShortBuffer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.mesh;
+
+import java.nio.Buffer;
+import java.nio.ShortBuffer;
+
+/**
+ * IndexBuffer implementation for {@link ShortBuffer}s.
+ *
+ * @author lex
+ */
+public class IndexShortBuffer extends IndexBuffer {
+
+ private ShortBuffer buf;
+
+ public IndexShortBuffer(ShortBuffer buffer) {
+ this.buf = buffer;
+ }
+
+ @Override
+ public int get(int i) {
+ return buf.get(i) & 0x0000FFFF;
+ }
+
+ @Override
+ public void put(int i, int value) {
+ buf.put(i, (short) value);
+ }
+
+ @Override
+ public int size() {
+ return buf.limit();
+ }
+
+ @Override
+ public Buffer getBuffer() {
+ return buf;
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/VirtualIndexBuffer.java b/engine/src/core/com/jme3/scene/mesh/VirtualIndexBuffer.java
new file mode 100644
index 0000000..c1499cc
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/VirtualIndexBuffer.java
@@ -0,0 +1,106 @@
+package com.jme3.scene.mesh;
+
+import com.jme3.scene.Mesh.Mode;
+import java.nio.Buffer;
+
+/**
+ * IndexBuffer implementation that generates vertex indices sequentially
+ * based on a specific Mesh {@link Mode}.
+ * The generated indices are as if the mesh is in the given mode
+ * but contains no index buffer, thus this implementation will
+ * return the indices if the index buffer was there and contained sequential
+ * triangles.
+ * Example:
+ * <ul>
+ * <li>{@link Mode#Triangles}: 0, 1, 2 | 3, 4, 5 | 6, 7, 8 | ...</li>
+ * <li>{@link Mode#TriangleStrip}: 0, 1, 2 | 2, 1, 3 | 2, 3, 4 | ...</li>
+ * <li>{@link Mode#TriangleFan}: 0, 1, 2 | 0, 2, 3 | 0, 3, 4 | ...</li>
+ * </ul>
+ *
+ * @author Kirill Vainer
+ */
+public class VirtualIndexBuffer extends IndexBuffer {
+
+ protected int numVerts = 0;
+ protected int numIndices = 0;
+ protected Mode meshMode;
+
+ public VirtualIndexBuffer(int numVerts, Mode meshMode){
+ this.numVerts = numVerts;
+ this.meshMode = meshMode;
+ switch (meshMode) {
+ case Points:
+ numIndices = numVerts;
+ return;
+ case LineLoop:
+ numIndices = (numVerts - 1) * 2 + 1;
+ return;
+ case LineStrip:
+ numIndices = (numVerts - 1) * 2;
+ return;
+ case Lines:
+ numIndices = numVerts;
+ return;
+ case TriangleFan:
+ numIndices = (numVerts - 2) * 3;
+ return;
+ case TriangleStrip:
+ numIndices = (numVerts - 2) * 3;
+ return;
+ case Triangles:
+ numIndices = numVerts;
+ return;
+ case Hybrid:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public int get(int i) {
+ if (meshMode == Mode.Triangles || meshMode == Mode.Lines || meshMode == Mode.Points){
+ return i;
+ }else if (meshMode == Mode.LineStrip){
+ return (i + 1) / 2;
+ }else if (meshMode == Mode.LineLoop){
+ return (i == (numVerts-1)) ? 0 : ((i + 1) / 2);
+ }else if (meshMode == Mode.TriangleStrip){
+ int triIndex = i/3;
+ int vertIndex = i%3;
+ boolean isBack = (i/3)%2==1;
+ if (!isBack){
+ return triIndex + vertIndex;
+ }else{
+ switch (vertIndex){
+ case 0: return triIndex + 1;
+ case 1: return triIndex;
+ case 2: return triIndex + 2;
+ default: throw new AssertionError();
+ }
+ }
+ }else if (meshMode == Mode.TriangleFan){
+ int vertIndex = i%3;
+ if (vertIndex == 0)
+ return 0;
+ else
+ return (i / 3) + vertIndex;
+ }else{
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public void put(int i, int value) {
+ throw new UnsupportedOperationException("Does not represent index buffer");
+ }
+
+ @Override
+ public int size() {
+ return numIndices;
+ }
+
+ @Override
+ public Buffer getBuffer() {
+ return null;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/WrappedIndexBuffer.java b/engine/src/core/com/jme3/scene/mesh/WrappedIndexBuffer.java
new file mode 100644
index 0000000..056a1cd
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/WrappedIndexBuffer.java
@@ -0,0 +1,86 @@
+package com.jme3.scene.mesh;
+
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer.Type;
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * <code>WrappedIndexBuffer</code> converts vertex indices from a non list based
+ * mesh mode such as {@link Mode#TriangleStrip} or {@link Mode#LineLoop}
+ * into a list based mode such as {@link Mode#Triangles} or {@link Mode#Lines}.
+ * As it is often more convenient to read vertex data in list format
+ * than in a non-list format, using this class is recommended to avoid
+ * convoluting classes used to process mesh data from an external source.
+ *
+ * @author Kirill Vainer
+ */
+public class WrappedIndexBuffer extends VirtualIndexBuffer {
+
+ private final IndexBuffer ib;
+
+ public WrappedIndexBuffer(Mesh mesh){
+ super(mesh.getVertexCount(), mesh.getMode());
+ this.ib = mesh.getIndexBuffer();
+ switch (meshMode){
+ case Points:
+ numIndices = mesh.getTriangleCount();
+ break;
+ case Lines:
+ case LineLoop:
+ case LineStrip:
+ numIndices = mesh.getTriangleCount() * 2;
+ break;
+ case Triangles:
+ case TriangleStrip:
+ case TriangleFan:
+ numIndices = mesh.getTriangleCount() * 3;
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public int get(int i) {
+ int superIdx = super.get(i);
+ return ib.get(superIdx);
+ }
+
+ @Override
+ public Buffer getBuffer() {
+ return ib.getBuffer();
+ }
+
+ public static void convertToList(Mesh mesh){
+ IndexBuffer inBuf = mesh.getIndicesAsList();
+ IndexBuffer outBuf = IndexBuffer.createIndexBuffer(mesh.getVertexCount(),
+ inBuf.size());
+
+ for (int i = 0; i < inBuf.size(); i++){
+ outBuf.put(i, inBuf.get(i));
+ }
+
+ mesh.clearBuffer(Type.Index);
+ switch (mesh.getMode()){
+ case LineLoop:
+ case LineStrip:
+ mesh.setMode(Mode.Lines);
+ break;
+ case TriangleStrip:
+ case TriangleFan:
+ mesh.setMode(Mode.Triangles);
+ break;
+ default:
+ break;
+ }
+ if (outBuf instanceof IndexIntBuffer){
+ mesh.setBuffer(Type.Index, 3, (IntBuffer)outBuf.getBuffer());
+ }else{
+ mesh.setBuffer(Type.Index, 3, (ShortBuffer)outBuf.getBuffer());
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/mesh/package.html b/engine/src/core/com/jme3/scene/mesh/package.html
new file mode 100644
index 0000000..5362c52
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/mesh/package.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.scene.mesh</code> package contains utilities
+for reading from {@link com.jme3.scene.mesh.IndexBuffer index buffers}.
+Several implementations are provided of the {@link com.jme3.scene.mesh.IndexBuffer}
+class:
+<ul>
+ <li>{@link com.jme3.scene.mesh.IndexByteBuffer} - For reading 8-bit index buffers</li>
+ <li>{@link com.jme3.scene.mesh.IndexShortBuffer} - For reading 16-bit index buffers</li>
+ <li>{@link com.jme3.scene.mesh.IndexIntBuffer} - For reading 32-bit index buffers</li>
+ <li>{@link com.jme3.scene.mesh.VirtualIndexBuffer} - For reading "virtual indices", for
+ those meshes that do not have an index buffer</li>
+ <li>{@link com.jme3.scene.mesh.WrappedIndexBuffer} - For converting from
+ non-list based mode indices to list based</li>
+</ul>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/scene/package.html b/engine/src/core/com/jme3/scene/package.html
new file mode 100644
index 0000000..53f8105
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/package.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.input</code> package contains the scene graph implementation
+in jMonkeyEngine.
+
+<p>
+ The scene graph is the most important package in jME, as it is the API
+ used to manage scene elements so that they can be rendered.
+ The {@link com.jme3.scene.Spatial} class provides a common base class
+ for all scene graph elements. The {@link com.jme3.scene.Node} class provides
+ the "branches" in the graph, used to organize elements in a tree
+ hierarchy. The {@link com.jme3.scene.Geometry} is the leaf class that
+ will contain a {@link com.jme3.scene.Mesh} object (geometry data
+ such as vertex positions, normals, etc) and a {@link com.jme3.scene.Material}
+ object containing information on how the geometry should be shaded.
+</p>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/scene/shape/AbstractBox.java b/engine/src/core/com/jme3/scene/shape/AbstractBox.java
new file mode 100644
index 0000000..6c68dc5
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/AbstractBox.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import java.io.IOException;
+
+/**
+ * An eight sided box.
+ * <p>
+ * A {@code Box} is defined by a minimal point and a maximal point. The eight
+ * vertices that make the box are then computed, they are computed in such
+ * a way as to generate an axis-aligned box.
+ * <p>
+ * This class does not control how the geometry data is generated, see {@link Box}
+ * for that.
+ *
+ * @author <a href="mailto:ianp@ianp.org">Ian Phillips</a>
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public abstract class AbstractBox extends Mesh {
+
+ public final Vector3f center = new Vector3f(0f, 0f, 0f);
+
+ public float xExtent, yExtent, zExtent;
+
+ public AbstractBox() {
+ super();
+ }
+
+ /**
+ * Gets the array or vectors representing the 8 vertices of the box.
+ *
+ * @return a newly created array of vertex vectors.
+ */
+ protected final Vector3f[] computeVertices() {
+ Vector3f[] axes = {
+ Vector3f.UNIT_X.mult(xExtent),
+ Vector3f.UNIT_Y.mult(yExtent),
+ Vector3f.UNIT_Z.mult(zExtent)
+ };
+ return new Vector3f[] {
+ center.subtract(axes[0]).subtractLocal(axes[1]).subtractLocal(axes[2]),
+ center.add(axes[0]).subtractLocal(axes[1]).subtractLocal(axes[2]),
+ center.add(axes[0]).addLocal(axes[1]).subtractLocal(axes[2]),
+ center.subtract(axes[0]).addLocal(axes[1]).subtractLocal(axes[2]),
+ center.add(axes[0]).subtractLocal(axes[1]).addLocal(axes[2]),
+ center.subtract(axes[0]).subtractLocal(axes[1]).addLocal(axes[2]),
+ center.add(axes[0]).addLocal(axes[1]).addLocal(axes[2]),
+ center.subtract(axes[0]).addLocal(axes[1]).addLocal(axes[2])
+ };
+ }
+
+ /**
+ * Convert the indices into the list of vertices that define the box's geometry.
+ */
+ protected abstract void duUpdateGeometryIndices();
+
+ /**
+ * Update the normals of each of the box's planes.
+ */
+ protected abstract void duUpdateGeometryNormals();
+
+ /**
+ * Update the points that define the texture of the box.
+ * <p>
+ * It's a one-to-one ratio, where each plane of the box has it's own copy
+ * of the texture. That is, the texture is repeated one time for each face.
+ */
+ protected abstract void duUpdateGeometryTextures();
+
+ /**
+ * Update the position of the vertices that define the box.
+ * <p>
+ * These eight points are determined from the minimum and maximum point.
+ */
+ protected abstract void duUpdateGeometryVertices();
+
+ /**
+ * Get the center point of this box.
+ */
+ public final Vector3f getCenter() {
+ return center;
+ }
+
+ /**
+ * Get the x-axis size (extent) of this box.
+ */
+ public final float getXExtent() {
+ return xExtent;
+ }
+
+ /**
+ * Get the y-axis size (extent) of this box.
+ */
+ public final float getYExtent() {
+ return yExtent;
+ }
+
+ /**
+ * Get the z-axis size (extent) of this box.
+ */
+ public final float getZExtent() {
+ return zExtent;
+ }
+
+ /**
+ * Rebuilds the box after a property has been directly altered.
+ * <p>
+ * For example, if you call {@code getXExtent().x = 5.0f} then you will
+ * need to call this method afterwards in order to update the box.
+ */
+ public final void updateGeometry() {
+ duUpdateGeometryVertices();
+ duUpdateGeometryNormals();
+ duUpdateGeometryTextures();
+ duUpdateGeometryIndices();
+ }
+
+ /**
+ * Rebuilds this box based on a new set of parameters.
+ * <p>
+ * Note that the actual sides will be twice the given extent values because
+ * the box extends in both directions from the center for each extent.
+ *
+ * @param center the center of the box.
+ * @param x the x extent of the box, in each directions.
+ * @param y the y extent of the box, in each directions.
+ * @param z the z extent of the box, in each directions.
+ */
+ public final void updateGeometry(Vector3f center, float x, float y, float z) {
+ if (center != null) {this.center.set(center); }
+ this.xExtent = x;
+ this.yExtent = y;
+ this.zExtent = z;
+ updateGeometry();
+ }
+
+ /**
+ * Rebuilds this box based on a new set of parameters.
+ * <p>
+ * The box is updated so that the two opposite corners are {@code minPoint}
+ * and {@code maxPoint}, the other corners are created from those two positions.
+ *
+ * @param minPoint the new minimum point of the box.
+ * @param maxPoint the new maximum point of the box.
+ */
+ public final void updateGeometry(Vector3f minPoint, Vector3f maxPoint) {
+ center.set(maxPoint).addLocal(minPoint).multLocal(0.5f);
+ float x = maxPoint.x - center.x;
+ float y = maxPoint.y - center.y;
+ float z = maxPoint.z - center.z;
+ updateGeometry(center, x, y, z);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ xExtent = capsule.readFloat("xExtent", 0);
+ yExtent = capsule.readFloat("yExtent", 0);
+ zExtent = capsule.readFloat("zExtent", 0);
+ center.set((Vector3f) capsule.readSavable("center", Vector3f.ZERO.clone()));
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(xExtent, "xExtent", 0);
+ capsule.write(yExtent, "yExtent", 0);
+ capsule.write(zExtent, "zExtent", 0);
+ capsule.write(center, "center", Vector3f.ZERO);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/scene/shape/Box.java b/engine/src/core/com/jme3/scene/shape/Box.java
new file mode 100644
index 0000000..307547e
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Box.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// $Id: Box.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+
+/**
+ * A box with solid (filled) faces.
+ *
+ * @author Mark Powell
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class Box extends AbstractBox {
+
+ private static final short[] GEOMETRY_INDICES_DATA = {
+ 2, 1, 0, 3, 2, 0, // back
+ 6, 5, 4, 7, 6, 4, // right
+ 10, 9, 8, 11, 10, 8, // front
+ 14, 13, 12, 15, 14, 12, // left
+ 18, 17, 16, 19, 18, 16, // top
+ 22, 21, 20, 23, 22, 20 // bottom
+ };
+
+ private static final float[] GEOMETRY_NORMALS_DATA = {
+ 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, // back
+ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // right
+ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // front
+ -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // left
+ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // top
+ 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0 // bottom
+ };
+
+ private static final float[] GEOMETRY_TEXTURE_DATA = {
+ 1, 0, 0, 0, 0, 1, 1, 1, // back
+ 1, 0, 0, 0, 0, 1, 1, 1, // right
+ 1, 0, 0, 0, 0, 1, 1, 1, // front
+ 1, 0, 0, 0, 0, 1, 1, 1, // left
+ 1, 0, 0, 0, 0, 1, 1, 1, // top
+ 1, 0, 0, 0, 0, 1, 1, 1 // bottom
+ };
+
+ /**
+ * Creates a new box.
+ * <p>
+ * The box has a center of 0,0,0 and extends in the out from the center by
+ * the given amount in <em>each</em> direction. So, for example, a box
+ * with extent of 0.5 would be the unit cube.
+ *
+ * @param x the size of the box along the x axis, in both directions.
+ * @param y the size of the box along the y axis, in both directions.
+ * @param z the size of the box along the z axis, in both directions.
+ */
+ public Box(float x, float y, float z) {
+ super();
+ updateGeometry(Vector3f.ZERO, x, y, z);
+ }
+
+ /**
+ * Creates a new box.
+ * <p>
+ * The box has the given center and extends in the out from the center by
+ * the given amount in <em>each</em> direction. So, for example, a box
+ * with extent of 0.5 would be the unit cube.
+ *
+ * @param center the center of the box.
+ * @param x the size of the box along the x axis, in both directions.
+ * @param y the size of the box along the y axis, in both directions.
+ * @param z the size of the box along the z axis, in both directions.
+ */
+ public Box(Vector3f center, float x, float y, float z) {
+ super();
+ updateGeometry(center, x, y, z);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Box</code> object.
+ * <p>
+ * The minimum and maximum point are provided, these two points define the
+ * shape and size of the box but not it's orientation or position. You should
+ * use the {@link #setLocalTranslation()} and {@link #setLocalRotation()}
+ * methods to define those properties.
+ *
+ * @param min the minimum point that defines the box.
+ * @param max the maximum point that defines the box.
+ */
+ public Box(Vector3f min, Vector3f max) {
+ super();
+ updateGeometry(min, max);
+ }
+
+ /**
+ * Empty constructor for serialization only. Do not use.
+ */
+ public Box(){
+ super();
+ }
+
+ /**
+ * Creates a clone of this box.
+ * <p>
+ * The cloned box will have '_clone' appended to it's name, but all other
+ * properties will be the same as this box.
+ */
+ @Override
+ public Box clone() {
+ return new Box(center.clone(), xExtent, yExtent, zExtent);
+ }
+
+ protected void duUpdateGeometryIndices() {
+ if (getBuffer(Type.Index) == null){
+ setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(GEOMETRY_INDICES_DATA));
+ }
+ }
+
+ protected void duUpdateGeometryNormals() {
+ if (getBuffer(Type.Normal) == null){
+ setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(GEOMETRY_NORMALS_DATA));
+ }
+ }
+
+ protected void duUpdateGeometryTextures() {
+ if (getBuffer(Type.TexCoord) == null){
+ setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(GEOMETRY_TEXTURE_DATA));
+ }
+ }
+
+ protected void duUpdateGeometryVertices() {
+ FloatBuffer fpb = BufferUtils.createVector3Buffer(24);
+ Vector3f[] v = computeVertices();
+ fpb.put(new float[] {
+ v[0].x, v[0].y, v[0].z, v[1].x, v[1].y, v[1].z, v[2].x, v[2].y, v[2].z, v[3].x, v[3].y, v[3].z, // back
+ v[1].x, v[1].y, v[1].z, v[4].x, v[4].y, v[4].z, v[6].x, v[6].y, v[6].z, v[2].x, v[2].y, v[2].z, // right
+ v[4].x, v[4].y, v[4].z, v[5].x, v[5].y, v[5].z, v[7].x, v[7].y, v[7].z, v[6].x, v[6].y, v[6].z, // front
+ v[5].x, v[5].y, v[5].z, v[0].x, v[0].y, v[0].z, v[3].x, v[3].y, v[3].z, v[7].x, v[7].y, v[7].z, // left
+ v[2].x, v[2].y, v[2].z, v[6].x, v[6].y, v[6].z, v[7].x, v[7].y, v[7].z, v[3].x, v[3].y, v[3].z, // top
+ v[0].x, v[0].y, v[0].z, v[5].x, v[5].y, v[5].z, v[4].x, v[4].y, v[4].z, v[1].x, v[1].y, v[1].z // bottom
+ });
+ setBuffer(Type.Position, 3, fpb);
+ updateBound();
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/shape/Curve.java b/engine/src/core/com/jme3/scene/shape/Curve.java
new file mode 100644
index 0000000..fbeda59
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Curve.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.shape;
+
+import com.jme3.math.Spline;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A <code>Curve</code> is a visual, line-based representation of a {@link Spline}.
+ * The underlying Spline will be sampled N times where N is the number of
+ * segments as specified in the constructor. Each segment will represent
+ * one line in the generated mesh.
+ *
+ * @author Nehon
+ */
+public class Curve extends Mesh {
+
+ private Spline spline;
+ private Vector3f temp = new Vector3f();
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Curve(){
+ }
+
+ /**
+ * Create a curve mesh.
+ * Use a CatmullRom spline model that does not cycle.
+ *
+ * @param controlPoints the control points to use to create this curve
+ * @param nbSubSegments the number of subsegments between the control points
+ */
+ public Curve(Vector3f[] controlPoints, int nbSubSegments) {
+ this(new Spline(Spline.SplineType.CatmullRom, controlPoints, 10, false), nbSubSegments);
+ }
+
+ /**
+ * Create a curve mesh from a Spline
+ *
+ * @param spline the spline to use
+ * @param nbSubSegments the number of subsegments between the control points
+ */
+ public Curve(Spline spline, int nbSubSegments) {
+ super();
+ this.spline = spline;
+ switch (spline.getType()) {
+ case CatmullRom:
+ this.createCatmullRomMesh(nbSubSegments);
+ break;
+ case Bezier:
+ this.createBezierMesh(nbSubSegments);
+ break;
+ case Nurb:
+ this.createNurbMesh(nbSubSegments);
+ break;
+ case Linear:
+ default:
+ this.createLinearMesh();
+ break;
+ }
+ }
+
+ private void createCatmullRomMesh(int nbSubSegments) {
+ float[] array = new float[((spline.getControlPoints().size() - 1) * nbSubSegments + 1) * 3];
+ short[] indices = new short[(spline.getControlPoints().size() - 1) * nbSubSegments * 2];
+ int i = 0;
+ int cptCP = 0;
+ for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ array[i] = vector3f.x;
+ i++;
+ array[i] = vector3f.y;
+ i++;
+ array[i] = vector3f.z;
+ i++;
+ if (it.hasNext()) {
+ for (int j = 1; j < nbSubSegments; j++) {
+ spline.interpolate((float) j / nbSubSegments, cptCP, temp);
+ array[i] = temp.getX();
+ i++;
+ array[i] = temp.getY();
+ i++;
+ array[i] = temp.getZ();
+ i++;
+ }
+ }
+ cptCP++;
+ }
+
+ i = 0;
+ int k = 0;
+ for (int j = 0; j < (spline.getControlPoints().size() - 1) * nbSubSegments; j++) {
+ k = j;
+ indices[i] = (short) k;
+ i++;
+ k++;
+ indices[i] = (short) k;
+ i++;
+ }
+
+ this.setMode(Mesh.Mode.Lines);
+ this.setBuffer(VertexBuffer.Type.Position, 3, array);
+ this.setBuffer(VertexBuffer.Type.Index, 2, indices);//(spline.getControlPoints().size() - 1) * nbSubSegments * 2
+ this.updateBound();
+ this.updateCounts();
+ }
+
+ /**
+ * This method creates the Bezier path for this curve.
+ *
+ * @param nbSubSegments
+ * amount of subsegments between position control points
+ */
+ private void createBezierMesh(int nbSubSegments) {
+ if(nbSubSegments==0) {
+ nbSubSegments = 1;
+ }
+ int centerPointsAmount = (spline.getControlPoints().size() + 2) / 3;
+
+ //calculating vertices
+ float[] array = new float[((centerPointsAmount - 1) * nbSubSegments + 1) * 3];
+ int currentControlPoint = 0;
+ List<Vector3f> controlPoints = spline.getControlPoints();
+ int lineIndex = 0;
+ for (int i = 0; i < centerPointsAmount - 1; ++i) {
+ Vector3f vector3f = controlPoints.get(currentControlPoint);
+ array[lineIndex++] = vector3f.x;
+ array[lineIndex++] = vector3f.y;
+ array[lineIndex++] = vector3f.z;
+ for (int j = 1; j < nbSubSegments; ++j) {
+ spline.interpolate((float) j / nbSubSegments, currentControlPoint, temp);
+ array[lineIndex++] = temp.getX();
+ array[lineIndex++] = temp.getY();
+ array[lineIndex++] = temp.getZ();
+ }
+ currentControlPoint += 3;
+ }
+ Vector3f vector3f = controlPoints.get(currentControlPoint);
+ array[lineIndex++] = vector3f.x;
+ array[lineIndex++] = vector3f.y;
+ array[lineIndex++] = vector3f.z;
+
+ //calculating indexes
+ int i = 0, k = 0;
+ short[] indices = new short[(centerPointsAmount - 1) * nbSubSegments << 1];
+ for (int j = 0; j < (centerPointsAmount - 1) * nbSubSegments; ++j) {
+ k = j;
+ indices[i++] = (short) k;
+ ++k;
+ indices[i++] = (short) k;
+ }
+
+ this.setMode(Mesh.Mode.Lines);
+ this.setBuffer(VertexBuffer.Type.Position, 3, array);
+ this.setBuffer(VertexBuffer.Type.Index, 2, indices);
+ this.updateBound();
+ this.updateCounts();
+ }
+
+ /**
+ * This method creates the Nurb path for this curve.
+ * @param nbSubSegments
+ * amount of subsegments between position control points
+ */
+ private void createNurbMesh(int nbSubSegments) {
+ float minKnot = spline.getMinNurbKnot();
+ float maxKnot = spline.getMaxNurbKnot();
+ float deltaU = (maxKnot - minKnot)/nbSubSegments;
+
+ float[] array = new float[(nbSubSegments + 1) * 3];
+
+ float u = minKnot;
+ Vector3f interpolationResult = new Vector3f();
+ for(int i=0;i<array.length;i+=3) {
+ spline.interpolate(u, 0, interpolationResult);
+ array[i] = interpolationResult.x;
+ array[i + 1] = interpolationResult.y;
+ array[i + 2] = interpolationResult.z;
+ u += deltaU;
+ }
+
+ //calculating indexes
+ int i = 0;
+ short[] indices = new short[nbSubSegments << 1];
+ for (int j = 0; j < nbSubSegments; ++j) {
+ indices[i++] = (short) j;
+ indices[i++] = (short) (j + 1);
+ }
+
+ this.setMode(Mesh.Mode.Lines);
+ this.setBuffer(VertexBuffer.Type.Position, 3, array);
+ this.setBuffer(VertexBuffer.Type.Index, 2, indices);
+ this.updateBound();
+ this.updateCounts();
+ }
+
+ private void createLinearMesh() {
+ float[] array = new float[spline.getControlPoints().size() * 3];
+ short[] indices = new short[(spline.getControlPoints().size() - 1) * 2];
+ int i = 0;
+ int cpt = 0;
+ int k = 0;
+ int j = 0;
+ for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ array[i] = vector3f.getX();
+ i++;
+ array[i] = vector3f.getY();
+ i++;
+ array[i] = vector3f.getZ();
+ i++;
+ if (it.hasNext()) {
+ k = j;
+ indices[cpt] = (short) k;
+ cpt++;
+ k++;
+ indices[cpt] = (short) k;
+ cpt++;
+ j++;
+ }
+ }
+
+ this.setMode(Mesh.Mode.Lines);
+ this.setBuffer(VertexBuffer.Type.Position, 3, array);
+ this.setBuffer(VertexBuffer.Type.Index, 2, indices);
+ this.updateBound();
+ this.updateCounts();
+ }
+
+ /**
+ * This method returns the length of the curve.
+ * @return the length of the curve
+ */
+ public float getLength() {
+ return spline.getTotalLength();
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/shape/Cylinder.java b/engine/src/core/com/jme3/scene/shape/Cylinder.java
new file mode 100644
index 0000000..85bb970
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Cylinder.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// $Id: Cylinder.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import static com.jme3.util.BufferUtils.*;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * A simple cylinder, defined by it's height and radius.
+ * (Ported to jME3)
+ *
+ * @author Mark Powell
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class Cylinder extends Mesh {
+
+ private int axisSamples;
+
+ private int radialSamples;
+
+ private float radius;
+ private float radius2;
+
+ private float height;
+ private boolean closed;
+ private boolean inverted;
+
+ /**
+ * Default constructor for serialization only. Do not use.
+ */
+ public Cylinder() {
+ }
+
+ /**
+ * Creates a new Cylinder. By default its center is the origin. Usually, a
+ * higher sample number creates a better looking cylinder, but at the cost
+ * of more vertex information.
+ *
+ * @param axisSamples
+ * Number of triangle samples along the axis.
+ * @param radialSamples
+ * Number of triangle samples along the radial.
+ * @param radius
+ * The radius of the cylinder.
+ * @param height
+ * The cylinder's height.
+ */
+ public Cylinder(int axisSamples, int radialSamples,
+ float radius, float height) {
+ this(axisSamples, radialSamples, radius, height, false);
+ }
+
+ /**
+ * Creates a new Cylinder. By default its center is the origin. Usually, a
+ * higher sample number creates a better looking cylinder, but at the cost
+ * of more vertex information. <br>
+ * If the cylinder is closed the texture is split into axisSamples parts:
+ * top most and bottom most part is used for top and bottom of the cylinder,
+ * rest of the texture for the cylinder wall. The middle of the top is
+ * mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need
+ * a suited distorted texture.
+ *
+ * @param axisSamples
+ * Number of triangle samples along the axis.
+ * @param radialSamples
+ * Number of triangle samples along the radial.
+ * @param radius
+ * The radius of the cylinder.
+ * @param height
+ * The cylinder's height.
+ * @param closed
+ * true to create a cylinder with top and bottom surface
+ */
+ public Cylinder(int axisSamples, int radialSamples,
+ float radius, float height, boolean closed) {
+ this(axisSamples, radialSamples, radius, height, closed, false);
+ }
+
+ /**
+ * Creates a new Cylinder. By default its center is the origin. Usually, a
+ * higher sample number creates a better looking cylinder, but at the cost
+ * of more vertex information. <br>
+ * If the cylinder is closed the texture is split into axisSamples parts:
+ * top most and bottom most part is used for top and bottom of the cylinder,
+ * rest of the texture for the cylinder wall. The middle of the top is
+ * mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need
+ * a suited distorted texture.
+ *
+ * @param axisSamples
+ * Number of triangle samples along the axis.
+ * @param radialSamples
+ * Number of triangle samples along the radial.
+ * @param radius
+ * The radius of the cylinder.
+ * @param height
+ * The cylinder's height.
+ * @param closed
+ * true to create a cylinder with top and bottom surface
+ * @param inverted
+ * true to create a cylinder that is meant to be viewed from the
+ * interior.
+ */
+ public Cylinder(int axisSamples, int radialSamples,
+ float radius, float height, boolean closed, boolean inverted) {
+ this(axisSamples, radialSamples, radius, radius, height, closed, inverted);
+ }
+
+ public Cylinder(int axisSamples, int radialSamples,
+ float radius, float radius2, float height, boolean closed, boolean inverted) {
+ super();
+ updateGeometry(axisSamples, radialSamples, radius, radius2, height, closed, inverted);
+ }
+
+ /**
+ * @return the number of samples along the cylinder axis
+ */
+ public int getAxisSamples() {
+ return axisSamples;
+ }
+
+ /**
+ * @return Returns the height.
+ */
+ public float getHeight() {
+ return height;
+ }
+
+ /**
+ * @return number of samples around cylinder
+ */
+ public int getRadialSamples() {
+ return radialSamples;
+ }
+
+ /**
+ * @return Returns the radius.
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ public float getRadius2() {
+ return radius2;
+ }
+
+ /**
+ * @return true if end caps are used.
+ */
+ public boolean isClosed() {
+ return closed;
+ }
+
+ /**
+ * @return true if normals and uvs are created for interior use
+ */
+ public boolean isInverted() {
+ return inverted;
+ }
+
+ /**
+ * Rebuilds the cylinder based on a new set of parameters.
+ *
+ * @param axisSamples the number of samples along the axis.
+ * @param radialSamples the number of samples around the radial.
+ * @param radius the radius of the bottom of the cylinder.
+ * @param radius2 the radius of the top of the cylinder.
+ * @param height the cylinder's height.
+ * @param closed should the cylinder have top and bottom surfaces.
+ * @param inverted is the cylinder is meant to be viewed from the inside.
+ */
+ public void updateGeometry(int axisSamples, int radialSamples,
+ float radius, float radius2, float height, boolean closed, boolean inverted) {
+ this.axisSamples = axisSamples + (closed ? 2 : 0);
+ this.radialSamples = radialSamples;
+ this.radius = radius;
+ this.radius2 = radius2;
+ this.height = height;
+ this.closed = closed;
+ this.inverted = inverted;
+
+// VertexBuffer pvb = getBuffer(Type.Position);
+// VertexBuffer nvb = getBuffer(Type.Normal);
+// VertexBuffer tvb = getBuffer(Type.TexCoord);
+
+ // Vertices
+ int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0);
+
+ setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount));
+
+ // Normals
+ setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount));
+
+ // Texture co-ordinates
+ setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount));
+
+ int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples;
+
+ setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount));
+
+ // generate geometry
+ float inverseRadial = 1.0f / radialSamples;
+ float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1);
+ float inverseAxisLessTexture = 1.0f / (axisSamples - 1);
+ float halfHeight = 0.5f * height;
+
+ // Generate points on the unit circle to be used in computing the mesh
+ // points on a cylinder slice.
+ float[] sin = new float[radialSamples + 1];
+ float[] cos = new float[radialSamples + 1];
+
+ for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
+ float angle = FastMath.TWO_PI * inverseRadial * radialCount;
+ cos[radialCount] = FastMath.cos(angle);
+ sin[radialCount] = FastMath.sin(angle);
+ }
+ sin[radialSamples] = sin[0];
+ cos[radialSamples] = cos[0];
+
+ // calculate normals
+ Vector3f[] vNormals = null;
+ Vector3f vNormal = Vector3f.UNIT_Z;
+
+ if ((height != 0.0f) && (radius != radius2)) {
+ vNormals = new Vector3f[radialSamples];
+ Vector3f vHeight = Vector3f.UNIT_Z.mult(height);
+ Vector3f vRadial = new Vector3f();
+
+ for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
+ vRadial.set(cos[radialCount], sin[radialCount], 0.0f);
+ Vector3f vRadius = vRadial.mult(radius);
+ Vector3f vRadius2 = vRadial.mult(radius2);
+ Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius));
+ Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z);
+ vNormals[radialCount] = vMantle.cross(vTangent).normalize();
+ }
+ }
+
+ FloatBuffer nb = getFloatBuffer(Type.Normal);
+ FloatBuffer pb = getFloatBuffer(Type.Position);
+ FloatBuffer tb = getFloatBuffer(Type.TexCoord);
+
+ // generate the cylinder itself
+ Vector3f tempNormal = new Vector3f();
+ for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) {
+ float axisFraction;
+ float axisFractionTexture;
+ int topBottom = 0;
+ if (!closed) {
+ axisFraction = axisCount * inverseAxisLess; // in [0,1]
+ axisFractionTexture = axisFraction;
+ } else {
+ if (axisCount == 0) {
+ topBottom = -1; // bottom
+ axisFraction = 0;
+ axisFractionTexture = inverseAxisLessTexture;
+ } else if (axisCount == axisSamples - 1) {
+ topBottom = 1; // top
+ axisFraction = 1;
+ axisFractionTexture = 1 - inverseAxisLessTexture;
+ } else {
+ axisFraction = (axisCount - 1) * inverseAxisLess;
+ axisFractionTexture = axisCount * inverseAxisLessTexture;
+ }
+ }
+
+ // compute center of slice
+ float z = -halfHeight + height * axisFraction;
+ Vector3f sliceCenter = new Vector3f(0, 0, z);
+
+ // compute slice vertices with duplication at end point
+ int save = i;
+ for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) {
+ float radialFraction = radialCount * inverseRadial; // in [0,1)
+ tempNormal.set(cos[radialCount], sin[radialCount], 0.0f);
+
+ if (vNormals != null) {
+ vNormal = vNormals[radialCount];
+ } else if (radius == radius2) {
+ vNormal = tempNormal;
+ }
+
+ if (topBottom == 0) {
+ if (!inverted)
+ nb.put(vNormal.x).put(vNormal.y).put(vNormal.z);
+ else
+ nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z);
+ } else {
+ nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1));
+ }
+
+ tempNormal.multLocal((radius - radius2) * axisFraction + radius2)
+ .addLocal(sliceCenter);
+ pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z);
+
+ tb.put((inverted ? 1 - radialFraction : radialFraction))
+ .put(axisFractionTexture);
+ }
+
+ BufferUtils.copyInternalVector3(pb, save, i);
+ BufferUtils.copyInternalVector3(nb, save, i);
+
+ tb.put((inverted ? 0.0f : 1.0f))
+ .put(axisFractionTexture);
+ }
+
+ if (closed) {
+ pb.put(0).put(0).put(-halfHeight); // bottom center
+ nb.put(0).put(0).put(-1 * (inverted ? -1 : 1));
+ tb.put(0.5f).put(0);
+ pb.put(0).put(0).put(halfHeight); // top center
+ nb.put(0).put(0).put(1 * (inverted ? -1 : 1));
+ tb.put(0.5f).put(1);
+ }
+
+ IndexBuffer ib = getIndexBuffer();
+ int index = 0;
+ // Connectivity
+ for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) {
+ int i0 = axisStart;
+ int i1 = i0 + 1;
+ axisStart += radialSamples + 1;
+ int i2 = axisStart;
+ int i3 = i2 + 1;
+ for (int i = 0; i < radialSamples; i++) {
+ if (closed && axisCount == 0) {
+ if (!inverted) {
+ ib.put(index++, i0++);
+ ib.put(index++, vertCount - 2);
+ ib.put(index++, i1++);
+ } else {
+ ib.put(index++, i0++);
+ ib.put(index++, i1++);
+ ib.put(index++, vertCount - 2);
+ }
+ } else if (closed && axisCount == axisSamples - 2) {
+ ib.put(index++, i2++);
+ ib.put(index++, inverted ? vertCount - 1 : i3++);
+ ib.put(index++, inverted ? i3++ : vertCount - 1);
+ } else {
+ ib.put(index++, i0++);
+ ib.put(index++, inverted ? i2 : i1);
+ ib.put(index++, inverted ? i1 : i2);
+ ib.put(index++, i1++);
+ ib.put(index++, inverted ? i2++ : i3++);
+ ib.put(index++, inverted ? i3++ : i2++);
+ }
+ }
+ }
+
+ updateBound();
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ axisSamples = capsule.readInt("axisSamples", 0);
+ radialSamples = capsule.readInt("radialSamples", 0);
+ radius = capsule.readFloat("radius", 0);
+ radius2 = capsule.readFloat("radius2", 0);
+ height = capsule.readFloat("height", 0);
+ closed = capsule.readBoolean("closed", false);
+ inverted = capsule.readBoolean("inverted", false);
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(axisSamples, "axisSamples", 0);
+ capsule.write(radialSamples, "radialSamples", 0);
+ capsule.write(radius, "radius", 0);
+ capsule.write(radius2, "radius2", 0);
+ capsule.write(height, "height", 0);
+ capsule.write(closed, "closed", false);
+ capsule.write(inverted, "inverted", false);
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/scene/shape/Dome.java b/engine/src/core/com/jme3/scene/shape/Dome.java
new file mode 100644
index 0000000..6f429fc
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Dome.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// $Id: Dome.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * A hemisphere.
+ *
+ * @author Peter Andersson
+ * @author Joshua Slack (Original sphere code that was adapted)
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class Dome extends Mesh {
+
+ private int planes;
+ private int radialSamples;
+ /** The radius of the dome */
+ private float radius;
+ /** The center of the dome */
+ private Vector3f center;
+ private boolean insideView = true;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Dome() {
+ }
+
+ /**
+ * Constructs a dome for use as a SkyDome. The SkyDome is centered at the origin
+ * and only visible from the inside.
+ * @param planes
+ * The number of planes along the Z-axis. Must be >= 2.
+ * Influences how round the arch of the dome is.
+ * @param radialSamples
+ * The number of samples along the radial.
+ * Influences how round the base of the dome is.
+ * @param radius
+ * Radius of the dome.
+ * @see #Dome(java.lang.String, com.jme.math.Vector3f, int, int, float)
+ */
+ public Dome(int planes, int radialSamples, float radius) {
+ this(new Vector3f(0, 0, 0), planes, radialSamples, radius);
+ }
+
+ /**
+ * Constructs a dome visible from the inside, e.g. for use as a SkyDome.
+ * All geometry data buffers are updated automatically. <br>
+ * For a cone, set planes=2. For a pyramid, set radialSamples=4 and planes=2.
+ * Increasing planes and radialSamples increase the quality of the dome.
+ *
+ * @param center
+ * Center of the dome.
+ * @param planes
+ * The number of planes along the Z-axis. Must be >= 2.
+ * Influences how round the arch of the dome is.
+ * @param radialSamples
+ * The number of samples along the radial.
+ * Influences how round the base of the dome is.
+ * @param radius
+ * The radius of the dome.
+ */
+ public Dome(Vector3f center, int planes, int radialSamples,
+ float radius) {
+ super();
+ updateGeometry(center, planes, radialSamples, radius, true);
+ }
+
+ /**
+ * Constructs a dome. Use this constructor for half-sphere, pyramids, or cones.
+ * All geometry data buffers are updated automatically. <br>
+ * For a cone, set planes=2. For a pyramid, set radialSamples=4 and planes=2.
+ * Setting higher values for planes and radialSamples increases
+ * the quality of the half-sphere.
+ *
+ * @param center
+ * Center of the dome.
+ * @param planes
+ * The number of planes along the Z-axis. Must be >= 2.
+ * Influences how round the arch of the dome is.
+ * @param radialSamples
+ * The number of samples along the radial.
+ * Influences how round the base of the dome is.
+ * @param radius
+ * The radius of the dome.
+ * @param insideView
+ * If true, the dome is only visible from the inside, like a SkyDome.
+ * If false, the dome is only visible from the outside.
+ */
+ public Dome(Vector3f center, int planes, int radialSamples,
+ float radius, boolean insideView) {
+ super();
+ updateGeometry(center, planes, radialSamples, radius, insideView);
+ }
+
+ public Vector3f getCenter() {
+ return center;
+ }
+
+ /**
+ * Get the number of planar segments along the z-axis of the dome.
+ */
+ public int getPlanes() {
+ return planes;
+ }
+
+ /**
+ * Get the number of samples radially around the main axis of the dome.
+ */
+ public int getRadialSamples() {
+ return radialSamples;
+ }
+
+ /**
+ * Get the radius of the dome.
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * Are the triangles connected in such a way as to present a view out from the dome or not.
+ */
+ public boolean isInsideView() {
+ return insideView;
+ }
+
+ /**
+ * Rebuilds the dome with a new set of parameters.
+ *
+ * @param center the new center of the dome.
+ * @param planes the number of planes along the Z-axis.
+ * @param radialSamples the new number of radial samples of the dome.
+ * @param radius the new radius of the dome.
+ * @param insideView should the dome be set up to be viewed from the inside looking out.
+ */
+ public void updateGeometry(Vector3f center, int planes,
+ int radialSamples, float radius, boolean insideView) {
+ this.insideView = insideView;
+ this.center = center != null ? center : new Vector3f(0, 0, 0);
+ this.planes = planes;
+ this.radialSamples = radialSamples;
+ this.radius = radius;
+
+ int vertCount = ((planes - 1) * (radialSamples + 1)) + 1;
+
+ // Allocate vertices, allocating one extra in each radial to get the
+ // correct texture coordinates
+// setVertexCount();
+// setVertexBuffer(createVector3Buffer(getVertexCount()));
+
+ // allocate normals
+// setNormalBuffer(createVector3Buffer(getVertexCount()));
+
+ // allocate texture coordinates
+// getTextureCoords().set(0, new TexCoords(createVector2Buffer(getVertexCount())));
+
+ FloatBuffer vb = BufferUtils.createVector3Buffer(vertCount);
+ FloatBuffer nb = BufferUtils.createVector3Buffer(vertCount);
+ FloatBuffer tb = BufferUtils.createVector2Buffer(vertCount);
+ setBuffer(Type.Position, 3, vb);
+ setBuffer(Type.Normal, 3, nb);
+ setBuffer(Type.TexCoord, 2, tb);
+
+ // generate geometry
+ float fInvRS = 1.0f / radialSamples;
+ float fYFactor = 1.0f / (planes - 1);
+
+ // Generate points on the unit circle to be used in computing the mesh
+ // points on a dome slice.
+ float[] afSin = new float[(radialSamples)];
+ float[] afCos = new float[(radialSamples)];
+ for (int iR = 0; iR < radialSamples; iR++) {
+ float fAngle = FastMath.TWO_PI * fInvRS * iR;
+ afCos[iR] = FastMath.cos(fAngle);
+ afSin[iR] = FastMath.sin(fAngle);
+ }
+
+ TempVars vars = TempVars.get();
+ Vector3f tempVc = vars.vect3;
+ Vector3f tempVb = vars.vect2;
+ Vector3f tempVa = vars.vect1;
+
+ // generate the dome itself
+ int i = 0;
+ for (int iY = 0; iY < (planes - 1); iY++, i++) {
+ float fYFraction = fYFactor * iY; // in (0,1)
+ float fY = radius * fYFraction;
+ // compute center of slice
+ Vector3f kSliceCenter = tempVb.set(center);
+ kSliceCenter.y += fY;
+
+ // compute radius of slice
+ float fSliceRadius = FastMath.sqrt(FastMath.abs(radius * radius - fY * fY));
+
+ // compute slice vertices
+ Vector3f kNormal;
+ int iSave = i;
+ for (int iR = 0; iR < radialSamples; iR++, i++) {
+ float fRadialFraction = iR * fInvRS; // in [0,1)
+ Vector3f kRadial = tempVc.set(afCos[iR], 0, afSin[iR]);
+ kRadial.mult(fSliceRadius, tempVa);
+ vb.put(kSliceCenter.x + tempVa.x).put(
+ kSliceCenter.y + tempVa.y).put(
+ kSliceCenter.z + tempVa.z);
+
+ BufferUtils.populateFromBuffer(tempVa, vb, i);
+ kNormal = tempVa.subtractLocal(center);
+ kNormal.normalizeLocal();
+ if (insideView) {
+ nb.put(kNormal.x).put(kNormal.y).put(kNormal.z);
+ } else {
+ nb.put(-kNormal.x).put(-kNormal.y).put(-kNormal.z);
+ }
+
+ tb.put(fRadialFraction).put(fYFraction);
+ }
+ BufferUtils.copyInternalVector3(vb, iSave, i);
+ BufferUtils.copyInternalVector3(nb, iSave, i);
+ tb.put(1.0f).put(fYFraction);
+ }
+
+ vars.release();
+
+ // pole
+ vb.put(center.x).put(center.y + radius).put(center.z);
+ nb.put(0).put(insideView ? 1 : -1).put(0);
+ tb.put(0.5f).put(1.0f);
+
+ // allocate connectivity
+ int triCount = (planes - 2) * radialSamples * 2 + radialSamples;
+ ShortBuffer ib = BufferUtils.createShortBuffer(3 * triCount);
+ setBuffer(Type.Index, 3, ib);
+
+ // generate connectivity
+ int index = 0;
+ // Generate only for middle planes
+ for (int plane = 1; plane < (planes - 1); plane++) {
+ int bottomPlaneStart = ((plane - 1) * (radialSamples + 1));
+ int topPlaneStart = (plane * (radialSamples + 1));
+ for (int sample = 0; sample < radialSamples; sample++, index += 6) {
+ if (insideView){
+ ib.put((short) (bottomPlaneStart + sample));
+ ib.put((short) (bottomPlaneStart + sample + 1));
+ ib.put((short) (topPlaneStart + sample));
+ ib.put((short) (bottomPlaneStart + sample + 1));
+ ib.put((short) (topPlaneStart + sample + 1));
+ ib.put((short) (topPlaneStart + sample));
+ }else{
+ ib.put((short) (bottomPlaneStart + sample));
+ ib.put((short) (topPlaneStart + sample));
+ ib.put((short) (bottomPlaneStart + sample + 1));
+ ib.put((short) (bottomPlaneStart + sample + 1));
+ ib.put((short) (topPlaneStart + sample));
+ ib.put((short) (topPlaneStart + sample + 1));
+ }
+ }
+ }
+
+ // pole triangles
+ int bottomPlaneStart = (planes - 2) * (radialSamples + 1);
+ for (int samples = 0; samples < radialSamples; samples++, index += 3) {
+ if (insideView){
+ ib.put((short) (bottomPlaneStart + samples));
+ ib.put((short) (bottomPlaneStart + samples + 1));
+ ib.put((short) (vertCount - 1));
+ }else{
+ ib.put((short) (bottomPlaneStart + samples));
+ ib.put((short) (vertCount - 1));
+ ib.put((short) (bottomPlaneStart + samples + 1));
+ }
+ }
+
+ updateBound();
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ planes = capsule.readInt("planes", 0);
+ radialSamples = capsule.readInt("radialSamples", 0);
+ radius = capsule.readFloat("radius", 0);
+ center = (Vector3f) capsule.readSavable("center", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(planes, "planes", 0);
+ capsule.write(radialSamples, "radialSamples", 0);
+ capsule.write(radius, "radius", 0);
+ capsule.write(center, "center", Vector3f.ZERO);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/shape/Line.java b/engine/src/core/com/jme3/scene/shape/Line.java
new file mode 100644
index 0000000..1edbba7
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Line.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * A simple line implementation with a start and an end.
+ *
+ * @author Brent Owens
+ */
+public class Line extends Mesh {
+
+ private Vector3f start;
+ private Vector3f end;
+
+ public Line() {
+ }
+
+ public Line(Vector3f start, Vector3f end) {
+ setMode(Mode.Lines);
+ updateGeometry(start, end);
+ }
+
+ protected void updateGeometry(Vector3f start, Vector3f end) {
+ this.start = start;
+ this.end = end;
+ setBuffer(Type.Position, 3, new float[]{start.x, start.y, start.z,
+ end.x, end.y, end.z,});
+
+
+ setBuffer(Type.TexCoord, 2, new float[]{0, 0,
+ 1, 1});
+
+ setBuffer(Type.Normal, 3, new float[]{0, 0, 1,
+ 0, 0, 1});
+
+ setBuffer(Type.Index, 3, new short[]{0, 1});
+
+ updateBound();
+ }
+
+ /**
+ * Update the start and end points of the line.
+ */
+ public void updatePoints(Vector3f start, Vector3f end) {
+ VertexBuffer posBuf = getBuffer(Type.Position);
+
+ FloatBuffer fb = (FloatBuffer) posBuf.getData();
+
+ fb.put(start.x).put(start.y).put(start.z);
+ fb.put(end.x).put(end.y).put(end.z);
+
+ posBuf.updateData(fb);
+
+ updateBound();
+ }
+
+ public Vector3f getEnd() {
+ return end;
+ }
+
+ public Vector3f getStart() {
+ return start;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule out = ex.getCapsule(this);
+
+ out.write(start, "startVertex", null);
+ out.write(end, "endVertex", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule in = im.getCapsule(this);
+
+ start = (Vector3f) in.readSavable("startVertex", null);
+ end = (Vector3f) in.readSavable("endVertex", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/shape/PQTorus.java b/engine/src/core/com/jme3/scene/shape/PQTorus.java
new file mode 100644
index 0000000..26baed7
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/PQTorus.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// $Id: PQTorus.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import static com.jme3.util.BufferUtils.*;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * A parameterized torus, also known as a <em>pq</em> torus.
+ *
+ * @author Joshua Slack, Eric Woroshow
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class PQTorus extends Mesh {
+
+ private float p, q;
+
+ private float radius, width;
+
+ private int steps, radialSamples;
+
+ public PQTorus() {
+ }
+
+ /**
+ * Creates a parameterized torus.
+ * <p>
+ * Steps and radialSamples are both degree of accuracy values.
+ *
+ * @param p the x/z oscillation.
+ * @param q the y oscillation.
+ * @param radius the radius of the PQTorus.
+ * @param width the width of the torus.
+ * @param steps the steps along the torus.
+ * @param radialSamples radial samples for the torus.
+ */
+ public PQTorus(float p, float q, float radius, float width,
+ int steps, int radialSamples) {
+ super();
+ updateGeometry(p, q, radius, width, steps, radialSamples);
+ }
+
+ public float getP() {
+ return p;
+ }
+
+ public float getQ() {
+ return q;
+ }
+
+ public int getRadialSamples() {
+ return radialSamples;
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public int getSteps() {
+ return steps;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ p = capsule.readFloat("p", 0);
+ q = capsule.readFloat("q", 0);
+ radius = capsule.readFloat("radius", 0);
+ width = capsule.readFloat("width", 0);
+ steps = capsule.readInt("steps", 0);
+ radialSamples = capsule.readInt("radialSamples", 0);
+ }
+
+ /**
+ * Rebuilds this torus based on a new set of parameters.
+ *
+ * @param p the x/z oscillation.
+ * @param q the y oscillation.
+ * @param radius the radius of the PQTorus.
+ * @param width the width of the torus.
+ * @param steps the steps along the torus.
+ * @param radialSamples radial samples for the torus.
+ */
+ public void updateGeometry(float p, float q, float radius, float width, int steps, int radialSamples) {
+ this.p = p;
+ this.q = q;
+ this.radius = radius;
+ this.width = width;
+ this.steps = steps;
+ this.radialSamples = radialSamples;
+
+ final float thetaStep = (FastMath.TWO_PI / steps);
+ final float betaStep = (FastMath.TWO_PI / radialSamples);
+ Vector3f[] torusPoints = new Vector3f[steps];
+
+ // Allocate all of the required buffers
+ int vertCount = radialSamples * steps;
+
+ FloatBuffer fpb = createVector3Buffer(vertCount);
+ FloatBuffer fnb = createVector3Buffer(vertCount);
+ FloatBuffer ftb = createVector2Buffer(vertCount);
+
+ Vector3f pointB = new Vector3f(), T = new Vector3f(), N = new Vector3f(), B = new Vector3f();
+ Vector3f tempNorm = new Vector3f();
+ float r, x, y, z, theta = 0.0f, beta = 0.0f;
+ int nvertex = 0;
+
+ // Move along the length of the pq torus
+ for (int i = 0; i < steps; i++) {
+ theta += thetaStep;
+ float circleFraction = ((float) i) / (float) steps;
+
+ // Find the point on the torus
+ r = (0.5f * (2.0f + FastMath.sin(q * theta)) * radius);
+ x = (r * FastMath.cos(p * theta) * radius);
+ y = (r * FastMath.sin(p * theta) * radius);
+ z = (r * FastMath.cos(q * theta) * radius);
+ torusPoints[i] = new Vector3f(x, y, z);
+
+ // Now find a point slightly farther along the torus
+ r = (0.5f * (2.0f + FastMath.sin(q * (theta + 0.01f))) * radius);
+ x = (r * FastMath.cos(p * (theta + 0.01f)) * radius);
+ y = (r * FastMath.sin(p * (theta + 0.01f)) * radius);
+ z = (r * FastMath.cos(q * (theta + 0.01f)) * radius);
+ pointB = new Vector3f(x, y, z);
+
+ // Approximate the Frenet Frame
+ T = pointB.subtract(torusPoints[i]);
+ N = torusPoints[i].add(pointB);
+ B = T.cross(N);
+ N = B.cross(T);
+
+ // Normalise the two vectors and then use them to create an oriented circle
+ N = N.normalize();
+ B = B.normalize();
+ beta = 0.0f;
+ for (int j = 0; j < radialSamples; j++, nvertex++) {
+ beta += betaStep;
+ float cx = FastMath.cos(beta) * width;
+ float cy = FastMath.sin(beta) * width;
+ float radialFraction = ((float) j) / radialSamples;
+ tempNorm.x = (cx * N.x + cy * B.x);
+ tempNorm.y = (cx * N.y + cy * B.y);
+ tempNorm.z = (cx * N.z + cy * B.z);
+ fnb.put(tempNorm.x).put(tempNorm.y).put(tempNorm.z);
+ tempNorm.addLocal(torusPoints[i]);
+ fpb.put(tempNorm.x).put(tempNorm.y).put(tempNorm.z);
+ ftb.put(radialFraction).put(circleFraction);
+ }
+ }
+
+ // Update the indices data
+ ShortBuffer sib = createShortBuffer(6 * vertCount);
+ for (int i = 0; i < vertCount; i++) {
+ sib.put(new short[] {
+ (short)(i),
+ (short)(i - radialSamples),
+ (short)(i + 1),
+ (short)(i + 1),
+ (short)(i - radialSamples),
+ (short)(i - radialSamples + 1)
+ });
+ }
+ for (int i = 0, len = sib.capacity(); i < len; i++) {
+ int ind = sib.get(i);
+ if (ind < 0) {
+ ind += vertCount;
+ sib.put(i, (short) ind);
+ } else if (ind >= vertCount) {
+ ind -= vertCount;
+ sib.put(i, (short) ind);
+ }
+ }
+ sib.rewind();
+
+ setBuffer(Type.Position, 3, fpb);
+ setBuffer(Type.Normal, 3, fnb);
+ setBuffer(Type.TexCoord, 2, ftb);
+ setBuffer(Type.Index, 3, sib);
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(p, "p", 0);
+ capsule.write(q, "q", 0);
+ capsule.write(radius, "radius", 0);
+ capsule.write(width, "width", 0);
+ capsule.write(steps, "steps", 0);
+ capsule.write(radialSamples, "radialSamples", 0);
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/shape/Quad.java b/engine/src/core/com/jme3/scene/shape/Quad.java
new file mode 100644
index 0000000..1945358
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Quad.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.shape;
+
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+
+/**
+ * <code>Quad</code> represents a rectangular plane in space
+ * defined by 4 vertices. The quad's lower-left side is contained
+ * at the local space origin (0, 0, 0), while the upper-right
+ * side is located at the width/height coordinates (width, height, 0).
+ *
+ * @author Kirill Vainer
+ */
+public class Quad extends Mesh {
+
+ private float width;
+ private float height;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Quad(){
+ }
+
+ /**
+ * Create a quad with the given width and height. The quad
+ * is always created in the XY plane.
+ *
+ * @param width The X extent or width
+ * @param height The Y extent or width
+ */
+ public Quad(float width, float height){
+ updateGeometry(width, height);
+ }
+
+ /**
+ * Create a quad with the given width and height. The quad
+ * is always created in the XY plane.
+ *
+ * @param width The X extent or width
+ * @param height The Y extent or width
+ * @param flipCoords If true, the texture coordinates will be flipped
+ * along the Y axis.
+ */
+ public Quad(float width, float height, boolean flipCoords){
+ updateGeometry(width, height, flipCoords);
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+
+ public void updateGeometry(float width, float height){
+ updateGeometry(width, height, false);
+ }
+
+ public void updateGeometry(float width, float height, boolean flipCoords) {
+ this.width = width;
+ this.height = height;
+ setBuffer(Type.Position, 3, new float[]{0, 0, 0,
+ width, 0, 0,
+ width, height, 0,
+ 0, height, 0
+ });
+
+
+ if (flipCoords){
+ setBuffer(Type.TexCoord, 2, new float[]{0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0});
+ }else{
+ setBuffer(Type.TexCoord, 2, new float[]{0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1});
+ }
+ setBuffer(Type.Normal, 3, new float[]{0, 0, 1,
+ 0, 0, 1,
+ 0, 0, 1,
+ 0, 0, 1});
+ if (height < 0){
+ setBuffer(Type.Index, 3, new short[]{0, 2, 1,
+ 0, 3, 2});
+ }else{
+ setBuffer(Type.Index, 3, new short[]{0, 1, 2,
+ 0, 2, 3});
+ }
+
+ updateBound();
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/scene/shape/Sphere.java b/engine/src/core/com/jme3/scene/shape/Sphere.java
new file mode 100644
index 0000000..f8a5281
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Sphere.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// $Id: Sphere.java 4163 2009-03-25 01:14:55Z matt.yellen $
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * <code>Sphere</code> represents a 3D object with all points equidistance
+ * from a center point.
+ *
+ * @author Joshua Slack
+ * @version $Revision: 4163 $, $Date: 2009-03-24 21:14:55 -0400 (Tue, 24 Mar 2009) $
+ */
+public class Sphere extends Mesh {
+
+ public enum TextureMode {
+
+ /**
+ * Wrap texture radially and along z-axis
+ */
+ Original,
+ /**
+ * Wrap texure radially, but spherically project along z-axis
+ */
+ Projected,
+ /**
+ * Apply texture to each pole. Eliminates polar distortion,
+ * but mirrors the texture across the equator
+ */
+ Polar
+ }
+ protected int vertCount;
+ protected int triCount;
+ protected int zSamples;
+ protected int radialSamples;
+ protected boolean useEvenSlices;
+ protected boolean interior;
+ /** the distance from the center point each point falls on */
+ public float radius;
+ protected TextureMode textureMode = TextureMode.Original;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Sphere() {
+ }
+
+ /**
+ * Constructs a sphere. All geometry data buffers are updated automatically.
+ * Both zSamples and radialSamples increase the quality of the generated
+ * sphere.
+ *
+ * @param zSamples
+ * The number of samples along the Z.
+ * @param radialSamples
+ * The number of samples along the radial.
+ * @param radius
+ * The radius of the sphere.
+ */
+ public Sphere(int zSamples, int radialSamples, float radius) {
+ this(zSamples, radialSamples, radius, false, false);
+ }
+
+ /**
+ * Constructs a sphere. Additional arg to evenly space latitudinal slices
+ *
+ * @param zSamples
+ * The number of samples along the Z.
+ * @param radialSamples
+ * The number of samples along the radial.
+ * @param radius
+ * The radius of the sphere.
+ * @param useEvenSlices
+ * Slice sphere evenly along the Z axis
+ * @param interior
+ * Not yet documented
+ */
+ public Sphere(int zSamples, int radialSamples, float radius, boolean useEvenSlices, boolean interior) {
+ updateGeometry(zSamples, radialSamples, radius, useEvenSlices, interior);
+ }
+
+ public int getRadialSamples() {
+ return radialSamples;
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * @return Returns the textureMode.
+ */
+ public TextureMode getTextureMode() {
+ return textureMode;
+ }
+
+ public int getZSamples() {
+ return zSamples;
+ }
+
+ /**
+ * builds the vertices based on the radius, radial and zSamples.
+ */
+ private void setGeometryData() {
+ // allocate vertices
+ vertCount = (zSamples - 2) * (radialSamples + 1) + 2;
+
+ FloatBuffer posBuf = BufferUtils.createVector3Buffer(vertCount);
+
+ // allocate normals if requested
+ FloatBuffer normBuf = BufferUtils.createVector3Buffer(vertCount);
+
+ // allocate texture coordinates
+ FloatBuffer texBuf = BufferUtils.createVector2Buffer(vertCount);
+
+ setBuffer(Type.Position, 3, posBuf);
+ setBuffer(Type.Normal, 3, normBuf);
+ setBuffer(Type.TexCoord, 2, texBuf);
+
+ // generate geometry
+ float fInvRS = 1.0f / radialSamples;
+ float fZFactor = 2.0f / (zSamples - 1);
+
+ // Generate points on the unit circle to be used in computing the mesh
+ // points on a sphere slice.
+ float[] afSin = new float[(radialSamples + 1)];
+ float[] afCos = new float[(radialSamples + 1)];
+ for (int iR = 0; iR < radialSamples; iR++) {
+ float fAngle = FastMath.TWO_PI * fInvRS * iR;
+ afCos[iR] = FastMath.cos(fAngle);
+ afSin[iR] = FastMath.sin(fAngle);
+ }
+ afSin[radialSamples] = afSin[0];
+ afCos[radialSamples] = afCos[0];
+
+ TempVars vars = TempVars.get();
+ Vector3f tempVa = vars.vect1;
+ Vector3f tempVb = vars.vect2;
+ Vector3f tempVc = vars.vect3;
+
+ // generate the sphere itself
+ int i = 0;
+ for (int iZ = 1; iZ < (zSamples - 1); iZ++) {
+ float fAFraction = FastMath.HALF_PI * (-1.0f + fZFactor * iZ); // in (-pi/2, pi/2)
+ float fZFraction;
+ if (useEvenSlices) {
+ fZFraction = -1.0f + fZFactor * iZ; // in (-1, 1)
+ } else {
+ fZFraction = FastMath.sin(fAFraction); // in (-1,1)
+ }
+ float fZ = radius * fZFraction;
+
+ // compute center of slice
+ Vector3f kSliceCenter = tempVb.set(Vector3f.ZERO);
+ kSliceCenter.z += fZ;
+
+ // compute radius of slice
+ float fSliceRadius = FastMath.sqrt(FastMath.abs(radius * radius
+ - fZ * fZ));
+
+ // compute slice vertices with duplication at end point
+ Vector3f kNormal;
+ int iSave = i;
+ for (int iR = 0; iR < radialSamples; iR++) {
+ float fRadialFraction = iR * fInvRS; // in [0,1)
+ Vector3f kRadial = tempVc.set(afCos[iR], afSin[iR], 0);
+ kRadial.mult(fSliceRadius, tempVa);
+ posBuf.put(kSliceCenter.x + tempVa.x).put(
+ kSliceCenter.y + tempVa.y).put(
+ kSliceCenter.z + tempVa.z);
+
+ BufferUtils.populateFromBuffer(tempVa, posBuf, i);
+ kNormal = tempVa;
+ kNormal.normalizeLocal();
+ if (!interior) // allow interior texture vs. exterior
+ {
+ normBuf.put(kNormal.x).put(kNormal.y).put(
+ kNormal.z);
+ } else {
+ normBuf.put(-kNormal.x).put(-kNormal.y).put(
+ -kNormal.z);
+ }
+
+ if (textureMode == TextureMode.Original) {
+ texBuf.put(fRadialFraction).put(
+ 0.5f * (fZFraction + 1.0f));
+ } else if (textureMode == TextureMode.Projected) {
+ texBuf.put(fRadialFraction).put(
+ FastMath.INV_PI
+ * (FastMath.HALF_PI + FastMath.asin(fZFraction)));
+ } else if (textureMode == TextureMode.Polar) {
+ float r = (FastMath.HALF_PI - FastMath.abs(fAFraction)) / FastMath.PI;
+ float u = r * afCos[iR] + 0.5f;
+ float v = r * afSin[iR] + 0.5f;
+ texBuf.put(u).put(v);
+ }
+
+ i++;
+ }
+
+ BufferUtils.copyInternalVector3(posBuf, iSave, i);
+ BufferUtils.copyInternalVector3(normBuf, iSave, i);
+
+ if (textureMode == TextureMode.Original) {
+ texBuf.put(1.0f).put(
+ 0.5f * (fZFraction + 1.0f));
+ } else if (textureMode == TextureMode.Projected) {
+ texBuf.put(1.0f).put(
+ FastMath.INV_PI
+ * (FastMath.HALF_PI + FastMath.asin(fZFraction)));
+ } else if (textureMode == TextureMode.Polar) {
+ float r = (FastMath.HALF_PI - FastMath.abs(fAFraction)) / FastMath.PI;
+ texBuf.put(r + 0.5f).put(0.5f);
+ }
+
+ i++;
+ }
+
+ vars.release();
+
+ // south pole
+ posBuf.position(i * 3);
+ posBuf.put(0f).put(0f).put(-radius);
+
+ normBuf.position(i * 3);
+ if (!interior) {
+ normBuf.put(0).put(0).put(-1); // allow for inner
+ } // texture orientation
+ // later.
+ else {
+ normBuf.put(0).put(0).put(1);
+ }
+
+ texBuf.position(i * 2);
+
+ if (textureMode == TextureMode.Polar) {
+ texBuf.put(0.5f).put(0.5f);
+ } else {
+ texBuf.put(0.5f).put(0.0f);
+ }
+
+ i++;
+
+ // north pole
+ posBuf.put(0).put(0).put(radius);
+
+ if (!interior) {
+ normBuf.put(0).put(0).put(1);
+ } else {
+ normBuf.put(0).put(0).put(-1);
+ }
+
+ if (textureMode == TextureMode.Polar) {
+ texBuf.put(0.5f).put(0.5f);
+ } else {
+ texBuf.put(0.5f).put(1.0f);
+ }
+
+ updateBound();
+ setStatic();
+ }
+
+ /**
+ * sets the indices for rendering the sphere.
+ */
+ private void setIndexData() {
+ // allocate connectivity
+ triCount = 2 * (zSamples - 2) * radialSamples;
+ ShortBuffer idxBuf = BufferUtils.createShortBuffer(3 * triCount);
+ setBuffer(Type.Index, 3, idxBuf);
+
+ // generate connectivity
+ int index = 0;
+ for (int iZ = 0, iZStart = 0; iZ < (zSamples - 3); iZ++) {
+ int i0 = iZStart;
+ int i1 = i0 + 1;
+ iZStart += (radialSamples + 1);
+ int i2 = iZStart;
+ int i3 = i2 + 1;
+ for (int i = 0; i < radialSamples; i++, index += 6) {
+ if (!interior) {
+ idxBuf.put((short) i0++);
+ idxBuf.put((short) i1);
+ idxBuf.put((short) i2);
+ idxBuf.put((short) i1++);
+ idxBuf.put((short) i3++);
+ idxBuf.put((short) i2++);
+ } else { // inside view
+ idxBuf.put((short) i0++);
+ idxBuf.put((short) i2);
+ idxBuf.put((short) i1);
+ idxBuf.put((short) i1++);
+ idxBuf.put((short) i2++);
+ idxBuf.put((short) i3++);
+ }
+ }
+ }
+
+ // south pole triangles
+ for (int i = 0; i < radialSamples; i++, index += 3) {
+ if (!interior) {
+ idxBuf.put((short) i);
+ idxBuf.put((short) (vertCount - 2));
+ idxBuf.put((short) (i + 1));
+ } else { // inside view
+ idxBuf.put((short) i);
+ idxBuf.put((short) (i + 1));
+ idxBuf.put((short) (vertCount - 2));
+ }
+ }
+
+ // north pole triangles
+ int iOffset = (zSamples - 3) * (radialSamples + 1);
+ for (int i = 0; i < radialSamples; i++, index += 3) {
+ if (!interior) {
+ idxBuf.put((short) (i + iOffset));
+ idxBuf.put((short) (i + 1 + iOffset));
+ idxBuf.put((short) (vertCount - 1));
+ } else { // inside view
+ idxBuf.put((short) (i + iOffset));
+ idxBuf.put((short) (vertCount - 1));
+ idxBuf.put((short) (i + 1 + iOffset));
+ }
+ }
+ }
+
+ /**
+ * @param textureMode
+ * The textureMode to set.
+ */
+ public void setTextureMode(TextureMode textureMode) {
+ this.textureMode = textureMode;
+ setGeometryData();
+ }
+
+ /**
+ * Changes the information of the sphere into the given values.
+ *
+ * @param zSamples the number of zSamples of the sphere.
+ * @param radialSamples the number of radial samples of the sphere.
+ * @param radius the radius of the sphere.
+ */
+ public void updateGeometry(int zSamples, int radialSamples, float radius) {
+ updateGeometry(zSamples, radialSamples, radius, false, false);
+ }
+
+ public void updateGeometry(int zSamples, int radialSamples, float radius, boolean useEvenSlices, boolean interior) {
+ this.zSamples = zSamples;
+ this.radialSamples = radialSamples;
+ this.radius = radius;
+ this.useEvenSlices = useEvenSlices;
+ this.interior = interior;
+ setGeometryData();
+ setIndexData();
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ zSamples = capsule.readInt("zSamples", 0);
+ radialSamples = capsule.readInt("radialSamples", 0);
+ radius = capsule.readFloat("radius", 0);
+ useEvenSlices = capsule.readBoolean("useEvenSlices", false);
+ textureMode = capsule.readEnum("textureMode", TextureMode.class, TextureMode.Original);
+ interior = capsule.readBoolean("interior", false);
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(zSamples, "zSamples", 0);
+ capsule.write(radialSamples, "radialSamples", 0);
+ capsule.write(radius, "radius", 0);
+ capsule.write(useEvenSlices, "useEvenSlices", false);
+ capsule.write(textureMode, "textureMode", TextureMode.Original);
+ capsule.write(interior, "interior", false);
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/shape/StripBox.java b/engine/src/core/com/jme3/scene/shape/StripBox.java
new file mode 100644
index 0000000..81abff9
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/StripBox.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// $Id: Box.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+
+/**
+ * A box with solid (filled) faces.
+ *
+ * @author Mark Powell
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class StripBox extends AbstractBox {
+
+ private static final short[] GEOMETRY_INDICES_DATA =
+ { 1, 0, 4,
+ 5,
+ 7,
+ 0,
+ 3,
+ 1,
+ 2,
+ 4,
+ 6,
+ 7,
+ 2,
+ 3 };
+
+ private static final float[] GEOMETRY_TEXTURE_DATA = {
+ 1, 0,
+ 0, 0,
+ 0, 1,
+ 1, 1,
+
+ 1, 0,
+ 0, 0,
+ 1, 1,
+ 0, 1
+ };
+
+ /**
+ * Creates a new box.
+ * <p>
+ * The box has a center of 0,0,0 and extends in the out from the center by
+ * the given amount in <em>each</em> direction. So, for example, a box
+ * with extent of 0.5 would be the unit cube.
+ *
+ * @param x the size of the box along the x axis, in both directions.
+ * @param y the size of the box along the y axis, in both directions.
+ * @param z the size of the box along the z axis, in both directions.
+ */
+ public StripBox(float x, float y, float z) {
+ super();
+ updateGeometry(Vector3f.ZERO, x, y, z);
+ }
+
+ /**
+ * Creates a new box.
+ * <p>
+ * The box has the given center and extends in the out from the center by
+ * the given amount in <em>each</em> direction. So, for example, a box
+ * with extent of 0.5 would be the unit cube.
+ *
+ * @param center the center of the box.
+ * @param x the size of the box along the x axis, in both directions.
+ * @param y the size of the box along the y axis, in both directions.
+ * @param z the size of the box along the z axis, in both directions.
+ */
+ public StripBox(Vector3f center, float x, float y, float z) {
+ super();
+ updateGeometry(center, x, y, z);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Box</code> object.
+ * <p>
+ * The minimum and maximum point are provided, these two points define the
+ * shape and size of the box but not it’s orientation or position. You should
+ * use the {@link #setLocalTranslation()} and {@link #setLocalRotation()}
+ * methods to define those properties.
+ *
+ * @param min the minimum point that defines the box.
+ * @param max the maximum point that defines the box.
+ */
+ public StripBox(Vector3f min, Vector3f max) {
+ super();
+ updateGeometry(min, max);
+ }
+
+ /**
+ * Empty constructor for serialization only. Do not use.
+ */
+ public StripBox(){
+ super();
+ }
+
+ /**
+ * Creates a clone of this box.
+ * <p>
+ * The cloned box will have ‘_clone’ appended to it’s name, but all other
+ * properties will be the same as this box.
+ */
+ @Override
+ public StripBox clone() {
+ return new StripBox(center.clone(), xExtent, yExtent, zExtent);
+ }
+
+ protected void duUpdateGeometryIndices() {
+ if (getBuffer(Type.Index) == null){
+ setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(GEOMETRY_INDICES_DATA));
+ }
+ }
+
+ protected void duUpdateGeometryNormals() {
+ if (getBuffer(Type.Normal) == null){
+ float[] normals = new float[8 * 3];
+
+ Vector3f[] vert = computeVertices();
+ Vector3f norm = new Vector3f();
+
+ for (int i = 0; i < 8; i++) {
+ norm.set(vert[i]).normalizeLocal();
+
+ normals[i * 3 + 0] = norm.x;
+ normals[i * 3 + 1] = norm.x;
+ normals[i * 3 + 2] = norm.x;
+ }
+
+ setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
+ }
+ }
+
+ protected void duUpdateGeometryTextures() {
+ if (getBuffer(Type.TexCoord) == null){
+ setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(GEOMETRY_TEXTURE_DATA));
+ }
+ }
+
+ protected void duUpdateGeometryVertices() {
+ FloatBuffer fpb = BufferUtils.createVector3Buffer(8 * 3);
+ Vector3f[] v = computeVertices();
+ fpb.put(new float[] {
+ v[0].x, v[0].y, v[0].z,
+ v[1].x, v[1].y, v[1].z,
+ v[2].x, v[2].y, v[2].z,
+ v[3].x, v[3].y, v[3].z,
+ v[4].x, v[4].y, v[4].z,
+ v[5].x, v[5].y, v[5].z,
+ v[6].x, v[6].y, v[6].z,
+ v[7].x, v[7].y, v[7].z,
+ });
+ setBuffer(Type.Position, 3, fpb);
+ setMode(Mode.TriangleStrip);
+ updateBound();
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/scene/shape/Surface.java b/engine/src/core/com/jme3/scene/shape/Surface.java
new file mode 100644
index 0000000..77d259b
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Surface.java
@@ -0,0 +1,283 @@
+package com.jme3.scene.shape;
+
+import com.jme3.math.CurveAndSurfaceMath;
+import com.jme3.math.FastMath;
+import com.jme3.math.Spline.SplineType;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.util.BufferUtils;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class represents a surface described by knots, weights and control points.
+ * Currently the following types are supported:
+ * a) NURBS
+ * @author Marcin Roguski (Kealthas)
+ */
+public class Surface extends Mesh {
+
+ private SplineType type; //the type of the surface
+ private List<List<Vector4f>> controlPoints; //space control points and their weights
+ private List<Float>[] knots; //knots of the surface
+ private int basisUFunctionDegree; //the degree of basis U function
+ private int basisVFunctionDegree; //the degree of basis V function
+ private int uSegments; //the amount of U segments
+ private int vSegments; //the amount of V segments
+
+ /**
+ * Constructor. Constructs required surface.
+ * @param controlPoints space control points
+ * @param nurbKnots knots of the surface
+ * @param uSegments the amount of U segments
+ * @param vSegments the amount of V segments
+ * @param basisUFunctionDegree the degree of basis U function
+ * @param basisVFunctionDegree the degree of basis V function
+ */
+ private Surface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
+ int uSegments, int vSegments, int basisUFunctionDegree, int basisVFunctionDegree) {
+ this.validateInputData(controlPoints, nurbKnots, uSegments, vSegments);
+ this.type = SplineType.Nurb;
+ this.uSegments = uSegments;
+ this.vSegments = vSegments;
+ this.controlPoints = controlPoints;
+ this.knots = nurbKnots;
+ this.basisUFunctionDegree = basisUFunctionDegree;
+ CurveAndSurfaceMath.prepareNurbsKnots(nurbKnots[0], basisUFunctionDegree);
+ if (nurbKnots[1] != null) {
+ this.basisVFunctionDegree = basisVFunctionDegree;
+ CurveAndSurfaceMath.prepareNurbsKnots(nurbKnots[1], basisVFunctionDegree);
+ }
+
+ this.buildSurface();
+ }
+
+ /**
+ * This method creates a NURBS surface.
+ * @param controlPoints space control points
+ * @param nurbKnots knots of the surface
+ * @param uSegments the amount of U segments
+ * @param vSegments the amount of V segments
+ * @param basisUFunctionDegree the degree of basis U function
+ * @param basisVFunctionDegree the degree of basis V function
+ * @return an instance of NURBS surface
+ */
+ public static final Surface createNurbsSurface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
+ int uSegments, int vSegments, int basisUFunctionDegree, int basisVFunctionDegree) {
+ Surface result = new Surface(controlPoints, nurbKnots, uSegments, vSegments, basisUFunctionDegree, basisVFunctionDegree);
+ result.type = SplineType.Nurb;
+ return result;
+ }
+
+ /**
+ * This method creates the surface.
+ */
+ private void buildSurface() {
+ boolean smooth = true;//TODO: take smoothing into consideration
+ float minUKnot = this.getMinUNurbKnot();
+ float maxUKnot = this.getMaxUNurbKnot();
+ float deltaU = (maxUKnot - minUKnot) / uSegments;
+
+ float minVKnot = this.getMinVNurbKnot();
+ float maxVKnot = this.getMaxVNurbKnot();
+ float deltaV = (maxVKnot - minVKnot) / vSegments;
+
+ Vector3f[] vertices = new Vector3f[(uSegments + 1) * (vSegments + 1)];
+
+ float u = minUKnot, v = minVKnot;
+ int arrayIndex = 0;
+
+ for (int i = 0; i <= vSegments; ++i) {
+ for (int j = 0; j <= uSegments; ++j) {
+ Vector3f interpolationResult = new Vector3f();
+ CurveAndSurfaceMath.interpolate(u, v, controlPoints, knots, basisUFunctionDegree, basisVFunctionDegree, interpolationResult);
+ vertices[arrayIndex++] = interpolationResult;
+ u += deltaU;
+ }
+ u = minUKnot;
+ v += deltaV;
+ }
+
+ //adding indexes
+ int uVerticesAmount = uSegments + 1;
+ int[] indices = new int[uSegments * vSegments * 6];
+ arrayIndex = 0;
+ for (int i = 0; i < vSegments; ++i) {
+ for (int j = 0; j < uSegments; ++j) {
+ indices[arrayIndex++] = j + i * uVerticesAmount;
+ indices[arrayIndex++] = j + i * uVerticesAmount + 1;
+ indices[arrayIndex++] = j + i * uVerticesAmount + uVerticesAmount;
+ indices[arrayIndex++] = j + i * uVerticesAmount + 1;
+ indices[arrayIndex++] = j + i * uVerticesAmount + uVerticesAmount + 1;
+ indices[arrayIndex++] = j + i * uVerticesAmount + uVerticesAmount;
+ }
+ }
+
+ //normalMap merges normals of faces that will be rendered smooth
+ Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(vertices.length);
+ for (int i = 0; i < indices.length; i += 3) {
+ Vector3f n = FastMath.computeNormal(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
+ this.addNormal(n, normalMap, smooth, vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
+ }
+ //preparing normal list (the order of normals must match the order of vertices)
+ float[] normals = new float[vertices.length * 3];
+ arrayIndex = 0;
+ for (int i = 0; i < vertices.length; ++i) {
+ Vector3f n = normalMap.get(vertices[i]);
+ normals[arrayIndex++] = n.x;
+ normals[arrayIndex++] = n.y;
+ normals[arrayIndex++] = n.z;
+ }
+
+ this.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
+ this.setBuffer(VertexBuffer.Type.Index, 3, indices);
+ this.setBuffer(VertexBuffer.Type.Normal, 3, normals);
+ this.updateBound();
+ this.updateCounts();
+ }
+
+ public List<List<Vector4f>> getControlPoints() {
+ return controlPoints;
+ }
+
+ /**
+ * This method returns the amount of U control points.
+ * @return the amount of U control points
+ */
+ public int getUControlPointsAmount() {
+ return controlPoints.size();
+ }
+
+ /**
+ * This method returns the amount of V control points.
+ * @return the amount of V control points
+ */
+ public int getVControlPointsAmount() {
+ return controlPoints.get(0) == null ? 0 : controlPoints.get(0).size();
+ }
+
+ /**
+ * This method returns the degree of basis U function.
+ * @return the degree of basis U function
+ */
+ public int getBasisUFunctionDegree() {
+ return basisUFunctionDegree;
+ }
+
+ /**
+ * This method returns the degree of basis V function.
+ * @return the degree of basis V function
+ */
+ public int getBasisVFunctionDegree() {
+ return basisVFunctionDegree;
+ }
+
+ /**
+ * This method returns the knots for specified dimension (U knots - value: '0',
+ * V knots - value: '1').
+ * @param dim an integer specifying if the U or V knots are required
+ * @return an array of knots
+ */
+ public List<Float> getKnots(int dim) {
+ return knots[dim];
+ }
+
+ /**
+ * This method returns the type of the surface.
+ * @return the type of the surface
+ */
+ public SplineType getType() {
+ return type;
+ }
+
+ /**
+ * This method returns the minimum nurb curve U knot value.
+ * @return the minimum nurb curve knot value
+ */
+ private float getMinUNurbKnot() {
+ return knots[0].get(basisUFunctionDegree - 1);
+ }
+
+ /**
+ * This method returns the maximum nurb curve U knot value.
+ * @return the maximum nurb curve knot value
+ */
+ private float getMaxUNurbKnot() {
+ return knots[0].get(knots[0].size() - basisUFunctionDegree);
+ }
+
+ /**
+ * This method returns the minimum nurb curve U knot value.
+ * @return the minimum nurb curve knot value
+ */
+ private float getMinVNurbKnot() {
+ return knots[1].get(basisVFunctionDegree - 1);
+ }
+
+ /**
+ * This method returns the maximum nurb curve U knot value.
+ * @return the maximum nurb curve knot value
+ */
+ private float getMaxVNurbKnot() {
+ return knots[1].get(knots[1].size() - basisVFunctionDegree);
+ }
+
+ /**
+ * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
+ * @param normalToAdd
+ * a normal to be added
+ * @param normalMap
+ * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
+ * @param smooth
+ * the variable that indicates wheather to merge normals (creating the smooth mesh) or not
+ * @param vertices
+ * a list of vertices read from the blender file
+ */
+ private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
+ for (Vector3f v : vertices) {
+ Vector3f n = normalMap.get(v);
+ if (!smooth || n == null) {
+ normalMap.put(v, normalToAdd.clone());
+ } else {
+ n.addLocal(normalToAdd).normalizeLocal();
+ }
+ }
+ }
+
+ /**
+ * This method validates the input data. It throws {@link IllegalArgumentException} if
+ * the data is invalid.
+ * @param controlPoints space control points
+ * @param nurbKnots knots of the surface
+ * @param uSegments the amount of U segments
+ * @param vSegments the amount of V segments
+ */
+ private void validateInputData(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
+ int uSegments, int vSegments) {
+ int uPointsAmount = controlPoints.get(0).size();
+ for (int i = 1; i < controlPoints.size(); ++i) {
+ if (controlPoints.get(i).size() != uPointsAmount) {
+ throw new IllegalArgumentException("The amount of 'U' control points is invalid!");
+ }
+ }
+ if (uSegments <= 0) {
+ throw new IllegalArgumentException("U segments amount should be positive!");
+ }
+ if (vSegments < 0) {
+ throw new IllegalArgumentException("V segments amount cannot be negative!");
+ }
+ if (nurbKnots.length != 2) {
+ throw new IllegalArgumentException("Nurb surface should have two rows of knots!");
+ }
+ for (int i = 0; i < nurbKnots.length; ++i) {
+ for (int j = 0; j < nurbKnots[i].size() - 1; ++j) {
+ if (nurbKnots[i].get(j) > nurbKnots[i].get(j + 1)) {
+ throw new IllegalArgumentException("The knots' values cannot decrease!");
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/shape/Torus.java b/engine/src/core/com/jme3/scene/shape/Torus.java
new file mode 100644
index 0000000..8b9285a
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/shape/Torus.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// $Id: Torus.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * An ordinary (single holed) torus.
+ * <p>
+ * The center is by default the origin.
+ *
+ * @author Mark Powell
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class Torus extends Mesh {
+
+ private int circleSamples;
+
+ private int radialSamples;
+
+ private float innerRadius;
+
+ private float outerRadius;
+
+ public Torus() {
+ }
+
+ /**
+ * Constructs a new Torus. Center is the origin, but the Torus may be
+ * transformed.
+ *
+ * @param circleSamples
+ * The number of samples along the circles.
+ * @param radialSamples
+ * The number of samples along the radial.
+ * @param innerRadius
+ * The radius of the inner begining of the Torus.
+ * @param outerRadius
+ * The radius of the outter end of the Torus.
+ */
+ public Torus(int circleSamples, int radialSamples,
+ float innerRadius, float outerRadius) {
+ super();
+ updateGeometry(circleSamples, radialSamples, innerRadius, outerRadius);
+ }
+
+ public int getCircleSamples() {
+ return circleSamples;
+ }
+
+ public float getInnerRadius() {
+ return innerRadius;
+ }
+
+ public float getOuterRadius() {
+ return outerRadius;
+ }
+
+ public int getRadialSamples() {
+ return radialSamples;
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ circleSamples = capsule.readInt("circleSamples", 0);
+ radialSamples = capsule.readInt("radialSamples", 0);
+ innerRadius = capsule.readFloat("innerRadius", 0);
+ outerRadius = capsule.readFloat("outerRaidus", 0);
+ }
+
+ private void setGeometryData() {
+ // allocate vertices
+ int vertCount = (circleSamples + 1) * (radialSamples + 1);
+ FloatBuffer fpb = BufferUtils.createVector3Buffer(vertCount);
+ setBuffer(Type.Position, 3, fpb);
+
+ // allocate normals if requested
+ FloatBuffer fnb = BufferUtils.createVector3Buffer(vertCount);
+ setBuffer(Type.Normal, 3, fnb);
+
+ // allocate texture coordinates
+ FloatBuffer ftb = BufferUtils.createVector2Buffer(vertCount);
+ setBuffer(Type.TexCoord, 2, ftb);
+
+ // generate geometry
+ float inverseCircleSamples = 1.0f / circleSamples;
+ float inverseRadialSamples = 1.0f / radialSamples;
+ int i = 0;
+ // generate the cylinder itself
+ Vector3f radialAxis = new Vector3f(), torusMiddle = new Vector3f(), tempNormal = new Vector3f();
+ for (int circleCount = 0; circleCount < circleSamples; circleCount++) {
+ // compute center point on torus circle at specified angle
+ float circleFraction = circleCount * inverseCircleSamples;
+ float theta = FastMath.TWO_PI * circleFraction;
+ float cosTheta = FastMath.cos(theta);
+ float sinTheta = FastMath.sin(theta);
+ radialAxis.set(cosTheta, sinTheta, 0);
+ radialAxis.mult(outerRadius, torusMiddle);
+
+ // compute slice vertices with duplication at end point
+ int iSave = i;
+ for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
+ float radialFraction = radialCount * inverseRadialSamples;
+ // in [0,1)
+ float phi = FastMath.TWO_PI * radialFraction;
+ float cosPhi = FastMath.cos(phi);
+ float sinPhi = FastMath.sin(phi);
+ tempNormal.set(radialAxis).multLocal(cosPhi);
+ tempNormal.z += sinPhi;
+ fnb.put(tempNormal.x).put(tempNormal.y).put(
+ tempNormal.z);
+
+ tempNormal.multLocal(innerRadius).addLocal(torusMiddle);
+ fpb.put(tempNormal.x).put(tempNormal.y).put(
+ tempNormal.z);
+
+ ftb.put(radialFraction).put(circleFraction);
+ i++;
+ }
+
+ BufferUtils.copyInternalVector3(fpb, iSave, i);
+ BufferUtils.copyInternalVector3(fnb, iSave, i);
+
+ ftb.put(1.0f).put(circleFraction);
+
+ i++;
+ }
+
+ // duplicate the cylinder ends to form a torus
+ for (int iR = 0; iR <= radialSamples; iR++, i++) {
+ BufferUtils.copyInternalVector3(fpb, iR, i);
+ BufferUtils.copyInternalVector3(fnb, iR, i);
+ BufferUtils.copyInternalVector2(ftb, iR, i);
+ ftb.put(i * 2 + 1, 1.0f);
+ }
+ }
+
+ private void setIndexData() {
+ // allocate connectivity
+ int triCount = 2 * circleSamples * radialSamples;
+
+ ShortBuffer sib = BufferUtils.createShortBuffer(3 * triCount);
+ setBuffer(Type.Index, 3, sib);
+
+ int i;
+ // generate connectivity
+ int connectionStart = 0;
+ int index = 0;
+ for (int circleCount = 0; circleCount < circleSamples; circleCount++) {
+ int i0 = connectionStart;
+ int i1 = i0 + 1;
+ connectionStart += radialSamples + 1;
+ int i2 = connectionStart;
+ int i3 = i2 + 1;
+ for (i = 0; i < radialSamples; i++, index += 6) {
+// if (true) {
+ sib.put((short)i0++);
+ sib.put((short)i2);
+ sib.put((short)i1);
+ sib.put((short)i1++);
+ sib.put((short)i2++);
+ sib.put((short)i3++);
+
+// getIndexBuffer().put(i0++);
+// getIndexBuffer().put(i2);
+// getIndexBuffer().put(i1);
+// getIndexBuffer().put(i1++);
+// getIndexBuffer().put(i2++);
+// getIndexBuffer().put(i3++);
+// } else {
+// getIndexBuffer().put(i0++);
+// getIndexBuffer().put(i1);
+// getIndexBuffer().put(i2);
+// getIndexBuffer().put(i1++);
+// getIndexBuffer().put(i3++);
+// getIndexBuffer().put(i2++);
+// }
+ }
+ }
+ }
+
+ /**
+ * Rebuilds this torus based on a new set of parameters.
+ *
+ * @param circleSamples the number of samples along the circles.
+ * @param radialSamples the number of samples along the radial.
+ * @param innerRadius the radius of the inner begining of the Torus.
+ * @param outerRadius the radius of the outter end of the Torus.
+ */
+ public void updateGeometry(int circleSamples, int radialSamples, float innerRadius, float outerRadius) {
+ this.circleSamples = circleSamples;
+ this.radialSamples = radialSamples;
+ this.innerRadius = innerRadius;
+ this.outerRadius = outerRadius;
+ setGeometryData();
+ setIndexData();
+ updateBound();
+ updateCounts();
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(circleSamples, "circleSamples", 0);
+ capsule.write(radialSamples, "radialSamples", 0);
+ capsule.write(innerRadius, "innerRadius", 0);
+ capsule.write(outerRadius, "outerRadius", 0);
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/shader/Attribute.java b/engine/src/core/com/jme3/shader/Attribute.java
new file mode 100644
index 0000000..4b6a095
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/Attribute.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+/**
+ * An attribute is a shader variable mapping to a VertexBuffer data
+ * on the CPU.
+ *
+ * @author Kirill Vainer
+ */
+public class Attribute extends ShaderVariable {
+}
diff --git a/engine/src/core/com/jme3/shader/DefineList.java b/engine/src/core/com/jme3/shader/DefineList.java
new file mode 100644
index 0000000..abd3661
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/DefineList.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class DefineList implements Savable {
+
+ private final SortedMap<String, String> defines = new TreeMap<String, String>();
+ private String compiled = null;
+
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+
+ String[] keys = new String[defines.size()];
+ String[] vals = new String[defines.size()];
+
+ int i = 0;
+ for (Map.Entry<String, String> define : defines.entrySet()){
+ keys[i] = define.getKey();
+ vals[i] = define.getValue();
+ i++;
+ }
+
+ oc.write(keys, "keys", null);
+ oc.write(vals, "vals", null);
+
+ // for compatability only with older versions
+ oc.write(compiled, "compiled", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+
+ String[] keys = ic.readStringArray("keys", null);
+ String[] vals = ic.readStringArray("vals", null);
+ for (int i = 0; i < keys.length; i++){
+ defines.put(keys[i], vals[i]);
+ }
+
+ compiled = ic.readString("compiled", null);
+ }
+
+ public void clear() {
+ defines.clear();
+ compiled = "";
+ }
+
+ public String get(String key){
+ // I do not see the point of forcing a rebuild on get()
+ // so I'm commenting it out. -pspeed
+ //compiled = null;
+ return defines.get(key);
+ }
+
+// public void set(String key, String val){
+// compiled = null;
+// defines.put(key, val);
+// }
+
+ public boolean set(String key, VarType type, Object val){
+ if (val == null){
+ defines.remove(key);
+ compiled = null;
+ return true;
+ }
+
+ switch (type){
+ case Boolean:
+ if ( ((Boolean) val).booleanValue() ) {
+ // same literal, != should work
+ if( defines.put(key, "1") != "1" ) {
+ compiled = null;
+ return true;
+ }
+ } else if (defines.containsKey(key)) {
+ defines.remove(key);
+ compiled = null;
+ return true;
+ }
+
+ break;
+ case Float:
+ case Int:
+ String original = defines.put(key, val.toString());
+ if (!val.equals(original)) {
+ compiled = null;
+ return true;
+ }
+ break;
+ default:
+ // same literal, != should work
+ if (defines.put(key, "1") != "1") {
+ compiled = null;
+ return true;
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ public boolean remove(String key){
+ if (defines.remove(key) != null) {
+ compiled = null;
+ return true;
+ }
+
+ return false;
+ }
+
+ public void addFrom(DefineList other){
+ if (other == null)
+ return;
+
+ compiled = null;
+ defines.putAll(other.defines);
+ }
+
+ public String getCompiled(){
+ if (compiled == null){
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, String> entry : defines.entrySet()){
+ sb.append("#define ").append(entry.getKey()).append(" ");
+ sb.append(entry.getValue()).append('\n');
+ }
+ compiled = sb.toString();
+ }
+ return compiled;
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ for (Map.Entry<String, String> entry : defines.entrySet()) {
+ sb.append(entry.getKey());
+ if (i != defines.size() - 1)
+ sb.append(", ");
+
+ i++;
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shader/Shader.java b/engine/src/core/com/jme3/shader/Shader.java
new file mode 100644
index 0000000..265eed6
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/Shader.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+import com.jme3.export.*;
+import com.jme3.renderer.Renderer;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.ListMap;
+import com.jme3.util.NativeObject;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+
+public final class Shader extends NativeObject implements Savable {
+
+ private String language;
+
+ /**
+ * True if the shader is fully compiled & linked.
+ * (e.g no GL error will be invoked if used).
+ */
+ private boolean usable = false;
+
+ /**
+ * A list of all shaders currently attached.
+ */
+ private ArrayList<ShaderSource> shaderList;
+
+ /**
+ * Maps uniform name to the uniform variable.
+ */
+// private HashMap<String, Uniform> uniforms;
+ private ListMap<String, Uniform> uniforms;
+
+ /**
+ * Maps attribute name to the location of the attribute in the shader.
+ */
+ private IntMap<Attribute> attribs;
+
+ /**
+ * Type of shader. The shader will control the pipeline of it's type.
+ */
+ public static enum ShaderType {
+ /**
+ * Control fragment rasterization. (e.g color of pixel).
+ */
+ Fragment,
+
+ /**
+ * Control vertex processing. (e.g transform of model to clip space)
+ */
+ Vertex,
+
+ /**
+ * Control geometry assembly. (e.g compile a triangle list from input data)
+ */
+ Geometry;
+ }
+
+ /**
+ * Shader source describes a shader object in OpenGL. Each shader source
+ * is assigned a certain pipeline which it controls (described by it's type).
+ */
+ public static class ShaderSource extends NativeObject implements Savable {
+
+ ShaderType shaderType;
+
+ boolean usable = false;
+ String name = null;
+ String source = null;
+ String defines = null;
+
+ public ShaderSource(ShaderType type){
+ super(ShaderSource.class);
+ this.shaderType = type;
+ if (type == null)
+ throw new NullPointerException("The shader type must be specified");
+ }
+
+ protected ShaderSource(ShaderSource ss){
+ super(ShaderSource.class, ss.id);
+ this.shaderType = ss.shaderType;
+ usable = false;
+ name = ss.name;
+ // forget source & defines
+ }
+
+ public ShaderSource(){
+ super(ShaderSource.class);
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(shaderType, "shaderType", null);
+ oc.write(name, "name", null);
+ oc.write(source, "source", null);
+ oc.write(defines, "defines", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+ shaderType = ic.readEnum("shaderType", ShaderType.class, null);
+ name = ic.readString("name", null);
+ source = ic.readString("source", null);
+ defines = ic.readString("defines", null);
+ }
+
+ public void setName(String name){
+ this.name = name;
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ public ShaderType getType() {
+ return shaderType;
+ }
+
+ public void setSource(String source){
+ if (source == null)
+ throw new NullPointerException("Shader source cannot be null");
+
+ this.source = source;
+ setUpdateNeeded();
+ }
+
+ public void setDefines(String defines){
+ if (defines == null)
+ throw new NullPointerException("Shader defines cannot be null");
+
+ this.defines = defines;
+ setUpdateNeeded();
+ }
+
+ public String getSource(){
+ return source;
+ }
+
+ public String getDefines(){
+ return defines;
+ }
+
+ public boolean isUsable(){
+ return usable;
+ }
+
+ public void setUsable(boolean usable){
+ this.usable = usable;
+ }
+
+ @Override
+ public String toString(){
+ String nameTxt = "";
+ if (name != null)
+ nameTxt = "name="+name+", ";
+ if (defines != null)
+ nameTxt += "defines, ";
+
+
+ return getClass().getSimpleName() + "["+nameTxt+"type="
+ + shaderType.name()+"]";
+ }
+
+ public void resetObject(){
+ id = -1;
+ usable = false;
+ setUpdateNeeded();
+ }
+
+ public void deleteObject(Object rendererObject){
+ ((Renderer)rendererObject).deleteShaderSource(ShaderSource.this);
+ }
+
+ public NativeObject createDestructableClone(){
+ return new ShaderSource(ShaderSource.this);
+ }
+ }
+
+ /**
+ * Create an empty shader.
+ */
+ public Shader(String language){
+ super(Shader.class);
+ this.language = language;
+ shaderList = new ArrayList<ShaderSource>();
+// uniforms = new HashMap<String, Uniform>();
+ uniforms = new ListMap<String, Uniform>();
+ attribs = new IntMap<Attribute>();
+ }
+
+ /**
+ * Do not use this constructor. Serialization purposes only.
+ */
+ public Shader(){
+ super(Shader.class);
+ }
+
+ protected Shader(Shader s){
+ super(Shader.class, s.id);
+ shaderList = new ArrayList<ShaderSource>();
+ //uniforms = new ListMap<String, Uniform>();
+ //attribs = new IntMap<Attribute>();
+
+ // NOTE: Because ShaderSources are registered separately with
+ // the GLObjectManager
+ for (ShaderSource source : s.shaderList){
+ shaderList.add( (ShaderSource)source.createDestructableClone() );
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(language, "language", null);
+ oc.writeSavableArrayList(shaderList, "shaderList", null);
+ oc.writeIntSavableMap(attribs, "attribs", null);
+ oc.writeStringSavableMap(uniforms, "uniforms", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+ language = ic.readString("language", null);
+ shaderList = ic.readSavableArrayList("shaderList", null);
+ attribs = (IntMap<Attribute>) ic.readIntSavableMap("attribs", null);
+
+ HashMap<String, Uniform> uniMap = (HashMap<String, Uniform>) ic.readStringSavableMap("uniforms", null);
+ uniforms = new ListMap<String, Uniform>(uniMap);
+ }
+
+ /**
+ * Creates a deep clone of the shader, where the sources are available
+ * but have not been compiled yet. Does not copy the uniforms or attribs.
+ * @return
+ */
+// public Shader createDeepClone(String defines){
+// Shader newShader = new Shader(language);
+// for (ShaderSource source : shaderList){
+// if (!source.getDefines().equals(defines)){
+// // need to clone the shadersource so
+// // the correct defines can be placed
+// ShaderSource newSource = new ShaderSource(source.getType());
+// newSource.setSource(source.getSource());
+// newSource.setDefines(defines);
+// newShader.addSource(newSource);
+// }else{
+// // no need to clone source, also saves
+// // having to compile the shadersource
+// newShader.addSource(source);
+// }
+// }
+// return newShader;
+// }
+
+ /**
+ * Adds source code to a certain pipeline.
+ *
+ * @param type The pipeline to control
+ * @param source The shader source code (in GLSL).
+ */
+ public void addSource(ShaderType type, String name, String source, String defines){
+ ShaderSource shader = new ShaderSource(type);
+ shader.setSource(source);
+ shader.setName(name);
+ if (defines != null)
+ shader.setDefines(defines);
+
+ shaderList.add(shader);
+ setUpdateNeeded();
+ }
+
+ public void addSource(ShaderType type, String source, String defines){
+ addSource(type, null, source, defines);
+ }
+
+ public void addSource(ShaderType type, String source){
+ addSource(type, source, null);
+ }
+
+ /**
+ * Adds an existing shader source to this shader.
+ * @param source
+ */
+ private void addSource(ShaderSource source){
+ shaderList.add(source);
+ setUpdateNeeded();
+ }
+
+ public Uniform getUniform(String name){
+ Uniform uniform = uniforms.get(name);
+ if (uniform == null){
+ uniform = new Uniform();
+ uniform.name = name;
+ uniforms.put(name, uniform);
+ }
+ return uniform;
+ }
+
+ public void removeUniform(String name){
+ uniforms.remove(name);
+ }
+
+ public Attribute getAttribute(VertexBuffer.Type attribType){
+ int ordinal = attribType.ordinal();
+ Attribute attrib = attribs.get(ordinal);
+ if (attrib == null){
+ attrib = new Attribute();
+ attrib.name = attribType.name();
+ attribs.put(ordinal, attrib);
+ }
+ return attrib;
+ }
+
+// public Collection<Uniform> getUniforms(){
+// return uniforms.values();
+// }
+
+ public ListMap<String, Uniform> getUniformMap(){
+ return uniforms;
+ }
+
+// public Collection<Attribute> getAttributes() {
+// return attribs.
+// }
+
+ public Collection<ShaderSource> getSources(){
+ return shaderList;
+ }
+
+ public String getLanguage(){
+ return language;
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getSimpleName() + "[language="+language
+ + ", numSources="+shaderList.size()
+ + ", numUniforms="+uniforms.size()
+ + ", shaderSources="+getSources()+"]";
+ }
+
+ /**
+ * Clears all sources. Assuming that they have already been detached and
+ * removed on the GL side.
+ */
+ public void resetSources(){
+ shaderList.clear();
+ }
+
+ /**
+ * Returns true if this program and all it's shaders have been compiled,
+ * linked and validated successfuly.
+ */
+ public boolean isUsable(){
+ return usable;
+ }
+
+ /**
+ * Sets if the program can be used. Should only be called by the Renderer.
+ * @param usable
+ */
+ public void setUsable(boolean usable){
+ this.usable = usable;
+ }
+
+ /**
+ * Usually called when the shader itself changes or during any
+ * time when the var locations need to be refreshed.
+ */
+ public void resetLocations(){
+ // NOTE: Shader sources will be reset seperately from the shader itself.
+ for (Uniform uniform : uniforms.values()){
+ uniform.reset(); // fixes issue with re-initialization
+ }
+ for (Entry<Attribute> entry : attribs){
+ entry.getValue().location = -2;
+ }
+ }
+
+ @Override
+ public void setUpdateNeeded(){
+ super.setUpdateNeeded();
+ resetLocations();
+ }
+
+ /**
+ * Called by the object manager to reset all object IDs. This causes
+ * the shader to be reuploaded to the GPU incase the display was restarted.
+ */
+ @Override
+ public void resetObject() {
+ this.id = -1;
+ this.usable = false;
+
+ for (ShaderSource source : shaderList){
+ source.resetObject();
+ }
+
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((Renderer)rendererObject).deleteShader(this);
+ }
+
+ public NativeObject createDestructableClone(){
+ return new Shader(this);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shader/ShaderKey.java b/engine/src/core/com/jme3/shader/ShaderKey.java
new file mode 100644
index 0000000..a4c18eb
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/ShaderKey.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+public class ShaderKey extends AssetKey<Shader> {
+
+ protected String fragName;
+ protected DefineList defines;
+ protected String language;
+
+ public ShaderKey(){
+ }
+
+ public ShaderKey(String vertName, String fragName, DefineList defines, String lang){
+ super(vertName);
+ this.fragName = fragName;
+ this.defines = defines;
+ this.language = lang;
+ }
+
+ @Override
+ public String toString(){
+ return "V="+name + " F=" + fragName + (defines != null ? defines : "");
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null){
+ return false;
+ }
+ if (getClass() != obj.getClass()){
+ return false;
+ }
+
+ final ShaderKey other = (ShaderKey) obj;
+ if (name.equals(other.name) && fragName.equals(other.fragName)){
+// return true;
+ if (defines != null && other.defines != null)
+ return defines.getCompiled().equals(other.defines.getCompiled());
+ else if (defines != null || other.defines != null)
+ return false;
+ else
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 41 * hash + name.hashCode();
+ hash = 41 * hash + fragName.hashCode();
+ hash = 41 * hash + (defines != null ? defines.getCompiled().hashCode() : 0);
+ return hash;
+ }
+
+ public DefineList getDefines() {
+ return defines;
+ }
+
+ public String getVertName(){
+ return name;
+ }
+
+ public String getFragName() {
+ return fragName;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(fragName, "fragment_name", null);
+ oc.write(language, "language", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ fragName = ic.readString("fragment_name", null);
+ language = ic.readString("language", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shader/ShaderUtils.java b/engine/src/core/com/jme3/shader/ShaderUtils.java
new file mode 100644
index 0000000..8aecdeb
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/ShaderUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+public class ShaderUtils {
+
+ public static String convertToGLSL130(String input, boolean isFrag){
+ StringBuilder sb = new StringBuilder();
+ sb.append("#version 130\n");
+ if (isFrag){
+ input = input.replaceAll("varying", "in");
+ }else{
+ input = input.replaceAll("attribute", "in");
+ input = input.replaceAll("varying", "out");
+ }
+ sb.append(input);
+ return sb.toString();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shader/ShaderVariable.java b/engine/src/core/com/jme3/shader/ShaderVariable.java
new file mode 100644
index 0000000..7dbd2e2
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/ShaderVariable.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+public class ShaderVariable implements Savable {
+
+ // if -2, location not known
+ // if -1, not defined in shader
+ // if >= 0, uniform defined and available.
+ protected int location = -2;
+
+ /**
+ * Name of the uniform as was declared in the shader.
+ * E.g name = "g_WorldMatrix" if the decleration was
+ * "uniform mat4 g_WorldMatrix;".
+ */
+ protected String name = null;
+
+ /**
+ * True if the shader value was changed.
+ */
+ protected boolean updateNeeded = true;;
+
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", null);
+ }
+
+ public void setLocation(int location){
+ this.location = location;
+ }
+
+ public int getLocation(){
+ return location;
+ }
+
+ public void setName(String name){
+ this.name = name;
+ }
+
+ public String getName(){
+ return name;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shader/Uniform.java b/engine/src/core/com/jme3/shader/Uniform.java
new file mode 100644
index 0000000..228e097
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/Uniform.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.*;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+public class Uniform extends ShaderVariable {
+
+ private static final Integer ZERO_INT = Integer.valueOf(0);
+ private static final Float ZERO_FLT = Float.valueOf(0);
+ private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4);
+
+ /**
+ * Currently set value of the uniform.
+ */
+ protected Object value = null;
+ protected FloatBuffer multiData = null;
+
+ /**
+ * Type of uniform
+ */
+ protected VarType varType;
+
+ /**
+ * Binding to a renderer value, or null if user-defined uniform
+ */
+ protected UniformBinding binding;
+
+ protected boolean setByCurrentMaterial = false;
+// protected Object lastChanger = null;
+
+ @Override
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(varType, "varType", null);
+ oc.write(binding, "binding", null);
+ switch (varType){
+ case Boolean:
+ oc.write( ((Boolean)value).booleanValue(), "valueBoolean", false );
+ break;
+ case Float:
+ oc.write( ((Float)value).floatValue(), "valueFloat", 0);
+ break;
+ case FloatArray:
+ oc.write( (FloatBuffer)value, "valueFloatArray", null);
+ break;
+ case Int:
+ oc.write( ((Integer)value).intValue(), "valueInt", 0);
+ break;
+ case Matrix3:
+ oc.write( (Matrix3f)value, "valueMatrix3", null);
+ break;
+ case Matrix3Array:
+ case Matrix4Array:
+ case Vector2Array:
+ throw new UnsupportedOperationException("Come again?");
+ case Matrix4:
+ oc.write( (Matrix4f)value, "valueMatrix4", null);
+ break;
+ case Vector2:
+ oc.write( (Vector2f)value, "valueVector2", null);
+ break;
+ case Vector3:
+ oc.write( (Vector3f)value, "valueVector3", null);
+ break;
+ case Vector3Array:
+ oc.write( (FloatBuffer)value, "valueVector3Array", null);
+ break;
+ case Vector4:
+ oc.write( (ColorRGBA)value, "valueVector4", null);
+ break;
+ case Vector4Array:
+ oc.write( (FloatBuffer)value, "valueVector4Array", null);
+ break;
+ }
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ varType = ic.readEnum("varType", VarType.class, null);
+ binding = ic.readEnum("binding", UniformBinding.class, null);
+ switch (varType){
+ case Boolean:
+ value = ic.readBoolean("valueBoolean", false);
+ break;
+ case Float:
+ value = ic.readFloat("valueFloat", 0);
+ break;
+ case FloatArray:
+ value = ic.readFloatBuffer("valueFloatArray", null);
+ break;
+ case Int:
+ value = ic.readInt("valueInt", 0);
+ break;
+ case Matrix3:
+ multiData = ic.readFloatBuffer("valueMatrix3", null);
+ value = multiData;
+ break;
+ case Matrix4:
+ multiData = ic.readFloatBuffer("valueMatrix4", null);
+ value = multiData;
+ break;
+ case Vector2:
+ value = ic.readSavable("valueVector2", null);
+ break;
+ case Vector3:
+ value = ic.readSavable("valueVector3", null);
+ break;
+ case Vector3Array:
+ value = ic.readFloatBuffer("valueVector3Array", null);
+ break;
+ case Vector4:
+ value = ic.readSavable("valueVector4", null);
+ break;
+ case Vector4Array:
+ value = ic.readFloatBuffer("valueVector4Array", null);
+ break;
+ }
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ if (name != null){
+ sb.append("Uniform[name=");
+ sb.append(name);
+ if (varType != null){
+ sb.append(", type=");
+ sb.append(varType);
+ sb.append(", value=");
+ sb.append(value);
+ }else{
+ sb.append(", value=<not set>");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public void setBinding(UniformBinding binding){
+ this.binding = binding;
+ }
+
+ public UniformBinding getBinding(){
+ return binding;
+ }
+
+ public VarType getVarType() {
+ return varType;
+ }
+
+ public Object getValue(){
+ return value;
+ }
+
+ public boolean isSetByCurrentMaterial() {
+ return setByCurrentMaterial;
+ }
+
+ public void clearSetByCurrentMaterial(){
+ setByCurrentMaterial = false;
+ }
+
+// public void setLastChanger(Object lastChanger){
+// this.lastChanger = lastChanger;
+// }
+//
+// public Object getLastChanger(){
+// return lastChanger;
+// }
+
+ public void clearValue(){
+ updateNeeded = true;
+
+ if (multiData != null){
+ ZERO_BUF.clear();
+ multiData.clear();
+
+ while (multiData.remaining() > 0){
+ ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );
+ multiData.put(ZERO_BUF);
+ }
+
+ multiData.clear();
+
+ return;
+ }
+
+ if (varType == null)
+ return;
+
+ switch (varType){
+ case Int:
+ this.value = ZERO_INT;
+ break;
+ case Boolean:
+ this.value = Boolean.FALSE;
+ break;
+ case Float:
+ this.value = ZERO_FLT;
+ break;
+ case Vector2:
+ this.value = Vector2f.ZERO;
+ break;
+ case Vector3:
+ this.value = Vector3f.ZERO;
+ break;
+ case Vector4:
+ if (this.value instanceof ColorRGBA){
+ this.value = ColorRGBA.BlackNoAlpha;
+ }else{
+ this.value = Quaternion.ZERO;
+ }
+ break;
+ default:
+ break; // won't happen because those are either textures
+ // or multidata types
+ }
+ }
+
+ public void setValue(VarType type, Object value){
+ if (location == -1)
+ return;
+
+ if (varType != null && varType != type)
+ throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
+
+ if (value == null)
+ throw new NullPointerException();
+
+ setByCurrentMaterial = true;
+
+ switch (type){
+ case Matrix3:
+ Matrix3f m3 = (Matrix3f) value;
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(9);
+
+ m3.fillFloatBuffer(multiData, true);
+ multiData.clear();
+ break;
+ case Matrix4:
+ Matrix4f m4 = (Matrix4f) value;
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(16);
+
+ m4.fillFloatBuffer(multiData, true);
+ multiData.clear();
+ break;
+ case FloatArray:
+ float[] fa = (float[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(fa);
+ }else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
+ }
+
+ multiData.put(fa);
+ multiData.clear();
+ break;
+ case Vector2Array:
+ Vector2f[] v2a = (Vector2f[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(v2a);
+ } else {
+ multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
+ }
+
+ for (int i = 0; i < v2a.length; i++)
+ BufferUtils.setInBuffer(v2a[i], multiData, i);
+
+ multiData.clear();
+ break;
+ case Vector3Array:
+ Vector3f[] v3a = (Vector3f[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(v3a);
+ } else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
+ }
+
+ for (int i = 0; i < v3a.length; i++)
+ BufferUtils.setInBuffer(v3a[i], multiData, i);
+
+ multiData.clear();
+ break;
+ case Vector4Array:
+ Quaternion[] v4a = (Quaternion[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(v4a);
+ } else {
+ multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
+ }
+
+ for (int i = 0; i < v4a.length; i++)
+ BufferUtils.setInBuffer(v4a[i], multiData, i);
+
+ multiData.clear();
+ break;
+ case Matrix3Array:
+ Matrix3f[] m3a = (Matrix3f[]) value;
+
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
+ else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
+ }
+
+ for (int i = 0; i < m3a.length; i++)
+ m3a[i].fillFloatBuffer(multiData, true);
+
+ multiData.clear();
+ break;
+ case Matrix4Array:
+ Matrix4f[] m4a = (Matrix4f[]) value;
+
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
+ else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
+ }
+
+ for (int i = 0; i < m4a.length; i++)
+ m4a[i].fillFloatBuffer(multiData, true);
+
+ multiData.clear();
+ break;
+ // Only use check if equals optimization for primitive values
+ case Int:
+ case Float:
+ case Boolean:
+ if (this.value != null && this.value.equals(value))
+ return;
+
+ this.value = value;
+ break;
+ default:
+ this.value = value;
+ break;
+ }
+
+ if (multiData != null)
+ this.value = multiData;
+
+ varType = type;
+ updateNeeded = true;
+ }
+
+ public void setVector4Length(int length){
+ if (location == -1)
+ return;
+
+ FloatBuffer fb = (FloatBuffer) value;
+ if (fb == null || fb.capacity() < length){
+ value = BufferUtils.createFloatBuffer(length * 4);
+ }
+
+ varType = VarType.Vector4Array;
+ updateNeeded = true;
+ setByCurrentMaterial = true;
+ }
+
+ public void setVector4InArray(float x, float y, float z, float w, int index){
+ if (location == -1)
+ return;
+
+ if (varType != null && varType != VarType.Vector4Array)
+ throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
+
+ FloatBuffer fb = (FloatBuffer) value;
+ fb.position(index * 4);
+ fb.put(x).put(y).put(z).put(w);
+ fb.rewind();
+ updateNeeded = true;
+ setByCurrentMaterial = true;
+ }
+
+ public boolean isUpdateNeeded(){
+ return updateNeeded;
+ }
+
+ public void clearUpdateNeeded(){
+ updateNeeded = false;
+ }
+
+ public void reset(){
+ setByCurrentMaterial = false;
+ location = -2;
+ updateNeeded = true;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shader/UniformBinding.java b/engine/src/core/com/jme3/shader/UniformBinding.java
new file mode 100644
index 0000000..bdca35b
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/UniformBinding.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+public enum UniformBinding {
+
+ /**
+ * The world matrix. Converts Model space to World space.
+ * Type: mat4
+ */
+ WorldMatrix,
+
+ /**
+ * The view matrix. Converts World space to View space.
+ * Type: mat4
+ */
+ ViewMatrix,
+
+ /**
+ * The projection matrix. Converts View space to Clip/Projection space.
+ * Type: mat4
+ */
+ ProjectionMatrix,
+
+ /**
+ * The world view matrix. Converts Model space to View space.
+ * Type: mat4
+ */
+ WorldViewMatrix,
+
+ /**
+ * The normal matrix. The inverse transpose of the worldview matrix.
+ * Converts normals from model space to view space.
+ * Type: mat3
+ */
+ NormalMatrix,
+
+ /**
+ * The world view projection matrix. Converts Model space to Clip/Projection
+ * space.
+ * Type: mat4
+ */
+ WorldViewProjectionMatrix,
+
+ /**
+ * The view projection matrix. Converts Model space to Clip/Projection
+ * space.
+ * Type: mat4
+ */
+ ViewProjectionMatrix,
+
+
+ WorldMatrixInverse,
+ ViewMatrixInverse,
+ ProjectionMatrixInverse,
+ ViewProjectionMatrixInverse,
+ WorldViewMatrixInverse,
+ NormalMatrixInverse,
+ WorldViewProjectionMatrixInverse,
+
+ /**
+ * Contains the four viewport parameters in this order:
+ * X = Left,
+ * Y = Top,
+ * Z = Right,
+ * W = Bottom.
+ * Type: vec4
+ */
+ ViewPort,
+
+ /**
+ * The near and far values for the camera frustum.
+ * X = Near
+ * Y = Far.
+ * Type: vec2
+ */
+ FrustumNearFar,
+
+ /**
+ * The width and height of the camera.
+ * Type: vec2
+ */
+ Resolution,
+
+ /**
+ * Aspect ratio of the resolution currently set. Width/Height.
+ * Type: float
+ */
+ Aspect,
+
+ /**
+ * Camera position in world space.
+ * Type: vec3
+ */
+ CameraPosition,
+
+ /**
+ * Direction of the camera.
+ * Type: vec3
+ */
+ CameraDirection,
+
+ /**
+ * Left vector of the camera.
+ * Type: vec3
+ */
+ CameraLeft,
+
+ /**
+ * Up vector of the camera.
+ * Type: vec3
+ */
+ CameraUp,
+
+ /**
+ * Time in seconds since the application was started.
+ * Type: float
+ */
+ Time,
+
+ /**
+ * Time in seconds that the last frame took.
+ * Type: float
+ */
+ Tpf,
+
+ /**
+ * Frames per second.
+ * Type: float
+ */
+ FrameRate,
+}
diff --git a/engine/src/core/com/jme3/shader/VarType.java b/engine/src/core/com/jme3/shader/VarType.java
new file mode 100644
index 0000000..723bc93
--- /dev/null
+++ b/engine/src/core/com/jme3/shader/VarType.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.shader;
+
+public enum VarType {
+
+ Float,
+ Vector2,
+ Vector3,
+ Vector4,
+
+ FloatArray(true,false),
+ Vector2Array(true,false),
+ Vector3Array(true,false),
+ Vector4Array(true,false),
+
+ Boolean,
+
+ Matrix3(true,false),
+ Matrix4(true,false),
+
+ Matrix3Array(true,false),
+ Matrix4Array(true,false),
+
+ TextureBuffer(false,true),
+ Texture2D(false,true),
+ Texture3D(false,true),
+ TextureArray(false,true),
+ TextureCubeMap(false,true),
+ Int;
+
+ private boolean usesMultiData = false;
+ private boolean textureType = false;
+
+ VarType(){
+ }
+
+ VarType(boolean multiData, boolean textureType){
+ usesMultiData = multiData;
+ this.textureType = textureType;
+ }
+
+ public boolean isTextureType() {
+ return textureType;
+ }
+
+ public boolean usesMultiData() {
+ return usesMultiData;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java b/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java
new file mode 100644
index 0000000..3fd01c9
--- /dev/null
+++ b/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+
+/**
+ * BasicShadowRenderer uses standard shadow mapping with one map
+ * it's useful to render shadows in a small scene, but edges might look a bit jagged.
+ *
+ * @author Kirill Vainer
+ */
+public class BasicShadowRenderer implements SceneProcessor {
+
+ private RenderManager renderManager;
+ private ViewPort viewPort;
+ private FrameBuffer shadowFB;
+ private Texture2D shadowMap;
+ private Camera shadowCam;
+ private Material preshadowMat;
+ private Material postshadowMat;
+ private Picture dispPic = new Picture("Picture");
+ private boolean noOccluders = false;
+ private Vector3f[] points = new Vector3f[8];
+ private Vector3f direction = new Vector3f();
+
+ /**
+ * Creates a BasicShadowRenderer
+ * @param manager the asset manager
+ * @param size the size of the shadow map (the map is square)
+ */
+ public BasicShadowRenderer(AssetManager manager, int size) {
+ shadowFB = new FrameBuffer(size, size, 1);
+ shadowMap = new Texture2D(size, size, Format.Depth);
+ shadowFB.setDepthTexture(shadowMap);
+ shadowCam = new Camera(size, size);
+
+ preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
+ postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md");
+ postshadowMat.setTexture("ShadowMap", shadowMap);
+
+ dispPic.setTexture(manager, shadowMap, false);
+
+ for (int i = 0; i < points.length; i++) {
+ points[i] = new Vector3f();
+ }
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ renderManager = rm;
+ viewPort = vp;
+
+ reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight());
+ }
+
+ public boolean isInitialized() {
+ return viewPort != null;
+ }
+
+ /**
+ * returns the light direction used for this processor
+ * @return
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * sets the light direction to use to computs shadows
+ * @param direction
+ */
+ public void setDirection(Vector3f direction) {
+ this.direction.set(direction).normalizeLocal();
+ }
+
+ /**
+ * debug only
+ * @return
+ */
+ public Vector3f[] getPoints() {
+ return points;
+ }
+
+ /**
+ * debug only
+ * returns the shadow camera
+ * @return
+ */
+ public Camera getShadowCamera() {
+ return shadowCam;
+ }
+
+ public void postQueue(RenderQueue rq) {
+ GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
+ if (occluders.size() == 0) {
+ noOccluders = true;
+ return;
+ } else {
+ noOccluders = false;
+ }
+
+ GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
+
+ // update frustum points based on current camera
+ Camera viewCam = viewPort.getCamera();
+ ShadowUtil.updateFrustumPoints(viewCam,
+ viewCam.getFrustumNear(),
+ viewCam.getFrustumFar(),
+ 1.0f,
+ points);
+
+ Vector3f frustaCenter = new Vector3f();
+ for (Vector3f point : points) {
+ frustaCenter.addLocal(point);
+ }
+ frustaCenter.multLocal(1f / 8f);
+
+ // update light direction
+ shadowCam.setProjectionMatrix(null);
+ shadowCam.setParallelProjection(true);
+// shadowCam.setFrustumPerspective(45, 1, 1, 20);
+
+ shadowCam.lookAtDirection(direction, Vector3f.UNIT_Y);
+ shadowCam.update();
+ shadowCam.setLocation(frustaCenter);
+ shadowCam.update();
+ shadowCam.updateViewProjection();
+
+ // render shadow casters to shadow map
+ ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points);
+
+ Renderer r = renderManager.getRenderer();
+ renderManager.setCamera(shadowCam, false);
+ renderManager.setForcedMaterial(preshadowMat);
+
+ r.setFrameBuffer(shadowFB);
+ r.clearBuffers(false, true, false);
+ viewPort.getQueue().renderShadowQueue(ShadowMode.Cast, renderManager, shadowCam, true);
+ r.setFrameBuffer(viewPort.getOutputFrameBuffer());
+
+ renderManager.setForcedMaterial(null);
+ renderManager.setCamera(viewCam, false);
+ }
+
+ /**
+ * debug only
+ * @return
+ */
+ public Picture getDisplayPicture() {
+ return dispPic;
+ }
+
+ public void postFrame(FrameBuffer out) {
+ if (!noOccluders) {
+ postshadowMat.setMatrix4("LightViewProjectionMatrix", shadowCam.getViewProjectionMatrix());
+ renderManager.setForcedMaterial(postshadowMat);
+ viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, viewPort.getCamera(), true);
+ renderManager.setForcedMaterial(null);
+ }
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void cleanup() {
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ dispPic.setPosition(w / 20f, h / 20f);
+ dispPic.setWidth(w / 5f);
+ dispPic.setHeight(h / 5f);
+ }
+}
diff --git a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java
new file mode 100644
index 0000000..9fa95cb
--- /dev/null
+++ b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * <p/>
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.OpaqueComparator;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture.MagFilter;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture.ShadowCompareMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+
+/**
+ * PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)<br>
+ * It splits the view frustum in several parts and compute a shadow map for each
+ * one.<br> splits are distributed so that the closer they are from the camera,
+ * the smaller they are to maximize the resolution used of the shadow map.<br>
+ * This result in a better quality shadow than standard shadow mapping.<br> for
+ * more informations on this read this
+ * <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
+ * <p/>
+ * @author Rémy Bouquet aka Nehon
+ */
+public class PssmShadowRenderer implements SceneProcessor {
+
+ /**
+ * <code>FilterMode</code> specifies how shadows are filtered
+ */
+ public enum FilterMode {
+
+ /**
+ * Shadows are not filtered. Nearest sample is used, causing in blocky
+ * shadows.
+ */
+ Nearest,
+ /**
+ * Bilinear filtering is used. Has the potential of being hardware
+ * accelerated on some GPUs
+ */
+ Bilinear,
+ /**
+ * Dither-based sampling is used, very cheap but can look bad
+ * at low resolutions.
+ */
+ Dither,
+ /**
+ * 4x4 percentage-closer filtering is used. Shadows will be smoother
+ * at the cost of performance
+ */
+ PCF4,
+ /**
+ * 8x8 percentage-closer filtering is used. Shadows will be smoother
+ * at the cost of performance
+ */
+ PCF8
+ }
+
+ /**
+ * Specifies the shadow comparison mode
+ */
+ public enum CompareMode {
+
+ /**
+ * Shadow depth comparisons are done by using shader code
+ */
+ Software,
+ /**
+ * Shadow depth comparisons are done by using the GPU's dedicated
+ * shadowing pipeline.
+ */
+ Hardware;
+ }
+ private int nbSplits = 3;
+ private float lambda = 0.65f;
+ private float shadowIntensity = 0.7f;
+ private float zFarOverride = 0;
+ private RenderManager renderManager;
+ private ViewPort viewPort;
+ private FrameBuffer[] shadowFB;
+ private Texture2D[] shadowMaps;
+ private Texture2D dummyTex;
+ private Camera shadowCam;
+ private Material preshadowMat;
+ private Material postshadowMat;
+ private GeometryList splitOccluders = new GeometryList(new OpaqueComparator());
+ private Matrix4f[] lightViewProjectionsMatrices;
+ private ColorRGBA splits;
+ private float[] splitsArray;
+ private boolean noOccluders = false;
+ private Vector3f direction = new Vector3f();
+ private AssetManager assetManager;
+ private boolean debug = false;
+ private float edgesThickness = 1.0f;
+ private FilterMode filterMode;
+ private CompareMode compareMode;
+ private Picture[] dispPic;
+ private Vector3f[] points = new Vector3f[8];
+ private boolean flushQueues = true;
+
+ /**
+ * Create a PSSM Shadow Renderer
+ * More info on the technique at <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
+ * @param manager the application asset manager
+ * @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
+ * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
+ * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
+ */
+ public PssmShadowRenderer(AssetManager manager, int size, int nbSplits) {
+ this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
+
+ }
+
+ /**
+ * Create a PSSM Shadow Renderer
+ * More info on the technique at <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
+ * @param manager the application asset manager
+ * @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
+ * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
+ * @param postShadowMat the material used for post shadows if you need to override it *
+ */
+ //TODO remove the postShadowMat when we have shader injection....or remove this todo if we are in 2020.
+ public PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) {
+ assetManager = manager;
+ nbSplits = Math.max(Math.min(nbSplits, 4), 1);
+ this.nbSplits = nbSplits;
+
+ shadowFB = new FrameBuffer[nbSplits];
+ shadowMaps = new Texture2D[nbSplits];
+ dispPic = new Picture[nbSplits];
+ lightViewProjectionsMatrices = new Matrix4f[nbSplits];
+ splits = new ColorRGBA();
+ splitsArray = new float[nbSplits + 1];
+
+ //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
+ dummyTex = new Texture2D(size, size, Format.RGBA8);
+
+ preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
+ this.postshadowMat = postShadowMat;
+
+ for (int i = 0; i < nbSplits; i++) {
+ lightViewProjectionsMatrices[i] = new Matrix4f();
+ shadowFB[i] = new FrameBuffer(size, size, 1);
+ shadowMaps[i] = new Texture2D(size, size, Format.Depth);
+
+ shadowFB[i].setDepthTexture(shadowMaps[i]);
+
+ //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
+ shadowFB[i].setColorTexture(dummyTex);
+
+ postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
+
+ //quads for debuging purpose
+ dispPic[i] = new Picture("Picture" + i);
+ dispPic[i].setTexture(manager, shadowMaps[i], false);
+ }
+
+ setCompareMode(CompareMode.Hardware);
+ setFilterMode(FilterMode.Bilinear);
+ setShadowIntensity(0.7f);
+
+ shadowCam = new Camera(size, size);
+ shadowCam.setParallelProjection(true);
+
+ for (int i = 0; i < points.length; i++) {
+ points[i] = new Vector3f();
+ }
+ }
+
+ /**
+ * Sets the filtering mode for shadow edges see {@link FilterMode} for more info
+ * @param filterMode
+ */
+ public void setFilterMode(FilterMode filterMode) {
+ if (filterMode == null) {
+ throw new NullPointerException();
+ }
+
+ if (this.filterMode == filterMode) {
+ return;
+ }
+
+ this.filterMode = filterMode;
+ postshadowMat.setInt("FilterMode", filterMode.ordinal());
+ postshadowMat.setFloat("PCFEdge", edgesThickness);
+ if (compareMode == CompareMode.Hardware) {
+ for (Texture2D shadowMap : shadowMaps) {
+ if (filterMode == FilterMode.Bilinear) {
+ shadowMap.setMagFilter(MagFilter.Bilinear);
+ shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
+ } else {
+ shadowMap.setMagFilter(MagFilter.Nearest);
+ shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+ }
+ }
+ }
+ }
+
+ /**
+ * sets the shadow compare mode see {@link CompareMode} for more info
+ * @param compareMode
+ */
+ public void setCompareMode(CompareMode compareMode) {
+ if (compareMode == null) {
+ throw new NullPointerException();
+ }
+
+ if (this.compareMode == compareMode) {
+ return;
+ }
+
+ this.compareMode = compareMode;
+ for (Texture2D shadowMap : shadowMaps) {
+ if (compareMode == CompareMode.Hardware) {
+ shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
+ if (filterMode == FilterMode.Bilinear) {
+ shadowMap.setMagFilter(MagFilter.Bilinear);
+ shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
+ } else {
+ shadowMap.setMagFilter(MagFilter.Nearest);
+ shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+ }
+ } else {
+ shadowMap.setShadowCompareMode(ShadowCompareMode.Off);
+ shadowMap.setMagFilter(MagFilter.Nearest);
+ shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+ }
+ }
+ postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
+ }
+
+ //debug function that create a displayable frustrum
+ private Geometry createFrustum(Vector3f[] pts, int i) {
+ WireFrustum frustum = new WireFrustum(pts);
+ Geometry frustumMdl = new Geometry("f", frustum);
+ frustumMdl.setCullHint(Spatial.CullHint.Never);
+ frustumMdl.setShadowMode(ShadowMode.Off);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ frustumMdl.setMaterial(mat);
+ switch (i) {
+ case 0:
+ frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink);
+ break;
+ case 1:
+ frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red);
+ break;
+ case 2:
+ frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green);
+ break;
+ case 3:
+ frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue);
+ break;
+ default:
+ frustumMdl.getMaterial().setColor("Color", ColorRGBA.White);
+ break;
+ }
+
+ frustumMdl.updateGeometricState();
+ return frustumMdl;
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ renderManager = rm;
+ viewPort = vp;
+ }
+
+ public boolean isInitialized() {
+ return viewPort != null;
+ }
+
+ /**
+ * returns the light direction used by the processor
+ * @return
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * Sets the light direction to use to compute shadows
+ * @param direction
+ */
+ public void setDirection(Vector3f direction) {
+ this.direction.set(direction).normalizeLocal();
+ }
+
+ @SuppressWarnings("fallthrough")
+ public void postQueue(RenderQueue rq) {
+ GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
+ if (occluders.size() == 0) {
+ return;
+ }
+
+ GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
+ if (receivers.size() == 0) {
+ return;
+ }
+
+ Camera viewCam = viewPort.getCamera();
+
+ float zFar = zFarOverride;
+ if (zFar == 0) {
+ zFar = viewCam.getFrustumFar();
+ }
+
+ //We prevent computing the frustum points and splits with zeroed or negative near clip value
+ float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f);
+ ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points);
+
+ //shadowCam.setDirection(direction);
+ shadowCam.getRotation().lookAt(direction, shadowCam.getUp());
+ shadowCam.update();
+ shadowCam.updateViewProjection();
+
+ PssmShadowUtil.updateFrustumSplits(splitsArray, frustumNear, zFar, lambda);
+
+
+ switch (splitsArray.length) {
+ case 5:
+ splits.a = splitsArray[4];
+ case 4:
+ splits.b = splitsArray[3];
+ case 3:
+ splits.g = splitsArray[2];
+ case 2:
+ case 1:
+ splits.r = splitsArray[1];
+ break;
+ }
+
+ Renderer r = renderManager.getRenderer();
+ renderManager.setForcedMaterial(preshadowMat);
+ renderManager.setForcedTechnique("PreShadow");
+
+ for (int i = 0; i < nbSplits; i++) {
+
+ // update frustum points based on current camera and split
+ ShadowUtil.updateFrustumPoints(viewCam, splitsArray[i], splitsArray[i + 1], 1.0f, points);
+
+ //Updating shadow cam with curent split frustra
+ ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, splitOccluders);
+
+ //saving light view projection matrix for this split
+ lightViewProjectionsMatrices[i] = shadowCam.getViewProjectionMatrix().clone();
+ renderManager.setCamera(shadowCam, false);
+
+ r.setFrameBuffer(shadowFB[i]);
+ r.clearBuffers(false, true, false);
+
+ // render shadow casters to shadow map
+ viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
+ }
+ if (flushQueues) {
+ occluders.clear();
+ }
+ //restore setting for future rendering
+ r.setFrameBuffer(viewPort.getOutputFrameBuffer());
+ renderManager.setForcedMaterial(null);
+ renderManager.setForcedTechnique(null);
+ renderManager.setCamera(viewCam, false);
+
+ }
+
+ //debug only : displays depth shadow maps
+ private void displayShadowMap(Renderer r) {
+ Camera cam = viewPort.getCamera();
+ renderManager.setCamera(cam, true);
+ int h = cam.getHeight();
+ for (int i = 0; i < dispPic.length; i++) {
+ dispPic[i].setPosition(64 * (i + 1) + 128 * i, h / 20f);
+ dispPic[i].setWidth(128);
+ dispPic[i].setHeight(128);
+ dispPic[i].updateGeometricState();
+ renderManager.renderGeometry(dispPic[i]);
+ }
+ renderManager.setCamera(cam, false);
+ }
+
+ /**For dubuging purpose
+ * Allow to "snapshot" the current frustrum to the scene
+ */
+ public void displayDebug() {
+ debug = true;
+ }
+
+ public void postFrame(FrameBuffer out) {
+ Camera cam = viewPort.getCamera();
+ if (!noOccluders) {
+ postshadowMat.setColor("Splits", splits);
+ for (int i = 0; i < nbSplits; i++) {
+ postshadowMat.setMatrix4("LightViewProjectionMatrix" + i, lightViewProjectionsMatrices[i]);
+ }
+ renderManager.setForcedMaterial(postshadowMat);
+
+ viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
+
+ renderManager.setForcedMaterial(null);
+ renderManager.setCamera(cam, false);
+
+ }
+ if (debug) {
+ displayShadowMap(renderManager.getRenderer());
+ }
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void cleanup() {
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ /**
+ * returns the labda parameter<br>
+ * see {@link setLambda(float lambda)}
+ * @return lambda
+ */
+ public float getLambda() {
+ return lambda;
+ }
+
+ /*
+ * Adjust the repartition of the different shadow maps in the shadow extend
+ * usualy goes from 0.0 to 1.0
+ * a low value give a more linear repartition resulting in a constant quality in the shadow over the extends, but near shadows could look very jagged
+ * a high value give a more logarithmic repartition resulting in a high quality for near shadows, but the quality quickly decrease over the extend.
+ * the default value is set to 0.65f (theoric optimal value).
+ * @param lambda the lambda value.
+ */
+ public void setLambda(float lambda) {
+ this.lambda = lambda;
+ }
+
+ /**
+ * How far the shadows are rendered in the view
+ * see {@link setShadowZExtend(float zFar)}
+ * @return shadowZExtend
+ */
+ public float getShadowZExtend() {
+ return zFarOverride;
+ }
+
+ /**
+ * Set the distance from the eye where the shadows will be rendered
+ * default value is dynamicaly computed to the shadow casters/receivers union bound zFar, capped to view frustum far value.
+ * @param zFar the zFar values that override the computed one
+ */
+ public void setShadowZExtend(float zFar) {
+ this.zFarOverride = zFar;
+ }
+
+ /**
+ * returns the shdaow intensity<br>
+ * see {@link setShadowIntensity(float shadowIntensity)}
+ * @return shadowIntensity
+ */
+ public float getShadowIntensity() {
+ return shadowIntensity;
+ }
+
+ /**
+ * Set the shadowIntensity, the value should be between 0 and 1,
+ * a 0 value gives a bright and invisilble shadow,
+ * a 1 value gives a pitch black shadow,
+ * default is 0.7
+ * @param shadowIntensity the darkness of the shadow
+ */
+ public void setShadowIntensity(float shadowIntensity) {
+ this.shadowIntensity = shadowIntensity;
+ postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
+ }
+
+ /**
+ * returns the edges thickness <br>
+ * see {@link setEdgesThickness(int edgesThickness)}
+ * @return edgesThickness
+ */
+ public int getEdgesThickness() {
+ return (int) (edgesThickness * 10);
+ }
+
+ /**
+ * Sets the shadow edges thickness. default is 1, setting it to lower values can help to reduce the jagged effect of the shadow edges
+ * @param edgesThickness
+ */
+ public void setEdgesThickness(int edgesThickness) {
+ this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10));
+ this.edgesThickness *= 0.1f;
+ postshadowMat.setFloat("PCFEdge", edgesThickness);
+ }
+
+ /**
+ * returns true if the PssmRenderer flushed the shadow queues
+ * @return flushQueues
+ */
+ public boolean isFlushQueues() {
+ return flushQueues;
+ }
+
+ /**
+ * Set this to false if you want to use several PssmRederers to have multiple shadows cast by multiple light sources.
+ * Make sure the last PssmRenderer in the stack DO flush the queues, but not the others
+ * @param flushQueues
+ */
+ public void setFlushQueues(boolean flushQueues) {
+ this.flushQueues = flushQueues;
+ }
+}
diff --git a/engine/src/core/com/jme3/shadow/PssmShadowUtil.java b/engine/src/core/com/jme3/shadow/PssmShadowUtil.java
new file mode 100644
index 0000000..2633625
--- /dev/null
+++ b/engine/src/core/com/jme3/shadow/PssmShadowUtil.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix4f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.GeometryList;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+/**
+ * Includes various useful shadow mapping functions.
+ *
+ * @see
+ * <ul>
+ * <li><a href="http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/">http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/</a></li>
+ * <li><a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a></li>
+ * </ul>
+ * for more info.
+ */
+public final class PssmShadowUtil {
+
+ /**
+ * Updates the frustum splits stores in <code>splits</code> using PSSM.
+ */
+ public static void updateFrustumSplits(float[] splits, float near, float far, float lambda) {
+ for (int i = 0; i < splits.length; i++) {
+ float IDM = i / (float) splits.length;
+ float log = near * FastMath.pow((far / near), IDM);
+ float uniform = near + (far - near) * IDM;
+ splits[i] = log * lambda + uniform * (1.0f - lambda);
+ }
+
+ // This is used to improve the correctness of the calculations. Our main near- and farplane
+ // of the camera always stay the same, no matter what happens.
+ splits[0] = near;
+ splits[splits.length - 1] = far;
+ }
+
+ /**
+ * Compute the Zfar in the model vieuw to adjust the Zfar distance for the splits calculation
+ */
+ public static float computeZFar(GeometryList occ, GeometryList recv, Camera cam) {
+ Matrix4f mat = cam.getViewMatrix();
+ BoundingBox bbOcc = ShadowUtil.computeUnionBound(occ, mat);
+ BoundingBox bbRecv = ShadowUtil.computeUnionBound(recv, mat);
+
+ return min(max(bbOcc.getZExtent() - bbOcc.getCenter().z, bbRecv.getZExtent() - bbRecv.getCenter().z), cam.getFrustumFar());
+ }
+}
diff --git a/engine/src/core/com/jme3/shadow/ShadowCamera.java b/engine/src/core/com/jme3/shadow/ShadowCamera.java
new file mode 100644
index 0000000..920e456
--- /dev/null
+++ b/engine/src/core/com/jme3/shadow/ShadowCamera.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.PointLight;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+
+/**
+ * Creates a camera according to a light
+ * Handy to compute projection matrix of a light
+ * @author Kirill Vainer
+ */
+public class ShadowCamera {
+
+ private Vector3f[] points = new Vector3f[8];
+ private Light target;
+
+ public ShadowCamera(Light target) {
+ this.target = target;
+ for (int i = 0; i < points.length; i++) {
+ points[i] = new Vector3f();
+ }
+ }
+
+ /**
+ * Updates the camera view direction and position based on the light
+ */
+ public void updateLightCamera(Camera lightCam) {
+ if (target.getType() == Light.Type.Directional) {
+ DirectionalLight dl = (DirectionalLight) target;
+ lightCam.setParallelProjection(true);
+ lightCam.setLocation(Vector3f.ZERO);
+ lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y);
+ lightCam.setFrustum(-1, 1, -1, 1, 1, -1);
+ } else {
+ PointLight pl = (PointLight) target;
+ lightCam.setParallelProjection(false);
+ lightCam.setLocation(pl.getPosition());
+ // direction will have to be calculated automatically
+ lightCam.setFrustumPerspective(45, 1, 1, 300);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/shadow/ShadowUtil.java b/engine/src/core/com/jme3/shadow/ShadowUtil.java
new file mode 100644
index 0000000..5832bda
--- /dev/null
+++ b/engine/src/core/com/jme3/shadow/ShadowUtil.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.scene.Geometry;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Includes various useful shadow mapping functions.
+ *
+ * @see
+ * <ul>
+ * <li><a href="http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/">http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/</a></li>
+ * <li><a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a></li>
+ * </ul>
+ * for more info.
+ */
+public class ShadowUtil {
+
+ /**
+ * Updates a points arrays with the frustum corners of the provided camera.
+ * @param viewCam
+ * @param points
+ */
+ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
+ int w = viewCam.getWidth();
+ int h = viewCam.getHeight();
+ float n = viewCam.getFrustumNear();
+ float f = viewCam.getFrustumFar();
+
+ points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), n));
+ points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), n));
+ points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), n));
+ points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), n));
+
+ points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), f));
+ points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), f));
+ points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), f));
+ points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), f));
+ }
+
+ /**
+ * Updates the points array to contain the frustum corners of the given
+ * camera. The nearOverride and farOverride variables can be used
+ * to override the camera's near/far values with own values.
+ *
+ * TODO: Reduce creation of new vectors
+ *
+ * @param viewCam
+ * @param nearOverride
+ * @param farOverride
+ */
+ public static void updateFrustumPoints(Camera viewCam,
+ float nearOverride,
+ float farOverride,
+ float scale,
+ Vector3f[] points) {
+
+ Vector3f pos = viewCam.getLocation();
+ Vector3f dir = viewCam.getDirection();
+ Vector3f up = viewCam.getUp();
+
+ float depthHeightRatio = viewCam.getFrustumTop() / viewCam.getFrustumNear();
+ float near = nearOverride;
+ float far = farOverride;
+ float ftop = viewCam.getFrustumTop();
+ float fright = viewCam.getFrustumRight();
+ float ratio = fright / ftop;
+
+ float near_height;
+ float near_width;
+ float far_height;
+ float far_width;
+
+ if (viewCam.isParallelProjection()) {
+ near_height = ftop;
+ near_width = near_height * ratio;
+ far_height = ftop;
+ far_width = far_height * ratio;
+ } else {
+ near_height = depthHeightRatio * near;
+ near_width = near_height * ratio;
+ far_height = depthHeightRatio * far;
+ far_width = far_height * ratio;
+ }
+
+ Vector3f right = dir.cross(up).normalizeLocal();
+
+ Vector3f temp = new Vector3f();
+ temp.set(dir).multLocal(far).addLocal(pos);
+ Vector3f farCenter = temp.clone();
+ temp.set(dir).multLocal(near).addLocal(pos);
+ Vector3f nearCenter = temp.clone();
+
+ Vector3f nearUp = temp.set(up).multLocal(near_height).clone();
+ Vector3f farUp = temp.set(up).multLocal(far_height).clone();
+ Vector3f nearRight = temp.set(right).multLocal(near_width).clone();
+ Vector3f farRight = temp.set(right).multLocal(far_width).clone();
+
+ points[0].set(nearCenter).subtractLocal(nearUp).subtractLocal(nearRight);
+ points[1].set(nearCenter).addLocal(nearUp).subtractLocal(nearRight);
+ points[2].set(nearCenter).addLocal(nearUp).addLocal(nearRight);
+ points[3].set(nearCenter).subtractLocal(nearUp).addLocal(nearRight);
+
+ points[4].set(farCenter).subtractLocal(farUp).subtractLocal(farRight);
+ points[5].set(farCenter).addLocal(farUp).subtractLocal(farRight);
+ points[6].set(farCenter).addLocal(farUp).addLocal(farRight);
+ points[7].set(farCenter).subtractLocal(farUp).addLocal(farRight);
+
+ if (scale != 1.0f) {
+ // find center of frustum
+ Vector3f center = new Vector3f();
+ for (int i = 0; i < 8; i++) {
+ center.addLocal(points[i]);
+ }
+ center.divideLocal(8f);
+
+ Vector3f cDir = new Vector3f();
+ for (int i = 0; i < 8; i++) {
+ cDir.set(points[i]).subtractLocal(center);
+ cDir.multLocal(scale - 1.0f);
+ points[i].addLocal(cDir);
+ }
+ }
+ }
+
+ /**
+ * Compute bounds of a geomList
+ * @param list
+ * @param transform
+ * @return
+ */
+ public static BoundingBox computeUnionBound(GeometryList list, Transform transform) {
+ BoundingBox bbox = new BoundingBox();
+ for (int i = 0; i < list.size(); i++) {
+ BoundingVolume vol = list.get(i).getWorldBound();
+ BoundingVolume newVol = vol.transform(transform);
+ //Nehon : prevent NaN and infinity values to screw the final bounding box
+ if (!Float.isNaN(newVol.getCenter().x) && !Float.isInfinite(newVol.getCenter().x)) {
+ bbox.mergeLocal(newVol);
+ }
+ }
+ return bbox;
+ }
+
+ /**
+ * Compute bounds of a geomList
+ * @param list
+ * @param mat
+ * @return
+ */
+ public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) {
+ BoundingBox bbox = new BoundingBox();
+ BoundingVolume store = null;
+ for (int i = 0; i < list.size(); i++) {
+ BoundingVolume vol = list.get(i).getWorldBound();
+ store = vol.clone().transform(mat, null);
+ //Nehon : prevent NaN and infinity values to screw the final bounding box
+ if (!Float.isNaN(store.getCenter().x) && !Float.isInfinite(store.getCenter().x)) {
+ bbox.mergeLocal(store);
+ }
+ }
+ return bbox;
+ }
+
+ /**
+ * Computes the bounds of multiple bounding volumes
+ * @param bv
+ * @return
+ */
+ public static BoundingBox computeUnionBound(List<BoundingVolume> bv) {
+ BoundingBox bbox = new BoundingBox();
+ for (int i = 0; i < bv.size(); i++) {
+ BoundingVolume vol = bv.get(i);
+ bbox.mergeLocal(vol);
+ }
+ return bbox;
+ }
+
+ /**
+ * Compute bounds from an array of points
+ * @param pts
+ * @param transform
+ * @return
+ */
+ public static BoundingBox computeBoundForPoints(Vector3f[] pts, Transform transform) {
+ Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY);
+ Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY);
+ Vector3f temp = new Vector3f();
+ for (int i = 0; i < pts.length; i++) {
+ transform.transformVector(pts[i], temp);
+
+ min.minLocal(temp);
+ max.maxLocal(temp);
+ }
+ Vector3f center = min.add(max).multLocal(0.5f);
+ Vector3f extent = max.subtract(min).multLocal(0.5f);
+ return new BoundingBox(center, extent.x, extent.y, extent.z);
+ }
+
+ /**
+ * Compute bounds from an array of points
+ * @param pts
+ * @param mat
+ * @return
+ */
+ public static BoundingBox computeBoundForPoints(Vector3f[] pts, Matrix4f mat) {
+ Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY);
+ Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY);
+ Vector3f temp = new Vector3f();
+
+ for (int i = 0; i < pts.length; i++) {
+ float w = mat.multProj(pts[i], temp);
+
+ temp.x /= w;
+ temp.y /= w;
+ // Why was this commented out?
+ temp.z /= w;
+
+ min.minLocal(temp);
+ max.maxLocal(temp);
+ }
+
+ Vector3f center = min.add(max).multLocal(0.5f);
+ Vector3f extent = max.subtract(min).multLocal(0.5f);
+ //Nehon 08/18/2010 : Added an offset to the extend to avoid banding artifacts when the frustum are aligned
+ return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f);
+ }
+
+ /**
+ * Updates the shadow camera to properly contain the given
+ * points (which contain the eye camera frustum corners)
+ *
+ * @param shadowCam
+ * @param points
+ */
+ public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) {
+ boolean ortho = shadowCam.isParallelProjection();
+ shadowCam.setProjectionMatrix(null);
+
+ if (ortho) {
+ shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
+ } else {
+ shadowCam.setFrustumPerspective(45, 1, 1, 150);
+ }
+
+ Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
+ Matrix4f projMatrix = shadowCam.getProjectionMatrix();
+
+ BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);
+
+ Vector3f splitMin = splitBB.getMin(null);
+ Vector3f splitMax = splitBB.getMax(null);
+
+// splitMin.z = 0;
+
+ // Create the crop matrix.
+ float scaleX, scaleY, scaleZ;
+ float offsetX, offsetY, offsetZ;
+
+ scaleX = 2.0f / (splitMax.x - splitMin.x);
+ scaleY = 2.0f / (splitMax.y - splitMin.y);
+ offsetX = -0.5f * (splitMax.x + splitMin.x) * scaleX;
+ offsetY = -0.5f * (splitMax.y + splitMin.y) * scaleY;
+ scaleZ = 1.0f / (splitMax.z - splitMin.z);
+ offsetZ = -splitMin.z * scaleZ;
+
+ Matrix4f cropMatrix = new Matrix4f(scaleX, 0f, 0f, offsetX,
+ 0f, scaleY, 0f, offsetY,
+ 0f, 0f, scaleZ, offsetZ,
+ 0f, 0f, 0f, 1f);
+
+
+ Matrix4f result = new Matrix4f();
+ result.set(cropMatrix);
+ result.multLocal(projMatrix);
+
+ shadowCam.setProjectionMatrix(result);
+ }
+
+ /**
+ * Updates the shadow camera to properly contain the given
+ * points (which contain the eye camera frustum corners) and the
+ * shadow occluder objects.
+ *
+ * @param occluders
+ * @param receivers
+ * @param shadowCam
+ * @param points
+ */
+ public static void updateShadowCamera(GeometryList occluders,
+ GeometryList receivers,
+ Camera shadowCam,
+ Vector3f[] points) {
+ updateShadowCamera(occluders, receivers, shadowCam, points, null);
+ }
+
+ /**
+ * Updates the shadow camera to properly contain the given
+ * points (which contain the eye camera frustum corners) and the
+ * shadow occluder objects.
+ *
+ * @param occluders
+ * @param shadowCam
+ * @param points
+ */
+ public static void updateShadowCamera(GeometryList occluders,
+ GeometryList receivers,
+ Camera shadowCam,
+ Vector3f[] points,
+ GeometryList splitOccluders) {
+
+ boolean ortho = shadowCam.isParallelProjection();
+
+ shadowCam.setProjectionMatrix(null);
+
+ if (ortho) {
+ shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
+ } else {
+ shadowCam.setFrustumPerspective(45, 1, 1, 150);
+ }
+
+ // create transform to rotate points to viewspace
+ Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
+
+ BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);
+
+ ArrayList<BoundingVolume> visRecvList = new ArrayList<BoundingVolume>();
+ for (int i = 0; i < receivers.size(); i++) {
+ // convert bounding box to light's viewproj space
+ Geometry receiver = receivers.get(i);
+ BoundingVolume bv = receiver.getWorldBound();
+ BoundingVolume recvBox = bv.transform(viewProjMatrix, null);
+
+ if (splitBB.intersects(recvBox)) {
+ visRecvList.add(recvBox);
+ }
+ }
+
+ ArrayList<BoundingVolume> visOccList = new ArrayList<BoundingVolume>();
+ for (int i = 0; i < occluders.size(); i++) {
+ // convert bounding box to light's viewproj space
+ Geometry occluder = occluders.get(i);
+ BoundingVolume bv = occluder.getWorldBound();
+ BoundingVolume occBox = bv.transform(viewProjMatrix, null);
+
+ boolean intersects = splitBB.intersects(occBox);
+ if (!intersects && occBox instanceof BoundingBox) {
+ BoundingBox occBB = (BoundingBox) occBox;
+ //Kirill 01/10/2011
+ // Extend the occluder further into the frustum
+ // This fixes shadow dissapearing issues when
+ // the caster itself is not in the view camera
+ // but its shadow is in the camera
+ // The number is in world units
+ occBB.setZExtent(occBB.getZExtent() + 50);
+ occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
+ if (splitBB.intersects(occBB)) {
+ // To prevent extending the depth range too much
+ // We return the bound to its former shape
+ // Before adding it
+ occBB.setZExtent(occBB.getZExtent() - 50);
+ occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
+ visOccList.add(occBox);
+ if (splitOccluders != null) {
+ splitOccluders.add(occluder);
+ }
+ }
+ } else if (intersects) {
+ visOccList.add(occBox);
+ if (splitOccluders != null) {
+ splitOccluders.add(occluder);
+ }
+ }
+ }
+
+ BoundingBox casterBB = computeUnionBound(visOccList);
+ BoundingBox receiverBB = computeUnionBound(visRecvList);
+
+ //Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive shadows
+ if (visOccList.size() != visRecvList.size()) {
+ casterBB.setXExtent(casterBB.getXExtent() + 2.0f);
+ casterBB.setYExtent(casterBB.getYExtent() + 2.0f);
+ casterBB.setZExtent(casterBB.getZExtent() + 2.0f);
+ }
+
+ Vector3f casterMin = casterBB.getMin(null);
+ Vector3f casterMax = casterBB.getMax(null);
+
+ Vector3f receiverMin = receiverBB.getMin(null);
+ Vector3f receiverMax = receiverBB.getMax(null);
+
+ Vector3f splitMin = splitBB.getMin(null);
+ Vector3f splitMax = splitBB.getMax(null);
+
+ splitMin.z = 0;
+
+ if (!ortho) {
+ shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
+ }
+
+ Matrix4f projMatrix = shadowCam.getProjectionMatrix();
+
+ Vector3f cropMin = new Vector3f();
+ Vector3f cropMax = new Vector3f();
+
+ // IMPORTANT: Special handling for Z values
+ cropMin.x = max(max(casterMin.x, receiverMin.x), splitMin.x);
+ cropMax.x = min(min(casterMax.x, receiverMax.x), splitMax.x);
+
+ cropMin.y = max(max(casterMin.y, receiverMin.y), splitMin.y);
+ cropMax.y = min(min(casterMax.y, receiverMax.y), splitMax.y);
+
+ cropMin.z = min(casterMin.z, splitMin.z);
+ cropMax.z = min(receiverMax.z, splitMax.z);
+
+
+ // Create the crop matrix.
+ float scaleX, scaleY, scaleZ;
+ float offsetX, offsetY, offsetZ;
+
+ scaleX = (2.0f) / (cropMax.x - cropMin.x);
+ scaleY = (2.0f) / (cropMax.y - cropMin.y);
+
+ offsetX = -0.5f * (cropMax.x + cropMin.x) * scaleX;
+ offsetY = -0.5f * (cropMax.y + cropMin.y) * scaleY;
+
+ scaleZ = 1.0f / (cropMax.z - cropMin.z);
+ offsetZ = -cropMin.z * scaleZ;
+
+
+
+ Matrix4f cropMatrix = new Matrix4f(scaleX, 0f, 0f, offsetX,
+ 0f, scaleY, 0f, offsetY,
+ 0f, 0f, scaleZ, offsetZ,
+ 0f, 0f, 0f, 1f);
+
+
+ Matrix4f result = new Matrix4f();
+ result.set(cropMatrix);
+ result.multLocal(projMatrix);
+
+ shadowCam.setProjectionMatrix(result);
+
+ }
+}
diff --git a/engine/src/core/com/jme3/system/Annotations.java b/engine/src/core/com/jme3/system/Annotations.java
new file mode 100644
index 0000000..0cf72eb
--- /dev/null
+++ b/engine/src/core/com/jme3/system/Annotations.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+import checkers.quals.TypeQualifier;
+import java.lang.annotation.*;
+
+/**
+ * This class contains the Annotation definitions for jME3. Mostly these are used
+ * for code error checking.
+ * @author normenhansen
+ */
+public class Annotations {
+
+ /**
+ * Annotation used for math primitive fields, method parameters or method return values.
+ * Specifies that the primitve is read only and should not be changed.
+ */
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @TypeQualifier
+ @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.TYPE, ElementType.METHOD})
+ public @interface ReadOnly {
+ }
+
+ /**
+ * Annotation used for methods in math primitives that are destructive to the
+ * object (xxxLocal, setXXX etc.).
+ */
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @TypeQualifier
+ @Target({ElementType.METHOD})
+ public @interface Destructive {
+ }
+
+ /**
+ * Annotation used for public methods that are not to be called by users.
+ * Examples include update() methods etc.
+ */
+ public @interface Internal {
+ }
+}
diff --git a/engine/src/core/com/jme3/system/AppSettings.java b/engine/src/core/com/jme3/system/AppSettings.java
new file mode 100644
index 0000000..ad9facc
--- /dev/null
+++ b/engine/src/core/com/jme3/system/AppSettings.java
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+/**
+ * <code>AppSettings</code> provides a store of configuration
+ * to be used by the application.
+ * <p>
+ * By default only the {@link JmeContext context} uses the configuration,
+ * however the user may set and retrieve the settings as well.
+ *
+ * @author Kirill Vainer
+ */
+public final class AppSettings extends HashMap<String, Object> {
+
+ private static final AppSettings defaults = new AppSettings(false);
+
+ /**
+ * Use LWJGL as the display system and force using the OpenGL1.1 renderer.
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
+ */
+ public static final String LWJGL_OPENGL1 = "LWJGL-OPENGL1";
+
+ /**
+ * Use LWJGL as the display system and force using the OpenGL2.0 renderer.
+ * <p>
+ * If the underlying system does not support OpenGL2.0, then the context
+ * initialization will throw an exception.
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
+ */
+ public static final String LWJGL_OPENGL2 = "LWJGL-OpenGL2";
+
+ /**
+ * Use LWJGL as the display system and force using the core OpenGL3.3 renderer.
+ * <p>
+ * If the underlying system does not support OpenGL3.3, then the context
+ * initialization will throw an exception. Note that currently jMonkeyEngine
+ * does not have any shaders that support OpenGL3.3 therefore this
+ * option is not useful.
+ *
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
+ */
+ public static final String LWJGL_OPENGL3 = "LWJGL-OpenGL3";
+
+ /**
+ * Use LWJGL as the display system and allow the context
+ * to choose an appropriate renderer based on system capabilities.
+ * <p>
+ * If the GPU supports OpenGL2 or later, then the OpenGL2.0 renderer will
+ * be used, otherwise, the OpenGL1.1 renderer is used.
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
+ */
+ public static final String LWJGL_OPENGL_ANY = "LWJGL-OpenGL-Any";
+
+ /**
+ * The JOGL renderer is no longer supported by jME.
+ *
+ * @deprecated Use the LWJGL renderer instead.
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
+ */
+ @Deprecated
+ public static final String JOGL = "JOGL";
+
+ /**
+ * The "NULL" option is no longer supported
+ *
+ * @deprecated Specify the "null" value instead
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
+ * @see AppSettings#setAudioRenderer(java.lang.String)
+ */
+ @Deprecated
+ public static final String NULL = "NULL";
+
+ /**
+ * Use the LWJGL OpenAL based renderer for audio capabilities.
+ *
+ * @see AppSettings#setAudioRenderer(java.lang.String)
+ */
+ public static final String LWJGL_OPENAL = "LWJGL";
+
+ static {
+ defaults.put("Width", 640);
+ defaults.put("Height", 480);
+ defaults.put("BitsPerPixel", 24);
+ defaults.put("Frequency", 60);
+ defaults.put("DepthBits", 24);
+ defaults.put("StencilBits", 0);
+ defaults.put("Samples", 0);
+ defaults.put("Fullscreen", false);
+ defaults.put("Title", "jMonkey Engine 3.0");
+ defaults.put("Renderer", LWJGL_OPENGL2);
+ defaults.put("AudioRenderer", LWJGL_OPENAL);
+ defaults.put("DisableJoysticks", true);
+ defaults.put("UseInput", true);
+ defaults.put("VSync", false);
+ defaults.put("FrameRate", -1);
+ defaults.put("SettingsDialogImage", "/com/jme3/app/Monkey.png");
+ // defaults.put("Icons", null);
+ }
+
+ /**
+ * Create a new instance of <code>AppSettings</code>.
+ * <p>
+ * If <code>loadDefaults</code> is true, then the default settings
+ * will be set on the AppSettings.
+ * Use false if you want to change some settings but you would like the
+ * application to load settings from previous launches.
+ *
+ * @param loadDefaults If default settings are to be loaded.
+ */
+ public AppSettings(boolean loadDefaults) {
+ if (loadDefaults) {
+ putAll(defaults);
+ }
+ }
+
+ /**
+ * Copies all settings from <code>other</code> to <code>this</code>
+ * AppSettings.
+ * <p>
+ * Any settings that are specified in other will overwrite settings
+ * set on this AppSettings.
+ *
+ * @param other The AppSettings to copy the settings from
+ */
+ public void copyFrom(AppSettings other) {
+ this.putAll(other);
+ }
+
+ /**
+ * Same as {@link #copyFrom(com.jme3.system.AppSettings) }, except
+ * doesn't overwrite settings that are already set.
+ *
+ * @param other The AppSettings to merge the settings from
+ */
+ public void mergeFrom(AppSettings other) {
+ for (String key : other.keySet()) {
+ if (get(key) == null) {
+ put(key, other.get(key));
+ }
+ }
+ }
+
+ /**
+ * Loads the settings from the given properties input stream.
+ *
+ * @param in The InputStream to load from
+ * @throws IOException If an IOException occurs
+ *
+ * @see #save(java.io.OutputStream)
+ */
+ public void load(InputStream in) throws IOException {
+ Properties props = new Properties();
+ props.load(in);
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {
+ String key = (String) entry.getKey();
+ String val = (String) entry.getValue();
+ if (val != null) {
+ val = val.trim();
+ }
+ if (key.endsWith("(int)")) {
+ key = key.substring(0, key.length() - 5);
+ int iVal = Integer.parseInt(val);
+ putInteger(key, iVal);
+ } else if (key.endsWith("(string)")) {
+ putString(key.substring(0, key.length() - 8), val);
+ } else if (key.endsWith("(bool)")) {
+ boolean bVal = Boolean.parseBoolean(val);
+ putBoolean(key.substring(0, key.length() - 6), bVal);
+ } else {
+ throw new IOException("Cannot parse key: " + key);
+ }
+ }
+ }
+
+ /**
+ * Saves all settings to the given properties output stream.
+ *
+ * @param out The OutputStream to write to
+ * @throws IOException If an IOException occurs
+ *
+ * @see #load(java.io.InputStream)
+ */
+ public void save(OutputStream out) throws IOException {
+ Properties props = new Properties();
+ for (Map.Entry<String, Object> entry : entrySet()) {
+ Object val = entry.getValue();
+ String type;
+ if (val instanceof Integer) {
+ type = "(int)";
+ } else if (val instanceof String) {
+ type = "(string)";
+ } else if (val instanceof Boolean) {
+ type = "(bool)";
+ } else {
+ throw new UnsupportedEncodingException();
+ }
+ props.setProperty(entry.getKey() + type, val.toString());
+ }
+ props.store(out, "jME3 AppSettings");
+ }
+
+ /**
+ * Loads settings previously saved in the Java preferences.
+ *
+ * @param preferencesKey The preferencesKey previously used to save the settings.
+ * @throws BackingStoreException If an exception occurs with the preferences
+ *
+ * @see #save(java.lang.String)
+ */
+ public void load(String preferencesKey) throws BackingStoreException {
+ Preferences prefs = Preferences.userRoot().node(preferencesKey);
+ String[] keys = prefs.keys();
+ if (keys != null) {
+ for (String key : keys) {
+ Object defaultValue = defaults.get(key);
+ if (defaultValue instanceof Integer) {
+ put(key, prefs.getInt(key, (Integer) defaultValue));
+ } else if (defaultValue instanceof String) {
+ put(key, prefs.get(key, (String) defaultValue));
+ } else if (defaultValue instanceof Boolean) {
+ put(key, prefs.getBoolean(key, (Boolean) defaultValue));
+ }
+ }
+ }
+ }
+
+ /**
+ * Saves settings into the Java preferences.
+ * <p>
+ * On the Windows operating system, the preferences are saved in the registry
+ * at the following key:<br>
+ * <code>HKEY_CURRENT_USER\Software\JavaSoft\Prefs\[preferencesKey]</code>
+ *
+ * @param preferencesKey The preferences key to save at. Generally the
+ * application's unique name.
+ *
+ * @throws BackingStoreException If an exception occurs with the preferences
+ */
+ public void save(String preferencesKey) throws BackingStoreException {
+ Preferences prefs = Preferences.userRoot().node(preferencesKey);
+ for (String key : keySet()) {
+ prefs.put(key, get(key).toString());
+ }
+ }
+
+ /**
+ * Get an integer from the settings.
+ * <p>
+ * If the key is not set, then 0 is returned.
+ */
+ public int getInteger(String key) {
+ Integer i = (Integer) get(key);
+ if (i == null) {
+ return 0;
+ }
+
+ return i.intValue();
+ }
+
+ /**
+ * Get a boolean from the settings.
+ * <p>
+ * If the key is not set, then false is returned.
+ */
+ public boolean getBoolean(String key) {
+ Boolean b = (Boolean) get(key);
+ if (b == null) {
+ return false;
+ }
+
+ return b.booleanValue();
+ }
+
+ /**
+ * Get a string from the settings.
+ * <p>
+ * If the key is not set, then null is returned.
+ */
+ public String getString(String key) {
+ String s = (String) get(key);
+ if (s == null) {
+ return null;
+ }
+
+ return s;
+ }
+
+ /**
+ * Set an integer on the settings.
+ */
+ public void putInteger(String key, int value) {
+ put(key, Integer.valueOf(value));
+ }
+
+ /**
+ * Set a boolean on the settings.
+ */
+ public void putBoolean(String key, boolean value) {
+ put(key, Boolean.valueOf(value));
+ }
+
+ /**
+ * Set a string on the settings.
+ */
+ public void putString(String key, String value) {
+ put(key, value);
+ }
+
+ /**
+ * @param frameRate The frame-rate is the upper limit on how high
+ * the application's frames-per-second can go.
+ * (Default: -1 no frame rate limit imposed)
+ */
+ public void setFrameRate(int frameRate) {
+ putInteger("FrameRate", frameRate);
+ }
+
+ /**
+ * @param use If true, the application will initialize and use input.
+ * Set to false for headless applications that do not require keyboard
+ * or mouse input.
+ * (Default: true)
+ */
+ public void setUseInput(boolean use) {
+ putBoolean("UseInput", use);
+ }
+
+ /**
+ * @param use If true, the application will initialize and use joystick
+ * input. Set to false if no joystick input is desired.
+ * (Default: false)
+ */
+ public void setUseJoysticks(boolean use) {
+ putBoolean("DisableJoysticks", !use);
+ }
+
+ /**
+ * Set the graphics renderer to use, one of:<br>
+ * <ul>
+ * <li>AppSettings.LWJGL_OPENGL1 - Force OpenGL1.1 compatability</li>
+ * <li>AppSettings.LWJGL_OPENGL2 - Force OpenGL2 compatability</li>
+ * <li>AppSettings.LWJGL_OPENGL3 - Force OpenGL3.3 compatability</li>
+ * <li>AppSettings.LWJGL_OPENGL_ANY - Choose an appropriate
+ * OpenGL version based on system capabilities</li>
+ * <li>null - Disable graphics rendering</li>
+ * </ul>
+ * @param renderer The renderer to set
+ * (Default: AppSettings.LWJGL_OPENGL2)
+ */
+ public void setRenderer(String renderer) {
+ putString("Renderer", renderer);
+ }
+
+ /**
+ * Set a custom graphics renderer to use. The class should implement
+ * the {@link JmeContext} interface.
+ * @param clazz The custom context class.
+ * (Default: not set)
+ */
+ public void setCustomRenderer(Class<? extends JmeContext> clazz){
+ put("Renderer", "CUSTOM" + clazz.getName());
+ }
+
+ /**
+ * Set the audio renderer to use. One of:<br>
+ * <ul>
+ * <li>AppSettings.LWJGL_OPENAL - Default for LWJGL</li>
+ * <li>null - Disable audio</li>
+ * </ul>
+ * @param audioRenderer
+ * (Default: LWJGL)
+ */
+ public void setAudioRenderer(String audioRenderer) {
+ putString("AudioRenderer", audioRenderer);
+ }
+
+ /**
+ * @param value the width for the rendering display.
+ * (Default: 640)
+ */
+ public void setWidth(int value) {
+ putInteger("Width", value);
+ }
+
+ /**
+ * @param value the height for the rendering display.
+ * (Default: 480)
+ */
+ public void setHeight(int value) {
+ putInteger("Height", value);
+ }
+
+ /**
+ * Set the resolution for the rendering display
+ * @param width The width
+ * @param height The height
+ * (Default: 640x480)
+ */
+ public void setResolution(int width, int height) {
+ setWidth(width);
+ setHeight(height);
+ }
+
+ /**
+ * Set the frequency, also known as refresh rate, for the
+ * rendering display.
+ * @param value The frequency
+ * (Default: 60)
+ */
+ public void setFrequency(int value) {
+ putInteger("Frequency", value);
+ }
+
+ /**
+ * Sets the number of depth bits to use.
+ * <p>
+ * The number of depth bits specifies the precision of the depth buffer.
+ * To increase precision, specify 32 bits. To decrease precision, specify
+ * 16 bits. On some platforms 24 bits might not be supported, in that case,
+ * specify 16 bits.<p>
+ * (Default: 24)
+ *
+ * @param value The depth bits
+ */
+ public void setDepthBits(int value){
+ putInteger("DepthBits", value);
+ }
+
+ /**
+ * Set the number of stencil bits.
+ * <p>
+ * This value is only relevant when the stencil buffer is being used.
+ * Specify 8 to indicate an 8-bit stencil buffer, specify 0 to disable
+ * the stencil buffer.
+ * </p>
+ * (Default: 0)
+ *
+ * @param value Number of stencil bits
+ */
+ public void setStencilBits(int value){
+ putInteger("StencilBits", value);
+ }
+
+ /**
+ * Set the bits per pixel for the display. Appropriate
+ * values are 16 for RGB565 color format, or 24 for RGB8 color format.
+ *
+ * @param value The bits per pixel to set
+ * (Default: 24)
+ */
+ public void setBitsPerPixel(int value) {
+ putInteger("BitsPerPixel", value);
+ }
+
+ /**
+ * Set the number of samples per pixel. A value of 1 indicates
+ * each pixel should be single-sampled, higher values indicate
+ * a pixel should be multi-sampled.
+ *
+ * @param value The number of samples
+ * (Default: 1)
+ */
+ public void setSamples(int value) {
+ putInteger("Samples", value);
+ }
+
+ /**
+ * @param title The title of the rendering display
+ * (Default: jMonkeyEngine 3.0)
+ */
+ public void setTitle(String title) {
+ putString("Title", title);
+ }
+
+ /**
+ * @param value true to enable full-screen rendering, false to render in a window
+ * (Default: false)
+ */
+ public void setFullscreen(boolean value) {
+ putBoolean("Fullscreen", value);
+ }
+
+ /**
+ * Set to true to enable vertical-synchronization, limiting and synchronizing
+ * every frame rendered to the monitor's refresh rate.
+ * @param value
+ * (Default: false)
+ */
+ public void setVSync(boolean value) {
+ putBoolean("VSync", value);
+ }
+
+ /**
+ * Enable 3D stereo.
+ * <p>This feature requires hardware support from the GPU driver.
+ * @see <a href="http://en.wikipedia.org/wiki/Quad_buffering">http://en.wikipedia.org/wiki/Quad_buffering</a><br />
+ * Once enabled, filters or scene processors that handle 3D stereo rendering
+ * could use this feature to render using hardware 3D stereo.</p>
+ * (Default: false)
+ */
+ public void setStereo3D(boolean value){
+ putBoolean("Stereo3D", value);
+ }
+
+ /**
+ * Sets the application icons to be used, with the most preferred first.
+ * For Windows you should supply at least one 16x16 icon and one 32x32. The former is used for the title/task bar,
+ * the latter for the alt-tab icon.
+ * Linux (and similar platforms) expect one 32x32 icon.
+ * Mac OS X should be supplied one 128x128 icon.
+ * <br/>
+ * The icon is used for the settings window, and the LWJGL render window. Not currently supported for JOGL.
+ * Note that a bug in Java 6 (bug ID 6445278, currently hidden but available in Google cache) currently prevents
+ * the icon working for alt-tab on the settings dialog in Windows.
+ *
+ * @param value An array of BufferedImages to use as icons.
+ * (Default: not set)
+ */
+ public void setIcons(Object[] value) {
+ put("Icons", value);
+ }
+
+ /**
+ * Sets the path of the settings dialog image to use.
+ * <p>
+ * The image will be displayed in the settings dialog when the
+ * application is started.
+ * </p>
+ * (Default: /com/jme3/app/Monkey.png)
+ *
+ * @param path The path to the image in the classpath.
+ */
+ public void setSettingsDialogImage(String path) {
+ putString("SettingsDialogImage", path);
+ }
+
+ /**
+ * Get the framerate.
+ * @see #setFrameRate(int)
+ */
+ public int getFrameRate() {
+ return getInteger("FrameRate");
+ }
+
+ /**
+ * Get the use input state.
+ * @see #setUseInput(boolean)
+ */
+ public boolean useInput() {
+ return getBoolean("UseInput");
+ }
+
+ /**
+ * Get the renderer
+ * @see #setRenderer(java.lang.String)
+ */
+ public String getRenderer() {
+ return getString("Renderer");
+ }
+
+ /**
+ * Get the width
+ * @see #setWidth(int)
+ */
+ public int getWidth() {
+ return getInteger("Width");
+ }
+
+ /**
+ * Get the height
+ * @see #setHeight(int)
+ */
+ public int getHeight() {
+ return getInteger("Height");
+ }
+
+ /**
+ * Get the bits per pixel
+ * @see #setBitsPerPixel(int)
+ */
+ public int getBitsPerPixel() {
+ return getInteger("BitsPerPixel");
+ }
+
+ /**
+ * Get the frequency
+ * @see #setFrequency(int)
+ */
+ public int getFrequency() {
+ return getInteger("Frequency");
+ }
+
+ /**
+ * Get the number of depth bits
+ * @see #setDepthBits(int)
+ */
+ public int getDepthBits() {
+ return getInteger("DepthBits");
+ }
+
+ /**
+ * Get the number of stencil bits
+ * @see #setStencilBits(int)
+ */
+ public int getStencilBits() {
+ return getInteger("StencilBits");
+ }
+
+ /**
+ * Get the number of samples
+ * @see #setSamples(int)
+ */
+ public int getSamples() {
+ return getInteger("Samples");
+ }
+
+ /**
+ * Get the application title
+ * @see #setTitle(java.lang.String)
+ */
+ public String getTitle() {
+ return getString("Title");
+ }
+
+ /**
+ * Get the vsync state
+ * @see #setVSync(boolean)
+ */
+ public boolean isVSync() {
+ return getBoolean("VSync");
+ }
+
+ /**
+ * Get the fullscreen state
+ * @see #setFullscreen(boolean)
+ */
+ public boolean isFullscreen() {
+ return getBoolean("Fullscreen");
+ }
+
+ /**
+ * Get the use joysticks state
+ * @see #setUseJoysticks(boolean)
+ */
+ public boolean useJoysticks() {
+ return !getBoolean("DisableJoysticks");
+ }
+
+ /**
+ * Get the audio renderer
+ * @see #setAudioRenderer(java.lang.String)
+ */
+ public String getAudioRenderer() {
+ return getString("AudioRenderer");
+ }
+
+ /**
+ * Get the stereo 3D state
+ * @see #setStereo3D(boolean)
+ */
+ public boolean useStereo3D(){
+ return getBoolean("Stereo3D");
+ }
+
+ /**
+ * Get the icon array
+ * @see #setIcons(java.lang.Object[])
+ */
+ public Object[] getIcons() {
+ return (Object[]) get("Icons");
+ }
+
+ /**
+ * Get the settings dialog image
+ * @see #setSettingsDialogImage(java.lang.String)
+ */
+ public String getSettingsDialogImage() {
+ return getString("SettingsDialogImage");
+ }
+}
diff --git a/engine/src/core/com/jme3/system/JmeContext.java b/engine/src/core/com/jme3/system/JmeContext.java
new file mode 100644
index 0000000..37283a5
--- /dev/null
+++ b/engine/src/core/com/jme3/system/JmeContext.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.renderer.Renderer;
+
+/**
+ * Represents a rendering context within the engine.
+ */
+public interface JmeContext {
+
+ /**
+ * The type of context.
+ */
+ public enum Type {
+ /**
+ * A display can represent a windowed or a fullscreen-exclusive display.
+ * If windowed, the graphics are rendered to a new on-screen surface
+ * enclosed in a window defined by the operating system. Implementations
+ * are encouraged to not use AWT or Swing to create the OpenGL display
+ * but rather use native operating system functions to set up a native
+ * display with the windowing system.
+ */
+ Display,
+
+ /**
+ * A canvas type context makes a rendering surface available as an
+ * AWT {@link java.awt.Canvas} object that can be embedded in a Swing/AWT
+ * frame. To retrieve the Canvas object, you should cast the context
+ * to {@link JmeCanvasContext}.
+ */
+ Canvas,
+
+ /**
+ * An <code>OffscreenSurface</code> is a context that is not visible
+ * by the user. The application can use the offscreen surface to do
+ * General Purpose GPU computations or render a scene into a buffer
+ * in order to save it as a screenshot, video or send through a network.
+ */
+ OffscreenSurface,
+
+ /**
+ * A <code>Headless</code> context is not visible and does not have
+ * any drawable surface. The implementation does not provide any
+ * display, input, or sound support.
+ */
+ Headless;
+ }
+
+ /**
+ * @return The type of the context.
+ */
+ public Type getType();
+
+ /**
+ * @param settings the display settings to use for the created context. If
+ * the context has already been created, then <code>restart()</code> must be called
+ * for the changes to be applied.
+ */
+ public void setSettings(AppSettings settings);
+
+ /**
+ * Sets the listener that will receive events relating to context
+ * creation, update, and destroy.
+ */
+ public void setSystemListener(SystemListener listener);
+
+ /**
+ * @return The current display settings. Note that they might be
+ * different from the ones set with setDisplaySettings() if the context
+ * was restarted or the settings changed internally.
+ */
+ public AppSettings getSettings();
+
+ /**
+ * @return The renderer for this context, or null if not created yet.
+ */
+ public Renderer getRenderer();
+
+ /**
+ * @return Mouse input implementation. May be null if not available.
+ */
+ public MouseInput getMouseInput();
+
+ /**
+ * @return Keyboard input implementation. May be null if not available.
+ */
+ public KeyInput getKeyInput();
+
+ /**
+ * @return Joystick input implementation. May be null if not available.
+ */
+ public JoyInput getJoyInput();
+
+ /**
+ * @return Touch device input implementation. May be null if not available.
+ */
+ public TouchInput getTouchInput();
+
+ /**
+ * @return The timer for this context, or null if not created yet.
+ */
+ public Timer getTimer();
+
+ /**
+ * Sets the title of the display (if available). This does nothing
+ * for fullscreen, headless, or canvas contexts.
+ * @param title The new title of the display.
+ */
+ public void setTitle(String title);
+
+ /**
+ * @return True if the context has been created but not yet destroyed.
+ */
+ public boolean isCreated();
+
+ /**
+ * @return True if the context contains a valid render surface,
+ * if any of the rendering methods in {@link Renderer} are called
+ * while this is <code>false</code>, then the result is undefined.
+ */
+ public boolean isRenderable();
+
+ /**
+ * @param enabled If enabled, the context will automatically flush
+ * frames to the video card (swap buffers) after an update cycle.
+ */
+ public void setAutoFlushFrames(boolean enabled);
+
+ /**
+ * Creates the context and makes it active.
+ *
+ * @param waitFor If true, will wait until context has initialized.
+ */
+ public void create(boolean waitFor);
+
+ /**
+ * Destroys and then re-creates the context. This should be called after
+ * the display settings have been changed.
+ */
+ public void restart();
+
+ /**
+ * Destroys the context completely, making it inactive.
+ *
+ * @param waitFor If true, will wait until the context is destroyed fully.
+ */
+ public void destroy(boolean waitFor);
+
+}
diff --git a/engine/src/core/com/jme3/system/JmeSystem.java b/engine/src/core/com/jme3/system/JmeSystem.java
new file mode 100644
index 0000000..1a13a42
--- /dev/null
+++ b/engine/src/core/com/jme3/system/JmeSystem.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class JmeSystem {
+
+ private static JmeSystemDelegate systemDelegate;
+
+ public static void setSystemDelegate(JmeSystemDelegate systemDelegate) {
+ JmeSystem.systemDelegate = systemDelegate;
+ }
+
+ public static synchronized File getStorageFolder() {
+ checkDelegate();
+ return systemDelegate.getStorageFolder();
+ }
+
+ public static String getFullName() {
+ checkDelegate();
+ return systemDelegate.getFullName();
+ }
+
+ public static InputStream getResourceAsStream(String name) {
+ checkDelegate();
+ return systemDelegate.getResourceAsStream(name);
+ }
+
+ public static URL getResource(String name) {
+ checkDelegate();
+ return systemDelegate.getResource(name);
+ }
+
+ public static boolean trackDirectMemory() {
+ checkDelegate();
+ return systemDelegate.trackDirectMemory();
+ }
+
+ public static void setLowPermissions(boolean lowPerm) {
+ checkDelegate();
+ systemDelegate.setLowPermissions(lowPerm);
+ }
+
+ public static boolean isLowPermissions() {
+ checkDelegate();
+ return systemDelegate.isLowPermissions();
+ }
+
+ public static AssetManager newAssetManager(URL configFile) {
+ checkDelegate();
+ return systemDelegate.newAssetManager(configFile);
+ }
+
+ public static AssetManager newAssetManager() {
+ checkDelegate();
+ return systemDelegate.newAssetManager();
+ }
+
+ public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) {
+ checkDelegate();
+ return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry);
+ }
+
+ public static Platform getPlatform() {
+ checkDelegate();
+ return systemDelegate.getPlatform();
+ }
+
+ public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {
+ checkDelegate();
+ return systemDelegate.newContext(settings, contextType);
+ }
+
+ public static AudioRenderer newAudioRenderer(AppSettings settings) {
+ checkDelegate();
+ return systemDelegate.newAudioRenderer(settings);
+ }
+
+ public static void initialize(AppSettings settings) {
+ checkDelegate();
+ systemDelegate.initialize(settings);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void checkDelegate() {
+ if (systemDelegate == null) {
+ Class<JmeSystemDelegate> systemDelegateClass;
+ try {
+ systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem");
+ systemDelegate = systemDelegateClass.newInstance();
+ } catch (InstantiationException ex) {
+ Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
+ } catch (ClassNotFoundException ex) {
+ Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/system/JmeSystemDelegate.java b/engine/src/core/com/jme3/system/JmeSystemDelegate.java
new file mode 100644
index 0000000..60265ae
--- /dev/null
+++ b/engine/src/core/com/jme3/system/JmeSystemDelegate.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Kirill Vainer, normenhansen
+ */
+public abstract class JmeSystemDelegate {
+
+ protected final Logger logger = Logger.getLogger(JmeSystem.class.getName());
+ protected boolean initialized = false;
+ protected boolean lowPermissions = false;
+ protected File storageFolder = null;
+
+ public synchronized File getStorageFolder() {
+ if (lowPermissions) {
+ throw new UnsupportedOperationException("File system access restricted");
+ }
+ if (storageFolder == null) {
+ // Initialize storage folder
+ storageFolder = new File(System.getProperty("user.home"), ".jme3");
+ if (!storageFolder.exists()) {
+ storageFolder.mkdir();
+ }
+ }
+ return storageFolder;
+ }
+
+ public String getFullName() {
+ return JmeVersion.FULL_NAME;
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return this.getClass().getResourceAsStream(name);
+ }
+
+ public URL getResource(String name) {
+ return this.getClass().getResource(name);
+ }
+
+ public boolean trackDirectMemory() {
+ return false;
+ }
+
+ public void setLowPermissions(boolean lowPerm) {
+ lowPermissions = lowPerm;
+ }
+
+ public boolean isLowPermissions() {
+ return lowPermissions;
+ }
+
+ public abstract AssetManager newAssetManager(URL configFile);
+
+ public abstract AssetManager newAssetManager();
+
+ public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry);
+
+ private boolean is64Bit(String arch) {
+ if (arch.equals("x86")) {
+ return false;
+ } else if (arch.equals("amd64")) {
+ return true;
+ } else if (arch.equals("x86_64")) {
+ return true;
+ } else if (arch.equals("ppc") || arch.equals("PowerPC")) {
+ return false;
+ } else if (arch.equals("ppc64")) {
+ return true;
+ } else if (arch.equals("i386") || arch.equals("i686")) {
+ return false;
+ } else if (arch.equals("universal")) {
+ return false;
+ } else {
+ throw new UnsupportedOperationException("Unsupported architecture: " + arch);
+ }
+ }
+
+ public Platform getPlatform() {
+ String os = System.getProperty("os.name").toLowerCase();
+ String arch = System.getProperty("os.arch").toLowerCase();
+ boolean is64 = is64Bit(arch);
+ if (os.contains("windows")) {
+ return is64 ? Platform.Windows64 : Platform.Windows32;
+ } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) {
+ return is64 ? Platform.Linux64 : Platform.Linux32;
+ } else if (os.contains("mac os x") || os.contains("darwin")) {
+ if (arch.startsWith("ppc")) {
+ return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32;
+ } else {
+ return is64 ? Platform.MacOSX64 : Platform.MacOSX32;
+ }
+ } else {
+ throw new UnsupportedOperationException("The specified platform: " + os + " is not supported.");
+ }
+ }
+
+ public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType);
+
+ public abstract AudioRenderer newAudioRenderer(AppSettings settings);
+
+ public abstract void initialize(AppSettings settings);
+}
diff --git a/engine/src/core/com/jme3/system/JmeVersion.java b/engine/src/core/com/jme3/system/JmeVersion.java
new file mode 100644
index 0000000..95d021f
--- /dev/null
+++ b/engine/src/core/com/jme3/system/JmeVersion.java
@@ -0,0 +1,5 @@
+package com.jme3.system;
+
+public class JmeVersion {
+ public static final String FULL_NAME = "jMonkeyEngine 3.0.0 Beta";
+}
diff --git a/engine/src/core/com/jme3/system/NanoTimer.java b/engine/src/core/com/jme3/system/NanoTimer.java
new file mode 100644
index 0000000..13a150a
--- /dev/null
+++ b/engine/src/core/com/jme3/system/NanoTimer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system;
+
+/**
+ * <code>NanoTimer</code> is a System.nanoTime implementation of <code>Timer</code>.
+ * This is primarily useful for headless applications running on a server.
+ *
+ * @author Matthew D. Hicks
+ */
+public class NanoTimer extends Timer {
+
+ private static final long TIMER_RESOLUTION = 1000000000L;
+ private static final float INVERSE_TIMER_RESOLUTION = 1f/1000000000L;
+
+ private long startTime;
+ private long previousTime;
+ private float tpf;
+ private float fps;
+
+ public NanoTimer() {
+ startTime = System.nanoTime();
+ }
+
+ /**
+ * Returns the time in seconds. The timer starts
+ * at 0.0 seconds.
+ *
+ * @return the current time in seconds
+ */
+ @Override
+ public float getTimeInSeconds() {
+ return getTime() * INVERSE_TIMER_RESOLUTION;
+ }
+
+ public long getTime() {
+ return System.nanoTime() - startTime;
+ }
+
+ public long getResolution() {
+ return TIMER_RESOLUTION;
+ }
+
+ public float getFrameRate() {
+ return fps;
+ }
+
+ public float getTimePerFrame() {
+ return tpf;
+ }
+
+ public void update() {
+ tpf = (getTime() - previousTime) * (1.0f / TIMER_RESOLUTION);
+ fps = 1.0f / tpf;
+ previousTime = getTime();
+ }
+
+ public void reset() {
+ startTime = System.nanoTime();
+ previousTime = getTime();
+ }
+}
diff --git a/engine/src/core/com/jme3/system/NullContext.java b/engine/src/core/com/jme3/system/NullContext.java
new file mode 100644
index 0000000..75fdf2b
--- /dev/null
+++ b/engine/src/core/com/jme3/system/NullContext.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
+import com.jme3.renderer.Renderer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class NullContext implements JmeContext, Runnable {
+
+ protected static final Logger logger = Logger.getLogger(NullContext.class.getName());
+
+ protected AtomicBoolean created = new AtomicBoolean(false);
+ protected AtomicBoolean needClose = new AtomicBoolean(false);
+ protected final Object createdLock = new Object();
+
+ protected int frameRate;
+ protected AppSettings settings = new AppSettings(true);
+ protected Timer timer;
+ protected SystemListener listener;
+ protected NullRenderer renderer;
+
+ public Type getType() {
+ return Type.Headless;
+ }
+
+ public void setSystemListener(SystemListener listener){
+ this.listener = listener;
+ }
+
+ protected void initInThread(){
+ logger.info("NullContext created.");
+ logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
+
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+ }
+ });
+
+ timer = new NanoTimer();
+ renderer = new NullRenderer();
+ synchronized (createdLock){
+ created.set(true);
+ createdLock.notifyAll();
+ }
+
+ listener.initialize();
+ }
+
+ protected void deinitInThread(){
+ listener.destroy();
+ timer = null;
+ synchronized (createdLock){
+ created.set(false);
+ createdLock.notifyAll();
+ }
+ }
+
+ private static long timeThen;
+ private static long timeLate;
+
+ public void sync(int fps) {
+ long timeNow;
+ long gapTo;
+ long savedTimeLate;
+
+ gapTo = timer.getResolution() / fps + timeThen;
+ timeNow = timer.getTime();
+ savedTimeLate = timeLate;
+
+ try {
+ while (gapTo > timeNow + savedTimeLate) {
+ Thread.sleep(1);
+ timeNow = timer.getTime();
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ if (gapTo < timeNow) {
+ timeLate = timeNow - gapTo;
+ } else {
+ timeLate = 0;
+ }
+
+ timeThen = timeNow;
+ }
+
+ public void run(){
+ initInThread();
+
+ while (!needClose.get()){
+ listener.update();
+
+ if (frameRate > 0)
+ sync(frameRate);
+ }
+
+ deinitInThread();
+
+ logger.info("NullContext destroyed.");
+ }
+
+ public void destroy(boolean waitFor){
+ needClose.set(true);
+ if (waitFor)
+ waitFor(false);
+ }
+
+ public void create(boolean waitFor){
+ if (created.get()){
+ logger.warning("create() called when NullContext is already created!");
+ return;
+ }
+
+ new Thread(this, "Headless Application Thread").start();
+ if (waitFor)
+ waitFor(true);
+ }
+
+ public void restart() {
+ }
+
+ public void setAutoFlushFrames(boolean enabled){
+ }
+
+ public MouseInput getMouseInput() {
+ return new DummyMouseInput();
+ }
+
+ public KeyInput getKeyInput() {
+ return new DummyKeyInput();
+ }
+
+ public JoyInput getJoyInput() {
+ return null;
+ }
+
+ public TouchInput getTouchInput() {
+ return null;
+ }
+
+ public void setTitle(String title) {
+ }
+
+ public void create(){
+ create(false);
+ }
+
+ public void destroy(){
+ destroy(false);
+ }
+
+ protected void waitFor(boolean createdVal){
+ synchronized (createdLock){
+ while (created.get() != createdVal){
+ try {
+ createdLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+
+ public boolean isCreated(){
+ return created.get();
+ }
+
+ public void setSettings(AppSettings settings) {
+ this.settings.copyFrom(settings);
+ frameRate = settings.getFrameRate();
+ if (frameRate <= 0)
+ frameRate = 60; // use default update rate.
+ }
+
+ public AppSettings getSettings(){
+ return settings;
+ }
+
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ public Timer getTimer() {
+ return timer;
+ }
+
+ public boolean isRenderable() {
+ return true; // Doesn't really matter if true or false. Either way
+ // RenderManager won't render anything.
+ }
+}
diff --git a/engine/src/core/com/jme3/system/NullRenderer.java b/engine/src/core/com/jme3/system/NullRenderer.java
new file mode 100644
index 0000000..2b1d821
--- /dev/null
+++ b/engine/src/core/com/jme3/system/NullRenderer.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system;
+
+import com.jme3.light.LightList;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.Statistics;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import java.nio.ByteBuffer;
+import java.util.EnumSet;
+
+public class NullRenderer implements Renderer {
+
+ private static final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
+ private static final Statistics stats = new Statistics();
+
+ public EnumSet<Caps> getCaps() {
+ return caps;
+ }
+
+ public Statistics getStatistics() {
+ return stats;
+ }
+
+ public void invalidateState(){
+ }
+
+ public void clearBuffers(boolean color, boolean depth, boolean stencil) {
+ }
+
+ public void setBackgroundColor(ColorRGBA color) {
+ }
+
+ public void applyRenderState(RenderState state) {
+ }
+
+ public void setDepthRange(float start, float end) {
+ }
+
+ public void onFrame() {
+ }
+
+ public void setWorldMatrix(Matrix4f worldMatrix) {
+ }
+
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
+ }
+
+ public void setViewPort(int x, int y, int width, int height) {
+ }
+
+ public void setClipRect(int x, int y, int width, int height) {
+ }
+
+ public void clearClipRect() {
+ }
+
+ public void setLighting(LightList lights) {
+ }
+
+ public void setShader(Shader shader) {
+ }
+
+ public void deleteShader(Shader shader) {
+ }
+
+ public void deleteShaderSource(ShaderSource source) {
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
+ }
+
+ public void setMainFrameBufferOverride(FrameBuffer fb) {
+ }
+
+ public void setFrameBuffer(FrameBuffer fb) {
+ }
+
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+ }
+
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ }
+
+ public void setTexture(int unit, Texture tex) {
+ }
+
+ public void updateBufferData(VertexBuffer vb) {
+ }
+
+ public void deleteBuffer(VertexBuffer vb) {
+ }
+
+ public void renderMesh(Mesh mesh, int lod, int count) {
+ }
+
+ public void resetGLObjects() {
+ }
+
+ public void cleanup() {
+ }
+
+ public void deleteImage(Image image) {
+ }
+
+ public void setAlphaToCoverage(boolean value) {
+ }
+
+}
diff --git a/engine/src/core/com/jme3/system/Platform.java b/engine/src/core/com/jme3/system/Platform.java
new file mode 100644
index 0000000..5a3f5c9
--- /dev/null
+++ b/engine/src/core/com/jme3/system/Platform.java
@@ -0,0 +1,65 @@
+package com.jme3.system;
+
+public enum Platform {
+
+ /**
+ * Microsoft Windows 32 bit
+ */
+ Windows32,
+
+ /**
+ * Microsoft Windows 64 bit
+ */
+ Windows64,
+
+ /**
+ * Linux 32 bit
+ */
+ Linux32,
+
+ /**
+ * Linux 64 bit
+ */
+ Linux64,
+
+ /**
+ * Apple Mac OS X 32 bit
+ */
+ MacOSX32,
+
+ /**
+ * Apple Mac OS X 64 bit
+ */
+ MacOSX64,
+
+ /**
+ * Apple Mac OS X 32 bit PowerPC
+ */
+ MacOSX_PPC32,
+
+ /**
+ * Apple Mac OS X 64 bit PowerPC
+ */
+ MacOSX_PPC64,
+
+ /**
+ * Android ARM5
+ */
+ Android_ARM5,
+
+ /**
+ * Android ARM6
+ */
+ Android_ARM6,
+
+ /**
+ * Android ARM7
+ */
+ Android_ARM7,
+
+ /**
+ * Android x86
+ */
+ Android_X86;
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/system/SystemListener.java b/engine/src/core/com/jme3/system/SystemListener.java
new file mode 100644
index 0000000..e1ccf57
--- /dev/null
+++ b/engine/src/core/com/jme3/system/SystemListener.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system;
+
+/**
+ * The <code>ContextListener> provides a means for an application
+ * to receive events relating to a context.
+ */
+public interface SystemListener {
+
+ /**
+ * Callback to indicate the application to initialize. This method
+ * is called in the GL/Rendering thread so any GL-dependent resources
+ * can be initialized.
+ */
+ public void initialize();
+
+ /**
+ * Called to notify the application that the resolution has changed.
+ * @param width
+ * @param height
+ */
+ public void reshape(int width, int height);
+
+ /**
+ * Callback to update the application state, and render the scene
+ * to the back buffer.
+ */
+ public void update();
+
+ /**
+ * Called when the user requests to close the application. This
+ * could happen when he clicks the X button on the window, presses
+ * the Alt-F4 combination, attempts to shutdown the process from
+ * the task manager, or presses ESC.
+ * @param esc If true, the user pressed ESC to close the application.
+ */
+ public void requestClose(boolean esc);
+
+ /**
+ * Called when the application gained focus. The display
+ * implementation is not allowed to call this method before
+ * initialize() has been called or after destroy() has been called.
+ */
+ public void gainFocus();
+
+ /**
+ * Called when the application lost focus. The display
+ * implementation is not allowed to call this method before
+ * initialize() has been called or after destroy() has been called.
+ */
+ public void loseFocus();
+
+ /**
+ * Called when an error has occured. This is typically
+ * invoked when an uncought exception is thrown in the render thread.
+ * @param errorMsg The error message, if any, or null.
+ * @param t Throwable object, or null.
+ */
+ public void handleError(String errorMsg, Throwable t);
+
+ /**
+ * Callback to indicate that the context has been destroyed (either
+ * by the user or requested by the application itself). Typically
+ * cleanup of native resources should happen here. This method is called
+ * in the GL/Rendering thread.
+ */
+ public void destroy();
+}
diff --git a/engine/src/core/com/jme3/system/Timer.java b/engine/src/core/com/jme3/system/Timer.java
new file mode 100644
index 0000000..3ce1ee8
--- /dev/null
+++ b/engine/src/core/com/jme3/system/Timer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+/**
+ * <code>Timer</code> is the base class for a high resolution timer. It is
+ * created from getTimer("display system")
+ *
+ * @author Mark Powell
+ * @version $Id: Timer.java,v 1.18 2007/03/09 10:19:34 rherlitz Exp $
+ */
+public abstract class Timer {
+
+ /**
+ * Returns the current time in ticks. A tick is an arbitrary measure of time
+ * defined by the timer implementation. The number of ticks per second is
+ * given by <code>getResolution()</code>. The timer starts at 0 ticks.
+ *
+ * @return a long value representing the current time
+ */
+ public abstract long getTime();
+
+ /**
+ * Returns the time in seconds. The timer starts
+ * at 0.0 seconds.
+ *
+ * @return the current time in seconds
+ */
+ public float getTimeInSeconds() {
+ return getTime() / (float) getResolution();
+ }
+
+ /**
+ * Returns the resolution of the timer.
+ *
+ * @return the number of timer ticks per second
+ */
+ public abstract long getResolution();
+
+ /**
+ * Returns the "calls per second". If this is called every frame, then it
+ * will return the "frames per second".
+ *
+ * @return The "calls per second".
+ */
+ public abstract float getFrameRate();
+
+ /**
+ * Returns the time, in seconds, between the last call and the current one.
+ *
+ * @return Time between this call and the last one.
+ */
+ public abstract float getTimePerFrame();
+
+ /**
+ * <code>update</code> recalculates the frame rate based on the previous
+ * call to update. It is assumed that update is called each frame.
+ */
+ public abstract void update();
+
+ /**
+ * Reset the timer to 0. Clear any tpf history.
+ */
+ public abstract void reset();
+}
diff --git a/engine/src/core/com/jme3/texture/FrameBuffer.java b/engine/src/core/com/jme3/texture/FrameBuffer.java
new file mode 100644
index 0000000..b52927f
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/FrameBuffer.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture;
+
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.Renderer;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.NativeObject;
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * <code>FrameBuffer</code>s are rendering surfaces allowing
+ * off-screen rendering and render-to-texture functionality.
+ * Instead of the scene rendering to the screen, it is rendered into the
+ * FrameBuffer, the result can be either a texture or a buffer.
+ * <p>
+ * A <code>FrameBuffer</code> supports two methods of rendering,
+ * using a {@link Texture} or using a buffer.
+ * When using a texture, the result of the rendering will be rendered
+ * onto the texture, after which the texture can be placed on an object
+ * and rendered as if the texture was uploaded from disk.
+ * When using a buffer, the result is rendered onto
+ * a buffer located on the GPU, the data of this buffer is not accessible
+ * to the user. buffers are useful if one
+ * wishes to retrieve only the color content of the scene, but still desires
+ * depth testing (which requires a depth buffer).
+ * Buffers can be copied to other framebuffers
+ * including the main screen, by using
+ * {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, com.jme3.texture.FrameBuffer) }.
+ * The content of a {@link RenderBuffer} can be retrieved by using
+ * {@link Renderer#readFrameBuffer(com.jme3.texture.FrameBuffer, java.nio.ByteBuffer) }.
+ * <p>
+ * <code>FrameBuffer</code>s have several attachment points, there are
+ * several <em>color</em> attachment points and a single <em>depth</em>
+ * attachment point.
+ * The color attachment points support image formats such as
+ * {@link Format#RGBA8}, allowing rendering the color content of the scene.
+ * The depth attachment point requires a depth image format.
+ *
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ *
+ * @author Kirill Vainer
+ */
+public class FrameBuffer extends NativeObject {
+
+ private int width = 0;
+ private int height = 0;
+ private int samples = 1;
+ private ArrayList<RenderBuffer> colorBufs = new ArrayList<RenderBuffer>();
+ private RenderBuffer depthBuf = null;
+ private int colorBufIndex = 0;
+
+ /**
+ * <code>RenderBuffer</code> represents either a texture or a
+ * buffer that will be rendered to. <code>RenderBuffer</code>s
+ * are attached to an attachment slot on a <code>FrameBuffer</code>.
+ */
+ public class RenderBuffer {
+
+ Texture tex;
+ Image.Format format;
+ int id = -1;
+ int slot = -1;
+
+ /**
+ * @return The image format of the render buffer.
+ */
+ public Format getFormat() {
+ return format;
+ }
+
+ /**
+ * @return The texture to render to for this <code>RenderBuffer</code>
+ * or null if content should be rendered into a buffer.
+ */
+ public Texture getTexture(){
+ return tex;
+ }
+
+ /**
+ * Do not use.
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * Do not use.
+ */
+ public void setId(int id){
+ this.id = id;
+ }
+
+ /**
+ * Do not use.
+ */
+ public int getSlot() {
+ return slot;
+ }
+
+ public void resetObject(){
+ id = -1;
+ }
+
+ public RenderBuffer createDestructableClone(){
+ if (tex != null){
+ return null;
+ }else{
+ RenderBuffer destructClone = new RenderBuffer();
+ destructClone.id = id;
+ return destructClone;
+ }
+ }
+
+ @Override
+ public String toString(){
+ if (tex != null){
+ return "TextureTarget[format=" + format + "]";
+ }else{
+ return "BufferTarget[format=" + format + "]";
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Creates a new FrameBuffer with the given width, height, and number
+ * of samples. If any textures are attached to this FrameBuffer, then
+ * they must have the same number of samples as given in this constructor.
+ * <p>
+ * Note that if the {@link Renderer} does not expose the
+ * {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown
+ * if the width and height arguments are not power of two.
+ *
+ * @param width The width to use
+ * @param height The height to use
+ * @param samples The number of samples to use for a multisampled
+ * framebuffer, or 1 if the framebuffer should be singlesampled.
+ *
+ * @throws IllegalArgumentException If width or height are not positive.
+ */
+ public FrameBuffer(int width, int height, int samples){
+ super(FrameBuffer.class);
+ if (width <= 0 || height <= 0)
+ throw new IllegalArgumentException("FrameBuffer must have valid size.");
+
+ this.width = width;
+ this.height = height;
+ this.samples = samples == 0 ? 1 : samples;
+ }
+
+ protected FrameBuffer(FrameBuffer src){
+ super(FrameBuffer.class, src.id);
+ /*
+ for (RenderBuffer renderBuf : src.colorBufs){
+ RenderBuffer clone = renderBuf.createDestructableClone();
+ if (clone != null)
+ this.colorBufs.add(clone);
+ }
+
+ this.depthBuf = src.depthBuf.createDestructableClone();
+ */
+ }
+
+ /**
+ * Enables the use of a depth buffer for this <code>FrameBuffer</code>.
+ *
+ * @param format The format to use for the depth buffer.
+ * @throws IllegalArgumentException If <code>format</code> is not a depth format.
+ */
+ public void setDepthBuffer(Image.Format format){
+ if (id != -1)
+ throw new UnsupportedOperationException("FrameBuffer already initialized.");
+
+ if (!format.isDepthFormat())
+ throw new IllegalArgumentException("Depth buffer format must be depth.");
+
+ depthBuf = new RenderBuffer();
+ depthBuf.slot = -100; // -100 == special slot for DEPTH_BUFFER
+ depthBuf.format = format;
+ }
+
+ /**
+ * Enables the use of a color buffer for this <code>FrameBuffer</code>.
+ *
+ * @param format The format to use for the color buffer.
+ * @throws IllegalArgumentException If <code>format</code> is not a color format.
+ */
+ public void setColorBuffer(Image.Format format){
+ if (id != -1)
+ throw new UnsupportedOperationException("FrameBuffer already initialized.");
+
+ if (format.isDepthFormat())
+ throw new IllegalArgumentException("Color buffer format must be color/luminance.");
+
+ RenderBuffer colorBuf = new RenderBuffer();
+ colorBuf.slot = 0;
+ colorBuf.format = format;
+
+ colorBufs.clear();
+ colorBufs.add(colorBuf);
+ }
+
+ private void checkSetTexture(Texture tex, boolean depth){
+ Image img = tex.getImage();
+ if (img == null)
+ throw new IllegalArgumentException("Texture not initialized with RTT.");
+
+ if (depth && !img.getFormat().isDepthFormat())
+ throw new IllegalArgumentException("Texture image format must be depth.");
+ else if (!depth && img.getFormat().isDepthFormat())
+ throw new IllegalArgumentException("Texture image format must be color/luminance.");
+
+ // check that resolution matches texture resolution
+ if (width != img.getWidth() || height != img.getHeight())
+ throw new IllegalArgumentException("Texture image resolution " +
+ "must match FB resolution");
+
+ if (samples != tex.getImage().getMultiSamples())
+ throw new IllegalStateException("Texture samples must match framebuffer samples");
+ }
+
+ /**
+ * If enabled, any shaders rendering into this <code>FrameBuffer</code>
+ * will be able to write several results into the renderbuffers
+ * by using the <code>gl_FragData</code> array. Every slot in that
+ * array maps into a color buffer attached to this framebuffer.
+ *
+ * @param enabled True to enable MRT (multiple rendering targets).
+ */
+ public void setMultiTarget(boolean enabled){
+ if (enabled) colorBufIndex = -1;
+ else colorBufIndex = 0;
+ }
+
+ /**
+ * @return True if MRT (multiple rendering targets) is enabled.
+ * @see FrameBuffer#setMultiTarget(boolean)
+ */
+ public boolean isMultiTarget(){
+ return colorBufIndex == -1;
+ }
+
+ /**
+ * If MRT is not enabled ({@link FrameBuffer#setMultiTarget(boolean) } is false)
+ * then this specifies the color target to which the scene should be rendered.
+ * <p>
+ * By default the value is 0.
+ *
+ * @param index The color attachment index.
+ * @throws IllegalArgumentException If index is negative or doesn't map
+ * to any attachment on this framebuffer.
+ */
+ public void setTargetIndex(int index){
+ if (index < 0 || index >= 16)
+ throw new IllegalArgumentException("Target index must be between 0 and 16");
+
+ if (colorBufs.size() < index)
+ throw new IllegalArgumentException("The target at " + index + " is not set!");
+
+ colorBufIndex = index;
+ setUpdateNeeded();
+ }
+
+ /**
+ * @return The color target to which the scene should be rendered.
+ *
+ * @see FrameBuffer#setTargetIndex(int)
+ */
+ public int getTargetIndex(){
+ return colorBufIndex;
+ }
+
+ /**
+ * Set the color texture to use for this framebuffer.
+ * This automatically clears all existing textures added previously
+ * with {@link FrameBuffer#addColorTexture(com.jme3.texture.Texture2D) }
+ * and adds this texture as the only target.
+ *
+ * @param tex The color texture to set.
+ */
+ public void setColorTexture(Texture2D tex){
+ clearColorTargets();
+ addColorTexture(tex);
+ }
+
+ /**
+ * Clears all color targets that were set or added previously.
+ */
+ public void clearColorTargets(){
+ colorBufs.clear();
+ }
+
+ /**
+ * Add a color texture to use for this framebuffer.
+ * If MRT is enabled, then each subsequently added texture can be
+ * rendered to through a shader that writes to the array <code>gl_FragData</code>.
+ * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) }
+ * is rendered to by the shader.
+ *
+ * @param tex The texture to add.
+ */
+ public void addColorTexture(Texture2D tex) {
+ if (id != -1)
+ throw new UnsupportedOperationException("FrameBuffer already initialized.");
+
+ Image img = tex.getImage();
+ checkSetTexture(tex, false);
+
+ RenderBuffer colorBuf = new RenderBuffer();
+ colorBuf.slot = colorBufs.size();
+ colorBuf.tex = tex;
+ colorBuf.format = img.getFormat();
+
+ colorBufs.add(colorBuf);
+ }
+
+ /**
+ * Set the depth texture to use for this framebuffer.
+ *
+ * @param tex The color texture to set.
+ */
+ public void setDepthTexture(Texture2D tex){
+ if (id != -1)
+ throw new UnsupportedOperationException("FrameBuffer already initialized.");
+
+ Image img = tex.getImage();
+ checkSetTexture(tex, true);
+
+ depthBuf = new RenderBuffer();
+ depthBuf.slot = -100; // indicates GL_DEPTH_ATTACHMENT
+ depthBuf.tex = tex;
+ depthBuf.format = img.getFormat();
+ }
+
+ /**
+ * @return The number of color buffers attached to this texture.
+ */
+ public int getNumColorBuffers(){
+ return colorBufs.size();
+ }
+
+ /**
+ * @param index
+ * @return The color buffer at the given index.
+ */
+ public RenderBuffer getColorBuffer(int index){
+ return colorBufs.get(index);
+ }
+
+ /**
+ * @return The first color buffer attached to this FrameBuffer, or null
+ * if no color buffers are attached.
+ */
+ public RenderBuffer getColorBuffer() {
+ if (colorBufs.isEmpty())
+ return null;
+
+ return colorBufs.get(0);
+ }
+
+ /**
+ * @return The depth buffer attached to this FrameBuffer, or null
+ * if no depth buffer is attached
+ */
+ public RenderBuffer getDepthBuffer() {
+ return depthBuf;
+ }
+
+ /**
+ * @return The height in pixels of this framebuffer.
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * @return The width in pixels of this framebuffer.
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return The number of samples when using a multisample framebuffer, or
+ * 1 if this is a singlesampled framebuffer.
+ */
+ public int getSamples() {
+ return samples;
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ String mrtStr = colorBufIndex >= 0 ? "" + colorBufIndex : "mrt";
+ sb.append("FrameBuffer[format=").append(width).append("x").append(height)
+ .append("x").append(samples).append(", drawBuf=").append(mrtStr).append("]\n");
+ if (depthBuf != null)
+ sb.append("Depth => ").append(depthBuf).append("\n");
+ for (RenderBuffer colorBuf : colorBufs){
+ sb.append("Color(").append(colorBuf.slot)
+ .append(") => ").append(colorBuf).append("\n");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void resetObject() {
+ this.id = -1;
+
+ for (int i = 0; i < colorBufs.size(); i++) {
+ colorBufs.get(i).resetObject();
+ }
+
+ if (depthBuf != null)
+ depthBuf.resetObject();
+
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((Renderer)rendererObject).deleteFrameBuffer(this);
+ }
+
+ public NativeObject createDestructableClone(){
+ return new FrameBuffer(this);
+ }
+}
diff --git a/engine/src/core/com/jme3/texture/Image.java b/engine/src/core/com/jme3/texture/Image.java
new file mode 100644
index 0000000..5d23522
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/Image.java
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture;
+
+import com.jme3.export.*;
+import com.jme3.renderer.Renderer;
+import com.jme3.util.NativeObject;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <code>Image</code> defines a data format for a graphical image. The image
+ * is defined by a format, a height and width, and the image data. The width and
+ * height must be greater than 0. The data is contained in a byte buffer, and
+ * should be packed before creation of the image object.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ * @version $Id: Image.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ */
+public class Image extends NativeObject implements Savable /*, Cloneable*/ {
+
+ public enum Format {
+ /**
+ * 8-bit alpha
+ */
+ Alpha8(8),
+
+ /**
+ * 16-bit alpha
+ */
+ Alpha16(16),
+
+ /**
+ * 8-bit grayscale/luminance.
+ */
+ Luminance8(8),
+
+ /**
+ * 16-bit grayscale/luminance.
+ */
+ Luminance16(16),
+
+ /**
+ * half-precision floating-point grayscale/luminance.
+ */
+ Luminance16F(16,true),
+
+ /**
+ * single-precision floating-point grayscale/luminance.
+ */
+ Luminance32F(32,true),
+
+ /**
+ * 8-bit luminance/grayscale and 8-bit alpha.
+ */
+ Luminance8Alpha8(16),
+
+ /**
+ * 16-bit luminance/grayscale and 16-bit alpha.
+ */
+ Luminance16Alpha16(32),
+
+ /**
+ * half-precision floating-point grayscale/luminance and alpha.
+ */
+ Luminance16FAlpha16F(32,true),
+
+ Intensity8(8),
+ Intensity16(16),
+
+ /**
+ * 8-bit blue, green, and red.
+ */
+ BGR8(24), // BGR and ABGR formats are often used on windows systems
+
+ /**
+ * 8-bit red, green, and blue.
+ */
+ RGB8(24),
+
+ /**
+ * 10-bit red, green, and blue.
+ */
+ RGB10(30),
+
+ /**
+ * 16-bit red, green, and blue.
+ */
+ RGB16(48),
+
+ /**
+ * 5-bit red, 6-bit green, and 5-bit blue.
+ */
+ RGB565(16),
+
+ /**
+ * 4-bit alpha, red, green, and blue. Used on Android only.
+ */
+ ARGB4444(16),
+
+ /**
+ * 5-bit red, green, and blue with 1-bit alpha.
+ */
+ RGB5A1(16),
+
+ /**
+ * 8-bit red, green, blue, and alpha.
+ */
+ RGBA8(32),
+
+ /**
+ * 8-bit alpha, blue, green, and red.
+ */
+ ABGR8(32),
+
+ /**
+ * 16-bit red, green, blue and alpha
+ */
+ RGBA16(64),
+
+ /**
+ * S3TC compression DXT1.
+ * Called BC1 in DirectX10.
+ */
+ DXT1(4,false,true, false),
+
+ /**
+ * S3TC compression DXT1 with 1-bit alpha.
+ */
+ DXT1A(4,false,true, false),
+
+ /**
+ * S3TC compression DXT3 with 4-bit alpha.
+ * Called BC2 in DirectX10.
+ */
+ DXT3(8,false,true, false),
+
+ /**
+ * S3TC compression DXT5 with interpolated 8-bit alpha.
+ * Called BC3 in DirectX10.
+ */
+ DXT5(8,false,true, false),
+
+ /**
+ * Luminance-Alpha Texture Compression.
+ * Called BC5 in DirectX10.
+ */
+ LATC(8, false, true, false),
+
+ /**
+ * Arbitrary depth format. The precision is chosen by the video
+ * hardware.
+ */
+ Depth(0,true,false,false),
+
+ /**
+ * 16-bit depth.
+ */
+ Depth16(16,true,false,false),
+
+ /**
+ * 24-bit depth.
+ */
+ Depth24(24,true,false,false),
+
+ /**
+ * 32-bit depth.
+ */
+ Depth32(32,true,false,false),
+
+ /**
+ * single-precision floating point depth.
+ */
+ Depth32F(32,true,false,true),
+
+ /**
+ * Texture data is stored as {@link Format#RGB16F} in system memory,
+ * but will be converted to {@link Format#RGB111110F} when sent
+ * to the video hardware.
+ */
+ RGB16F_to_RGB111110F(48,true),
+
+ /**
+ * unsigned floating-point red, green and blue that uses 32 bits.
+ */
+ RGB111110F(32,true),
+
+ /**
+ * Texture data is stored as {@link Format#RGB16F} in system memory,
+ * but will be converted to {@link Format#RGB9E5} when sent
+ * to the video hardware.
+ */
+ RGB16F_to_RGB9E5(48,true),
+
+ /**
+ * 9-bit red, green and blue with 5-bit exponent.
+ */
+ RGB9E5(32,true),
+
+ /**
+ * half-precision floating point red, green, and blue.
+ */
+ RGB16F(48,true),
+
+ /**
+ * half-precision floating point red, green, blue, and alpha.
+ */
+ RGBA16F(64,true),
+
+ /**
+ * single-precision floating point red, green, and blue.
+ */
+ RGB32F(96,true),
+
+ /**
+ * single-precision floating point red, green, blue and alpha.
+ */
+ RGBA32F(128,true),
+
+ /**
+ * Luminance/grayscale texture compression.
+ * Called BC4 in DirectX10.
+ */
+ LTC(4, false, true, false);
+
+ private int bpp;
+ private boolean isDepth;
+ private boolean isCompressed;
+ private boolean isFloatingPoint;
+
+ private Format(int bpp){
+ this.bpp = bpp;
+ }
+
+ private Format(int bpp, boolean isFP){
+ this(bpp);
+ this.isFloatingPoint = isFP;
+ }
+
+ private Format(int bpp, boolean isDepth, boolean isCompressed, boolean isFP){
+ this(bpp, isFP);
+ this.isDepth = isDepth;
+ this.isCompressed = isCompressed;
+ }
+
+ /**
+ * @return bits per pixel.
+ */
+ public int getBitsPerPixel(){
+ return bpp;
+ }
+
+ /**
+ * @return True if this format is a depth format, false otherwise.
+ */
+ public boolean isDepthFormat(){
+ return isDepth;
+ }
+
+ /**
+ * @return True if this is a compressed image format, false if
+ * uncompressed.
+ */
+ public boolean isCompressed() {
+ return isCompressed;
+ }
+
+ /**
+ * @return True if this image format is in floating point,
+ * false if it is an integer format.
+ */
+ public boolean isFloatingPont(){
+ return isFloatingPoint;
+ }
+
+ }
+
+ // image attributes
+ protected Format format;
+ protected int width, height, depth;
+ protected int[] mipMapSizes;
+ protected ArrayList<ByteBuffer> data;
+ protected transient Object efficientData;
+ protected int multiSamples = 1;
+// protected int mipOffset = 0;
+
+ @Override
+ public void resetObject() {
+ this.id = -1;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((Renderer)rendererObject).deleteImage(this);
+ }
+
+ @Override
+ public NativeObject createDestructableClone() {
+ return new Image(id);
+ }
+
+ /**
+ * @return A shallow clone of this image. The data is not cloned.
+ */
+ @Override
+ public Image clone(){
+ Image clone = (Image) super.clone();
+ clone.mipMapSizes = mipMapSizes != null ? mipMapSizes.clone() : null;
+ clone.data = data != null ? new ArrayList<ByteBuffer>(data) : null;
+ clone.setUpdateNeeded();
+ return clone;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Image</code> object. All values
+ * are undefined.
+ */
+ public Image() {
+ super(Image.class);
+ data = new ArrayList<ByteBuffer>(1);
+ }
+
+ protected Image(int id){
+ super(Image.class, id);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Image</code> object. The
+ * attributes of the image are defined during construction.
+ *
+ * @param format
+ * the data format of the image.
+ * @param width
+ * the width of the image.
+ * @param height
+ * the height of the image.
+ * @param data
+ * the image data.
+ * @param mipMapSizes
+ * the array of mipmap sizes, or null for no mipmaps.
+ */
+ public Image(Format format, int width, int height, int depth, ArrayList<ByteBuffer> data,
+ int[] mipMapSizes) {
+
+ this();
+
+ if (mipMapSizes != null && mipMapSizes.length <= 1) {
+ mipMapSizes = null;
+ }
+
+ setFormat(format);
+ this.width = width;
+ this.height = height;
+ this.data = data;
+ this.depth = depth;
+ this.mipMapSizes = mipMapSizes;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Image</code> object. The
+ * attributes of the image are defined during construction.
+ *
+ * @param format
+ * the data format of the image.
+ * @param width
+ * the width of the image.
+ * @param height
+ * the height of the image.
+ * @param data
+ * the image data.
+ * @param mipMapSizes
+ * the array of mipmap sizes, or null for no mipmaps.
+ */
+ public Image(Format format, int width, int height, ByteBuffer data,
+ int[] mipMapSizes) {
+
+ this();
+
+ if (mipMapSizes != null && mipMapSizes.length <= 1) {
+ mipMapSizes = null;
+ }
+
+ setFormat(format);
+ this.width = width;
+ this.height = height;
+ if (data != null){
+ this.data = new ArrayList<ByteBuffer>(1);
+ this.data.add(data);
+ }
+ this.mipMapSizes = mipMapSizes;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Image</code> object. The
+ * attributes of the image are defined during construction.
+ *
+ * @param format
+ * the data format of the image.
+ * @param width
+ * the width of the image.
+ * @param height
+ * the height of the image.
+ * @param data
+ * the image data.
+ */
+ public Image(Format format, int width, int height, int depth, ArrayList<ByteBuffer> data) {
+ this(format, width, height, depth, data, null);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Image</code> object. The
+ * attributes of the image are defined during construction.
+ *
+ * @param format
+ * the data format of the image.
+ * @param width
+ * the width of the image.
+ * @param height
+ * the height of the image.
+ * @param data
+ * the image data.
+ */
+ public Image(Format format, int width, int height, ByteBuffer data) {
+ this(format, width, height, data, null);
+ }
+
+ /**
+ * @return The number of samples (for multisampled textures).
+ * @see Image#setMultiSamples(int)
+ */
+ public int getMultiSamples() {
+ return multiSamples;
+ }
+
+ /**
+ * @param multiSamples Set the number of samples to use for this image,
+ * setting this to a value higher than 1 turns this image/texture
+ * into a multisample texture (on OpenGL3.1 and higher).
+ */
+ public void setMultiSamples(int multiSamples) {
+ if (multiSamples <= 0)
+ throw new IllegalArgumentException("multiSamples must be > 0");
+
+ if (getData(0) != null)
+ throw new IllegalArgumentException("Cannot upload data as multisample texture");
+
+ if (hasMipmaps())
+ throw new IllegalArgumentException("Multisample textures do not support mipmaps");
+
+ this.multiSamples = multiSamples;
+ }
+
+ /**
+ * <code>setData</code> sets the data that makes up the image. This data
+ * is packed into an array of <code>ByteBuffer</code> objects.
+ *
+ * @param data
+ * the data that contains the image information.
+ */
+ public void setData(ArrayList<ByteBuffer> data) {
+ this.data = data;
+ setUpdateNeeded();
+ }
+
+ /**
+ * <code>setData</code> sets the data that makes up the image. This data
+ * is packed into a single <code>ByteBuffer</code>.
+ *
+ * @param data
+ * the data that contains the image information.
+ */
+ public void setData(ByteBuffer data) {
+ this.data = new ArrayList<ByteBuffer>(1);
+ this.data.add(data);
+ setUpdateNeeded();
+ }
+
+ public void addData(ByteBuffer data) {
+ if (this.data == null)
+ this.data = new ArrayList<ByteBuffer>(1);
+ this.data.add(data);
+ setUpdateNeeded();
+ }
+
+ public void setData(int index, ByteBuffer data) {
+ if (index >= 0) {
+ while (this.data.size() <= index) {
+ this.data.add(null);
+ }
+ this.data.set(index, data);
+ setUpdateNeeded();
+ } else {
+ throw new IllegalArgumentException("index must be greater than or equal to 0.");
+ }
+ }
+
+ /**
+ * Set the efficient data representation of this image.
+ * <p>
+ * Some system implementations are more efficient at operating
+ * on data other than ByteBuffers, in that case, this method can be used.
+ *
+ * @param efficientData
+ */
+ public void setEfficentData(Object efficientData){
+ this.efficientData = efficientData;
+ setUpdateNeeded();
+ }
+
+ /**
+ * @return The efficient data representation of this image.
+ * @see Image#setEfficentData(java.lang.Object)
+ */
+ public Object getEfficentData(){
+ return efficientData;
+ }
+
+ /**
+ * Sets the mipmap sizes stored in this image's data buffer. Mipmaps are
+ * stored sequentially, and the first mipmap is the main image data. To
+ * specify no mipmaps, pass null and this will automatically be expanded
+ * into a single mipmap of the full
+ *
+ * @param mipMapSizes
+ * the mipmap sizes array, or null for a single image map.
+ */
+ public void setMipMapSizes(int[] mipMapSizes) {
+ if (mipMapSizes != null && mipMapSizes.length <= 1)
+ mipMapSizes = null;
+
+ this.mipMapSizes = mipMapSizes;
+ setUpdateNeeded();
+ }
+
+ /**
+ * <code>setHeight</code> sets the height value of the image. It is
+ * typically a good idea to try to keep this as a multiple of 2.
+ *
+ * @param height
+ * the height of the image.
+ */
+ public void setHeight(int height) {
+ this.height = height;
+ setUpdateNeeded();
+ }
+
+ /**
+ * <code>setDepth</code> sets the depth value of the image. It is
+ * typically a good idea to try to keep this as a multiple of 2. This is
+ * used for 3d images.
+ *
+ * @param depth
+ * the depth of the image.
+ */
+ public void setDepth(int depth) {
+ this.depth = depth;
+ setUpdateNeeded();
+ }
+
+ /**
+ * <code>setWidth</code> sets the width value of the image. It is
+ * typically a good idea to try to keep this as a multiple of 2.
+ *
+ * @param width
+ * the width of the image.
+ */
+ public void setWidth(int width) {
+ this.width = width;
+ setUpdateNeeded();
+ }
+
+ /**
+ * <code>setFormat</code> sets the image format for this image.
+ *
+ * @param format
+ * the image format.
+ * @throws NullPointerException
+ * if format is null
+ * @see Format
+ */
+ public void setFormat(Format format) {
+ if (format == null) {
+ throw new NullPointerException("format may not be null.");
+ }
+
+ this.format = format;
+ setUpdateNeeded();
+ }
+
+ /**
+ * <code>getFormat</code> returns the image format for this image.
+ *
+ * @return the image format.
+ * @see Format
+ */
+ public Format getFormat() {
+ return format;
+ }
+
+ /**
+ * <code>getWidth</code> returns the width of this image.
+ *
+ * @return the width of this image.
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * <code>getHeight</code> returns the height of this image.
+ *
+ * @return the height of this image.
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * <code>getDepth</code> returns the depth of this image (for 3d images).
+ *
+ * @return the depth of this image.
+ */
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * <code>getData</code> returns the data for this image. If the data is
+ * undefined, null will be returned.
+ *
+ * @return the data for this image.
+ */
+ public List<ByteBuffer> getData() {
+ return data;
+ }
+
+ /**
+ * <code>getData</code> returns the data for this image. If the data is
+ * undefined, null will be returned.
+ *
+ * @return the data for this image.
+ */
+ public ByteBuffer getData(int index) {
+ if (data.size() > index)
+ return data.get(index);
+ else
+ return null;
+ }
+
+ /**
+ * Returns whether the image data contains mipmaps.
+ *
+ * @return true if the image data contains mipmaps, false if not.
+ */
+ public boolean hasMipmaps() {
+ return mipMapSizes != null;
+ }
+
+ /**
+ * Returns the mipmap sizes for this image.
+ *
+ * @return the mipmap sizes for this image.
+ */
+ public int[] getMipMapSizes() {
+ return mipMapSizes;
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getSimpleName());
+ sb.append("[size=").append(width).append("x").append(height);
+
+ if (depth > 1)
+ sb.append("x").append(depth);
+
+ sb.append(", format=").append(format.name());
+
+ if (hasMipmaps())
+ sb.append(", mips");
+
+ if (getId() >= 0)
+ sb.append(", id=").append(id);
+
+ sb.append("]");
+
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof Image)) {
+ return false;
+ }
+ Image that = (Image) other;
+ if (this.getFormat() != that.getFormat())
+ return false;
+ if (this.getWidth() != that.getWidth())
+ return false;
+ if (this.getHeight() != that.getHeight())
+ return false;
+ if (this.getData() != null && !this.getData().equals(that.getData()))
+ return false;
+ if (this.getData() == null && that.getData() != null)
+ return false;
+ if (this.getMipMapSizes() != null
+ && !Arrays.equals(this.getMipMapSizes(), that.getMipMapSizes()))
+ return false;
+ if (this.getMipMapSizes() == null && that.getMipMapSizes() != null)
+ return false;
+ if (this.getMultiSamples() != that.getMultiSamples())
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 97 * hash + (this.format != null ? this.format.hashCode() : 0);
+ hash = 97 * hash + this.width;
+ hash = 97 * hash + this.height;
+ hash = 97 * hash + this.depth;
+ hash = 97 * hash + Arrays.hashCode(this.mipMapSizes);
+ hash = 97 * hash + (this.data != null ? this.data.hashCode() : 0);
+ hash = 97 * hash + this.multiSamples;
+ return hash;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(format, "format", Format.RGBA8);
+ capsule.write(width, "width", 0);
+ capsule.write(height, "height", 0);
+ capsule.write(depth, "depth", 0);
+ capsule.write(mipMapSizes, "mipMapSizes", null);
+ capsule.write(multiSamples, "multiSamples", 1);
+ capsule.writeByteBufferArrayList(data, "data", null);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ format = capsule.readEnum("format", Format.class, Format.RGBA8);
+ width = capsule.readInt("width", 0);
+ height = capsule.readInt("height", 0);
+ depth = capsule.readInt("depth", 0);
+ mipMapSizes = capsule.readIntArray("mipMapSizes", null);
+ multiSamples = capsule.readInt("multiSamples", 1);
+ data = (ArrayList<ByteBuffer>) capsule.readByteBufferArrayList("data", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/texture/Texture.java b/engine/src/core/com/jme3/texture/Texture.java
new file mode 100644
index 0000000..1efedec
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/Texture.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture;
+
+import com.jme3.asset.Asset;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
+import com.jme3.export.*;
+import com.jme3.util.PlaceholderAssets;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>Texture</code> defines a texture object to be used to display an
+ * image on a piece of geometry. The image to be displayed is defined by the
+ * <code>Image</code> class. All attributes required for texture mapping are
+ * contained within this class. This includes mipmapping if desired,
+ * magnificationFilter options, apply options and correction options. Default
+ * values are as follows: minificationFilter - NearestNeighborNoMipMaps,
+ * magnificationFilter - NearestNeighbor, wrap - EdgeClamp on S,T and R, apply -
+ * Modulate, environment - None.
+ *
+ * @see com.jme3.texture.Image
+ * @author Mark Powell
+ * @author Joshua Slack
+ * @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ */
+public abstract class Texture implements Asset, Savable, Cloneable {
+
+ public enum Type {
+
+ /**
+ * Two dimensional texture (default). A rectangle.
+ */
+ TwoDimensional,
+
+ /**
+ * An array of two dimensional textures.
+ */
+ TwoDimensionalArray,
+
+ /**
+ * Three dimensional texture. (A cube)
+ */
+ ThreeDimensional,
+
+ /**
+ * A set of 6 TwoDimensional textures arranged as faces of a cube facing
+ * inwards.
+ */
+ CubeMap;
+ }
+
+ public enum MinFilter {
+
+ /**
+ * Nearest neighbor interpolation is the fastest and crudest filtering
+ * method - it simply uses the color of the texel closest to the pixel
+ * center for the pixel color. While fast, this results in aliasing and
+ * shimmering during minification. (GL equivalent: GL_NEAREST)
+ */
+ NearestNoMipMaps(false),
+
+ /**
+ * In this method the four nearest texels to the pixel center are
+ * sampled (at texture level 0), and their colors are combined by
+ * weighted averages. Though smoother, without mipmaps it suffers the
+ * same aliasing and shimmering problems as nearest
+ * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR)
+ */
+ BilinearNoMipMaps(false),
+
+ /**
+ * Same as NearestNeighborNoMipMaps except that instead of using samples
+ * from texture level 0, the closest mipmap level is chosen based on
+ * distance. This reduces the aliasing and shimmering significantly, but
+ * does not help with blockiness. (GL equivalent:
+ * GL_NEAREST_MIPMAP_NEAREST)
+ */
+ NearestNearestMipMap(true),
+
+ /**
+ * Same as BilinearNoMipMaps except that instead of using samples from
+ * texture level 0, the closest mipmap level is chosen based on
+ * distance. By using mipmapping we avoid the aliasing and shimmering
+ * problems of BilinearNoMipMaps. (GL equivalent:
+ * GL_LINEAR_MIPMAP_NEAREST)
+ */
+ BilinearNearestMipMap(true),
+
+ /**
+ * Similar to NearestNeighborNoMipMaps except that instead of using
+ * samples from texture level 0, a sample is chosen from each of the
+ * closest (by distance) two mipmap levels. A weighted average of these
+ * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR)
+ */
+ NearestLinearMipMap(true),
+
+ /**
+ * Trilinear filtering is a remedy to a common artifact seen in
+ * mipmapped bilinearly filtered images: an abrupt and very noticeable
+ * change in quality at boundaries where the renderer switches from one
+ * mipmap level to the next. Trilinear filtering solves this by doing a
+ * texture lookup and bilinear filtering on the two closest mipmap
+ * levels (one higher and one lower quality), and then linearly
+ * interpolating the results. This results in a smooth degradation of
+ * texture quality as distance from the viewer increases, rather than a
+ * series of sudden drops. Of course, closer than Level 0 there is only
+ * one mipmap level available, and the algorithm reverts to bilinear
+ * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR)
+ */
+ Trilinear(true);
+
+ private boolean usesMipMapLevels;
+
+ private MinFilter(boolean usesMipMapLevels) {
+ this.usesMipMapLevels = usesMipMapLevels;
+ }
+
+ public boolean usesMipMapLevels() {
+ return usesMipMapLevels;
+ }
+ }
+
+ public enum MagFilter {
+
+ /**
+ * Nearest neighbor interpolation is the fastest and crudest filtering
+ * mode - it simply uses the color of the texel closest to the pixel
+ * center for the pixel color. While fast, this results in texture
+ * 'blockiness' during magnification. (GL equivalent: GL_NEAREST)
+ */
+ Nearest,
+
+ /**
+ * In this mode the four nearest texels to the pixel center are sampled
+ * (at the closest mipmap level), and their colors are combined by
+ * weighted average according to distance. This removes the 'blockiness'
+ * seen during magnification, as there is now a smooth gradient of color
+ * change from one texel to the next, instead of an abrupt jump as the
+ * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR)
+ */
+ Bilinear;
+
+ }
+
+ public enum WrapMode {
+ /**
+ * Only the fractional portion of the coordinate is considered.
+ */
+ Repeat,
+ /**
+ * Only the fractional portion of the coordinate is considered, but if
+ * the integer portion is odd, we'll use 1 - the fractional portion.
+ * (Introduced around OpenGL1.4) Falls back on Repeat if not supported.
+ */
+ MirroredRepeat,
+ /**
+ * coordinate will be clamped to [0,1]
+ */
+ Clamp,
+ /**
+ * mirrors and clamps the texture coordinate, where mirroring and
+ * clamping a value f computes:
+ * <code>mirrorClamp(f) = min(1, max(1/(2*N),
+ * abs(f)))</code> where N
+ * is the size of the one-, two-, or three-dimensional texture image in
+ * the direction of wrapping. (Introduced after OpenGL1.4) Falls back on
+ * Clamp if not supported.
+ */
+ MirrorClamp,
+ /**
+ * coordinate will be clamped to the range [-1/(2N), 1 + 1/(2N)] where N
+ * is the size of the texture in the direction of clamping. Falls back
+ * on Clamp if not supported.
+ */
+ BorderClamp,
+ /**
+ * Wrap mode MIRROR_CLAMP_TO_BORDER_EXT mirrors and clamps to border the
+ * texture coordinate, where mirroring and clamping to border a value f
+ * computes:
+ * <code>mirrorClampToBorder(f) = min(1+1/(2*N), max(1/(2*N), abs(f)))</code>
+ * where N is the size of the one-, two-, or three-dimensional texture
+ * image in the direction of wrapping." (Introduced after OpenGL1.4)
+ * Falls back on BorderClamp if not supported.
+ */
+ MirrorBorderClamp,
+ /**
+ * coordinate will be clamped to the range [1/(2N), 1 - 1/(2N)] where N
+ * is the size of the texture in the direction of clamping. Falls back
+ * on Clamp if not supported.
+ */
+ EdgeClamp,
+ /**
+ * mirrors and clamps to edge the texture coordinate, where mirroring
+ * and clamping to edge a value f computes:
+ * <code>mirrorClampToEdge(f) = min(1-1/(2*N), max(1/(2*N), abs(f)))</code>
+ * where N is the size of the one-, two-, or three-dimensional texture
+ * image in the direction of wrapping. (Introduced after OpenGL1.4)
+ * Falls back on EdgeClamp if not supported.
+ */
+ MirrorEdgeClamp;
+ }
+
+ public enum WrapAxis {
+ /**
+ * S wrapping (u or "horizontal" wrap)
+ */
+ S,
+ /**
+ * T wrapping (v or "vertical" wrap)
+ */
+ T,
+ /**
+ * R wrapping (w or "depth" wrap)
+ */
+ R;
+ }
+
+ /**
+ * If this texture is a depth texture (the format is Depth*) then
+ * this value may be used to compare the texture depth to the R texture
+ * coordinate.
+ */
+ public enum ShadowCompareMode {
+ /**
+ * Shadow comparison mode is disabled.
+ * Texturing is done normally.
+ */
+ Off,
+
+ /**
+ * Compares the 3rd texture coordinate R to the value
+ * in this depth texture. If R <= texture value then result is 1.0,
+ * otherwise, result is 0.0. If filtering is set to bilinear or trilinear
+ * the implementation may sample the texture multiple times to provide
+ * smoother results in the range [0, 1].
+ */
+ LessOrEqual,
+
+ /**
+ * Compares the 3rd texture coordinate R to the value
+ * in this depth texture. If R >= texture value then result is 1.0,
+ * otherwise, result is 0.0. If filtering is set to bilinear or trilinear
+ * the implementation may sample the texture multiple times to provide
+ * smoother results in the range [0, 1].
+ */
+ GreaterOrEqual
+ }
+
+ /**
+ * The name of the texture (if loaded as a resource).
+ */
+ private String name = null;
+
+ /**
+ * The image stored in the texture
+ */
+ private Image image = null;
+
+ /**
+ * The texture key allows to reload a texture from a file
+ * if needed.
+ */
+ private TextureKey key = null;
+
+ private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps;
+ private MagFilter magnificationFilter = MagFilter.Bilinear;
+ private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off;
+ private int anisotropicFilter;
+
+ /**
+ * @return
+ */
+ @Override
+ public Texture clone(){
+ try {
+ return (Texture) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Constructor instantiates a new <code>Texture</code> object with default
+ * attributes.
+ */
+ public Texture() {
+ }
+
+ /**
+ * @return the MinificationFilterMode of this texture.
+ */
+ public MinFilter getMinFilter() {
+ return minificationFilter;
+ }
+
+ /**
+ * @param minificationFilter
+ * the new MinificationFilterMode for this texture.
+ * @throws IllegalArgumentException
+ * if minificationFilter is null
+ */
+ public void setMinFilter(MinFilter minificationFilter) {
+ if (minificationFilter == null) {
+ throw new IllegalArgumentException(
+ "minificationFilter can not be null.");
+ }
+ this.minificationFilter = minificationFilter;
+ }
+
+ /**
+ * @return the MagnificationFilterMode of this texture.
+ */
+ public MagFilter getMagFilter() {
+ return magnificationFilter;
+ }
+
+ /**
+ * @param magnificationFilter
+ * the new MagnificationFilter for this texture.
+ * @throws IllegalArgumentException
+ * if magnificationFilter is null
+ */
+ public void setMagFilter(MagFilter magnificationFilter) {
+ if (magnificationFilter == null) {
+ throw new IllegalArgumentException(
+ "magnificationFilter can not be null.");
+ }
+ this.magnificationFilter = magnificationFilter;
+ }
+
+ /**
+ * @return The ShadowCompareMode of this texture.
+ * @see ShadowCompareMode
+ */
+ public ShadowCompareMode getShadowCompareMode(){
+ return shadowCompareMode;
+ }
+
+ /**
+ * @param compareMode
+ * the new ShadowCompareMode for this texture.
+ * @throws IllegalArgumentException
+ * if compareMode is null
+ * @see ShadowCompareMode
+ */
+ public void setShadowCompareMode(ShadowCompareMode compareMode){
+ if (compareMode == null){
+ throw new IllegalArgumentException(
+ "compareMode can not be null.");
+ }
+ this.shadowCompareMode = compareMode;
+ }
+
+ /**
+ * <code>setImage</code> sets the image object that defines the texture.
+ *
+ * @param image
+ * the image that defines the texture.
+ */
+ public void setImage(Image image) {
+ this.image = image;
+ }
+
+ /**
+ * @param key The texture key that was used to load this texture
+ */
+ public void setKey(AssetKey key){
+ this.key = (TextureKey) key;
+ }
+
+ public AssetKey getKey(){
+ return this.key;
+ }
+
+ /**
+ * <code>getImage</code> returns the image data that makes up this
+ * texture. If no image data has been set, this will return null.
+ *
+ * @return the image data that makes up the texture.
+ */
+ public Image getImage() {
+ return image;
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for a
+ * particular axis.
+ *
+ * @param axis
+ * the texture axis to define a wrapmode on.
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if axis or mode are null or invalid for this type of texture
+ */
+ public abstract void setWrap(WrapAxis axis, WrapMode mode);
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for all axis.
+ *
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if mode is null or invalid for this type of texture
+ */
+ public abstract void setWrap(WrapMode mode);
+
+ /**
+ * <code>getWrap</code> returns the wrap mode for a given coordinate axis
+ * on this texture.
+ *
+ * @param axis
+ * the axis to return for
+ * @return the wrap mode of the texture.
+ * @throws IllegalArgumentException
+ * if axis is null or invalid for this type of texture
+ */
+ public abstract WrapMode getWrap(WrapAxis axis);
+
+ public abstract Type getType();
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the anisotropic filtering level for this texture. Default value
+ * is 1 (no anisotrophy), 2 means x2, 4 is x4, etc.
+ */
+ public int getAnisotropicFilter() {
+ return anisotropicFilter;
+ }
+
+ /**
+ * @param level
+ * the anisotropic filtering level for this texture.
+ */
+ public void setAnisotropicFilter(int level) {
+ if (level < 1)
+ anisotropicFilter = 1;
+ else
+ anisotropicFilter = level;
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getSimpleName());
+ sb.append("[name=").append(name);
+ if (image != null)
+ sb.append(", image=").append(image.toString());
+
+ sb.append("]");
+
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Texture other = (Texture) obj;
+ if (this.image != other.image && (this.image == null || !this.image.equals(other.image))) {
+ return false;
+ }
+ if (this.minificationFilter != other.minificationFilter) {
+ return false;
+ }
+ if (this.magnificationFilter != other.magnificationFilter) {
+ return false;
+ }
+ if (this.shadowCompareMode != other.shadowCompareMode) {
+ return false;
+ }
+ if (this.anisotropicFilter != other.anisotropicFilter) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 67 * hash + (this.image != null ? this.image.hashCode() : 0);
+ hash = 67 * hash + (this.minificationFilter != null ? this.minificationFilter.hashCode() : 0);
+ hash = 67 * hash + (this.magnificationFilter != null ? this.magnificationFilter.hashCode() : 0);
+ hash = 67 * hash + (this.shadowCompareMode != null ? this.shadowCompareMode.hashCode() : 0);
+ hash = 67 * hash + this.anisotropicFilter;
+ return hash;
+ }
+
+
+
+// public abstract Texture createSimpleClone();
+
+
+ /** Retrieve a basic clone of this Texture (ie, clone everything but the
+ * image data, which is shared)
+ *
+ * @return Texture
+ */
+ public Texture createSimpleClone(Texture rVal) {
+ rVal.setMinFilter(minificationFilter);
+ rVal.setMagFilter(magnificationFilter);
+ rVal.setShadowCompareMode(shadowCompareMode);
+// rVal.setHasBorder(hasBorder);
+ rVal.setAnisotropicFilter(anisotropicFilter);
+ rVal.setImage(image); // NOT CLONED.
+// rVal.memReq = memReq;
+ rVal.setKey(key);
+ rVal.setName(name);
+// rVal.setBlendColor(blendColor != null ? blendColor.clone() : null);
+// if (getTextureKey() != null) {
+// rVal.setTextureKey(getTextureKey());
+// }
+ return rVal;
+ }
+
+ public abstract Texture createSimpleClone();
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(name, "name", null);
+
+ if (key == null){
+ // no texture key is set, try to save image instead then
+ capsule.write(image, "image", null);
+ }else{
+ capsule.write(key, "key", null);
+ }
+
+ capsule.write(anisotropicFilter, "anisotropicFilter", 1);
+ capsule.write(minificationFilter, "minificationFilter",
+ MinFilter.BilinearNoMipMaps);
+ capsule.write(magnificationFilter, "magnificationFilter",
+ MagFilter.Bilinear);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ name = capsule.readString("name", null);
+ key = (TextureKey) capsule.readSavable("key", null);
+
+ // load texture from key, if available
+ if (key != null) {
+ // key is available, so try the texture from there.
+ try {
+ Texture loadedTex = e.getAssetManager().loadTexture(key);
+ image = loadedTex.getImage();
+ } catch (AssetNotFoundException ex){
+ Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot locate texture {0}", key);
+ image = PlaceholderAssets.getPlaceholderImage();
+ }
+ }else{
+ // no key is set on the texture. Attempt to load an embedded image
+ image = (Image) capsule.readSavable("image", null);
+ if (image == null){
+ // TODO: what to print out here? the texture has no key or data, there's no useful information ..
+ // assume texture.name is set even though the key is null
+ Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot load embedded image {0}", toString() );
+ }
+ }
+
+ anisotropicFilter = capsule.readInt("anisotropicFilter", 1);
+ minificationFilter = capsule.readEnum("minificationFilter",
+ MinFilter.class,
+ MinFilter.BilinearNoMipMaps);
+ magnificationFilter = capsule.readEnum("magnificationFilter",
+ MagFilter.class, MagFilter.Bilinear);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/texture/Texture2D.java b/engine/src/core/com/jme3/texture/Texture2D.java
new file mode 100644
index 0000000..9598413
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/Texture2D.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * @author Joshua Slack
+ */
+public class Texture2D extends Texture {
+
+ private WrapMode wrapS = WrapMode.EdgeClamp;
+ private WrapMode wrapT = WrapMode.EdgeClamp;
+
+ /**
+ * Creates a new two-dimensional texture with default attributes.
+ */
+ public Texture2D(){
+ super();
+ }
+
+ /**
+ * Creates a new two-dimensional texture using the given image.
+ * @param img The image to use.
+ */
+ public Texture2D(Image img){
+ super();
+ setImage(img);
+ if (img.getFormat().isDepthFormat()){
+ setMagFilter(MagFilter.Nearest);
+ setMinFilter(MinFilter.NearestNoMipMaps);
+ }
+ }
+
+ /**
+ * Creates a new two-dimensional texture for the purpose of offscreen
+ * rendering.
+ *
+ * @see com.jme3.texture.FrameBuffer
+ *
+ * @param width
+ * @param height
+ * @param format
+ */
+ public Texture2D(int width, int height, Image.Format format){
+ this(new Image(format, width, height, null));
+ }
+
+ /**
+ * Creates a new two-dimensional texture for the purpose of offscreen
+ * rendering.
+ *
+ * @see com.jme3.texture.FrameBuffer
+ *
+ * @param width
+ * @param height
+ * @param format
+ * @param numSamples
+ */
+ public Texture2D(int width, int height, int numSamples, Image.Format format){
+ this(new Image(format, width, height, null));
+ getImage().setMultiSamples(numSamples);
+ }
+
+ @Override
+ public Texture createSimpleClone() {
+ Texture2D clone = new Texture2D();
+ createSimpleClone(clone);
+ return clone;
+ }
+
+ @Override
+ public Texture createSimpleClone(Texture rVal) {
+ rVal.setWrap(WrapAxis.S, wrapS);
+ rVal.setWrap(WrapAxis.T, wrapT);
+ return super.createSimpleClone(rVal);
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for a
+ * particular axis.
+ *
+ * @param axis
+ * the texture axis to define a wrapmode on.
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if axis or mode are null
+ */
+ public void setWrap(WrapAxis axis, WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ } else if (axis == null) {
+ throw new IllegalArgumentException("axis can not be null.");
+ }
+ switch (axis) {
+ case S:
+ this.wrapS = mode;
+ break;
+ case T:
+ this.wrapT = mode;
+ break;
+ default:
+ throw new IllegalArgumentException("Not applicable for 2D textures");
+ }
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for all axis.
+ *
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if mode is null
+ */
+ public void setWrap(WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ }
+ this.wrapS = mode;
+ this.wrapT = mode;
+ }
+
+ /**
+ * <code>getWrap</code> returns the wrap mode for a given coordinate axis
+ * on this texture.
+ *
+ * @param axis
+ * the axis to return for
+ * @return the wrap mode of the texture.
+ * @throws IllegalArgumentException
+ * if axis is null
+ */
+ public WrapMode getWrap(WrapAxis axis) {
+ switch (axis) {
+ case S:
+ return wrapS;
+ case T:
+ return wrapT;
+ default:
+ throw new IllegalArgumentException("invalid WrapAxis: " + axis);
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return Type.TwoDimensional;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Texture2D)) {
+ return false;
+ }
+ Texture2D that = (Texture2D) other;
+ if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S))
+ return false;
+ if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T))
+ return false;
+ return super.equals(other);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = super.hashCode();
+ hash = 79 * hash + (this.wrapS != null ? this.wrapS.hashCode() : 0);
+ hash = 79 * hash + (this.wrapT != null ? this.wrapT.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp);
+ capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp);
+ wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/texture/Texture3D.java b/engine/src/core/com/jme3/texture/Texture3D.java
new file mode 100644
index 0000000..bded644
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/Texture3D.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.texture;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * @author Maarten Steur
+ */
+public class Texture3D extends Texture {
+
+ private WrapMode wrapS = WrapMode.EdgeClamp;
+ private WrapMode wrapT = WrapMode.EdgeClamp;
+ private WrapMode wrapR = WrapMode.EdgeClamp;
+
+ /**
+ * Creates a new two-dimensional texture with default attributes.
+ */
+ public Texture3D() {
+ super();
+ }
+
+ /**
+ * Creates a new three-dimensional texture using the given image.
+ * @param img The image to use.
+ */
+ public Texture3D(Image img) {
+ super();
+ setImage(img);
+ if (img.getFormat().isDepthFormat()) {
+ setMagFilter(MagFilter.Nearest);
+ setMinFilter(MinFilter.NearestNoMipMaps);
+ }
+ }
+
+ /**
+ * Creates a new three-dimensional texture for the purpose of offscreen
+ * rendering.
+ *
+ * @see com.jme3.texture.FrameBuffer
+ *
+ * @param width
+ * @param height
+ * @param depth
+ * @param format
+ */
+ public Texture3D(int width, int height, int depth, Image.Format format) {
+ this(new Image(format, width, height, depth, null));
+ }
+
+ /**
+ * Creates a new three-dimensional texture for the purpose of offscreen
+ * rendering.
+ *
+ * @see com.jme3.texture.FrameBuffer
+ *
+ * @param width
+ * @param height
+ * @param format
+ * @param numSamples
+ */
+ public Texture3D(int width, int height, int depth, int numSamples, Image.Format format) {
+ this(new Image(format, width, height, depth, null));
+ getImage().setMultiSamples(numSamples);
+ }
+
+ @Override
+ public Texture createSimpleClone() {
+ Texture3D clone = new Texture3D();
+ createSimpleClone(clone);
+ return clone;
+ }
+
+ @Override
+ public Texture createSimpleClone(Texture rVal) {
+ rVal.setWrap(WrapAxis.S, wrapS);
+ rVal.setWrap(WrapAxis.T, wrapT);
+ rVal.setWrap(WrapAxis.R, wrapR);
+ return super.createSimpleClone(rVal);
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for a
+ * particular axis.
+ *
+ * @param axis
+ * the texture axis to define a wrapmode on.
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if axis or mode are null
+ */
+ public void setWrap(WrapAxis axis, WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ } else if (axis == null) {
+ throw new IllegalArgumentException("axis can not be null.");
+ }
+ switch (axis) {
+ case S:
+ this.wrapS = mode;
+ break;
+ case T:
+ this.wrapT = mode;
+ break;
+ case R:
+ this.wrapR = mode;
+ break;
+ }
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for all axis.
+ *
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if mode is null
+ */
+ public void setWrap(WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ }
+ this.wrapS = mode;
+ this.wrapT = mode;
+ this.wrapR = mode;
+ }
+
+ /**
+ * <code>getWrap</code> returns the wrap mode for a given coordinate axis
+ * on this texture.
+ *
+ * @param axis
+ * the axis to return for
+ * @return the wrap mode of the texture.
+ * @throws IllegalArgumentException
+ * if axis is null
+ */
+ public WrapMode getWrap(WrapAxis axis) {
+ switch (axis) {
+ case S:
+ return wrapS;
+ case T:
+ return wrapT;
+ case R:
+ return wrapR;
+ }
+ throw new IllegalArgumentException("invalid WrapAxis: " + axis);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.ThreeDimensional;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Texture3D)) {
+ return false;
+ }
+ Texture3D that = (Texture3D) other;
+ if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S)) {
+ return false;
+ }
+ if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T)) {
+ return false;
+ }
+ if (this.getWrap(WrapAxis.R) != that.getWrap(WrapAxis.R)) {
+ return false;
+ }
+ return super.equals(other);
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp);
+ capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp);
+ capsule.write(wrapR, "wrapR", WrapMode.EdgeClamp);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp);
+ wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp);
+ wrapR = capsule.readEnum("wrapR", WrapMode.class, WrapMode.EdgeClamp);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/texture/TextureArray.java b/engine/src/core/com/jme3/texture/TextureArray.java
new file mode 100644
index 0000000..e39d95f
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/TextureArray.java
@@ -0,0 +1,113 @@
+package com.jme3.texture;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class implements a Texture array
+ * warning, this feature is only supported on opengl 3.0 version.
+ * To check if a hardware supports TextureArray check :
+ * renderManager.getRenderer().getCaps().contains(Caps.TextureArray)
+ * @author phate666
+ */
+public class TextureArray extends Texture {
+
+ private WrapMode wrapS = WrapMode.EdgeClamp;
+ private WrapMode wrapT = WrapMode.EdgeClamp;
+
+ /**
+ * Construct a TextureArray
+ * warning, this feature is only supported on opengl 3.0 version.
+ * To check if a hardware supports TextureArray check :
+ * renderManager.getRenderer().getCaps().contains(Caps.TextureArray)
+ */
+ public TextureArray() {
+ super();
+ }
+
+ /**
+ * Construct a TextureArray from the given list of images
+ * warning, this feature is only supported on opengl 3.0 version.
+ * To check if a hardware supports TextureArray check :
+ * renderManager.getRenderer().getCaps().contains(Caps.TextureArray)
+ * @param images
+ */
+ public TextureArray(List<Image> images) {
+ super();
+ int width = images.get(0).getWidth();
+ int height = images.get(0).getHeight();
+ Image arrayImage = new Image(images.get(0).getFormat(), width, height,
+ null);
+
+ for (Image img : images) {
+ if (img.getHeight() != height || img.getWidth() != width) {
+ Logger.getLogger(TextureArray.class.getName()).log(
+ Level.WARNING,
+ "all images must have the same width/height");
+ continue;
+ }
+ arrayImage.addData(img.getData(0));
+ }
+ setImage(arrayImage);
+ }
+
+ @Override
+ public Texture createSimpleClone() {
+ TextureArray clone = new TextureArray();
+ createSimpleClone(clone);
+ return clone;
+ }
+
+ @Override
+ public Texture createSimpleClone(Texture rVal) {
+ rVal.setWrap(WrapAxis.S, wrapS);
+ rVal.setWrap(WrapAxis.T, wrapT);
+ return super.createSimpleClone(rVal);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.TwoDimensionalArray;
+ }
+
+ @Override
+ public WrapMode getWrap(WrapAxis axis) {
+ switch (axis) {
+ case S:
+ return wrapS;
+ case T:
+ return wrapT;
+ default:
+ throw new IllegalArgumentException("invalid WrapAxis: " + axis);
+ }
+ }
+
+ @Override
+ public void setWrap(WrapAxis axis, WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ } else if (axis == null) {
+ throw new IllegalArgumentException("axis can not be null.");
+ }
+ switch (axis) {
+ case S:
+ this.wrapS = mode;
+ break;
+ case T:
+ this.wrapT = mode;
+ break;
+ default:
+ throw new IllegalArgumentException("Not applicable for 2D textures");
+ }
+ }
+
+ @Override
+ public void setWrap(WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ }
+ this.wrapS = mode;
+ this.wrapT = mode;
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/texture/TextureCubeMap.java b/engine/src/core/com/jme3/texture/TextureCubeMap.java
new file mode 100644
index 0000000..9290d8a
--- /dev/null
+++ b/engine/src/core/com/jme3/texture/TextureCubeMap.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * Describes a cubemap texture.
+ * The image specified by setImage must contain 6 data units,
+ * each data contains a 2D image representing a cube's face.
+ * The slices are specified in this order:<br/>
+ * <br/>
+ * 0 => Positive X (+x)<br/>
+ * 1 => Negative X (-x)<br/>
+ * 2 => Positive Y (+y)<br/>
+ * 3 => Negative Y (-y)<br/>
+ * 4 => Positive Z (+z)<br/>
+ * 5 => Negative Z (-z)<br/>
+ *
+ * @author Joshua Slack
+ */
+public class TextureCubeMap extends Texture {
+
+ private WrapMode wrapS = WrapMode.EdgeClamp;
+ private WrapMode wrapT = WrapMode.EdgeClamp;
+ private WrapMode wrapR = WrapMode.EdgeClamp;
+
+ /**
+ * Face of the Cubemap as described by its directional offset from the
+ * origin.
+ */
+// public enum Face {
+// PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ;
+// }
+
+ public TextureCubeMap(){
+ super();
+ }
+
+ public TextureCubeMap(Image img){
+ super();
+ setImage(img);
+ }
+
+ public Texture createSimpleClone() {
+ return createSimpleClone(new TextureCubeMap());
+ }
+
+ @Override
+ public Texture createSimpleClone(Texture rVal) {
+ rVal.setWrap(WrapAxis.S, wrapS);
+ rVal.setWrap(WrapAxis.T, wrapT);
+ rVal.setWrap(WrapAxis.R, wrapR);
+ return super.createSimpleClone(rVal);
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for a
+ * particular axis.
+ *
+ * @param axis
+ * the texture axis to define a wrapmode on.
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if axis or mode are null
+ */
+ public void setWrap(WrapAxis axis, WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ } else if (axis == null) {
+ throw new IllegalArgumentException("axis can not be null.");
+ }
+ switch (axis) {
+ case S:
+ this.wrapS = mode;
+ break;
+ case T:
+ this.wrapT = mode;
+ break;
+ case R:
+ this.wrapR = mode;
+ break;
+ }
+ }
+
+ /**
+ * <code>setWrap</code> sets the wrap mode of this texture for all axis.
+ *
+ * @param mode
+ * the wrap mode for the given axis of the texture.
+ * @throws IllegalArgumentException
+ * if mode is null
+ */
+ public void setWrap(WrapMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode can not be null.");
+ }
+ this.wrapS = mode;
+ this.wrapT = mode;
+ this.wrapR = mode;
+ }
+
+ /**
+ * <code>getWrap</code> returns the wrap mode for a given coordinate axis
+ * on this texture.
+ *
+ * @param axis
+ * the axis to return for
+ * @return the wrap mode of the texture.
+ * @throws IllegalArgumentException
+ * if axis is null
+ */
+ public WrapMode getWrap(WrapAxis axis) {
+ switch (axis) {
+ case S:
+ return wrapS;
+ case T:
+ return wrapT;
+ case R:
+ return wrapR;
+ }
+ throw new IllegalArgumentException("invalid WrapAxis: " + axis);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.CubeMap;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof TextureCubeMap)) {
+ return false;
+ }
+ TextureCubeMap that = (TextureCubeMap) other;
+ if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S))
+ return false;
+ if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T))
+ return false;
+ if (this.getWrap(WrapAxis.R) != that.getWrap(WrapAxis.R))
+ return false;
+ return super.equals(other);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = super.hashCode();
+ hash = 53 * hash + (this.wrapS != null ? this.wrapS.hashCode() : 0);
+ hash = 53 * hash + (this.wrapT != null ? this.wrapT.hashCode() : 0);
+ hash = 53 * hash + (this.wrapR != null ? this.wrapR.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp);
+ capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp);
+ capsule.write(wrapR, "wrapR", WrapMode.EdgeClamp);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp);
+ wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp);
+ wrapR = capsule.readEnum("wrapR", WrapMode.class, WrapMode.EdgeClamp);
+ }
+}
diff --git a/engine/src/core/com/jme3/ui/Picture.java b/engine/src/core/com/jme3/ui/Picture.java
new file mode 100644
index 0000000..81371f5
--- /dev/null
+++ b/engine/src/core/com/jme3/ui/Picture.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.ui;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture2D;
+
+/**
+ * A <code>Picture</code> represents a 2D image drawn on the screen.
+ * It can be used to represent sprites or other background elements.
+ *
+ * @author Kirill Vainer
+ */
+public class Picture extends Geometry {
+
+ private float width = 1f;
+ private float height = 1f;
+
+ /**
+ * Create a named picture.
+ *
+ * By default a picture's width and height are 1
+ * and its position is 0, 0.
+ *
+ * @param name the name of the picture in the scene graph
+ * @param flipY If true, the Y coordinates of the texture will be flipped.
+ */
+ public Picture(String name, boolean flipY){
+ super(name, new Quad(1, 1, flipY));
+ setQueueBucket(Bucket.Gui);
+ setCullHint(CullHint.Never);
+ }
+
+ /**
+ * Creates a named picture.
+ * By default a picture's width and height are 1
+ * and its position is 0, 0.
+ * The image texture coordinates will not be flipped.
+ *
+ * @param name the name of the picture in the scene graph
+ */
+ public Picture(String name){
+ this(name, false);
+ }
+
+ /*
+ * Serialization only. Do not use.
+ */
+ public Picture(){
+ }
+
+ /**
+ * Set the width in pixels of the picture, if the width
+ * does not match the texture's width, then the texture will
+ * be scaled to fit the picture.
+ *
+ * @param width the width to set.
+ */
+ public void setWidth(float width){
+ this.width = width;
+ setLocalScale(new Vector3f(width, height, 1f));
+ }
+
+ /**
+ * Set the height in pixels of the picture, if the height
+ * does not match the texture's height, then the texture will
+ * be scaled to fit the picture.
+ *
+ * @param height the height to set.
+ */
+ public void setHeight(float height){
+ this.height = height;
+ setLocalScale(new Vector3f(width, height, 1f));
+ }
+
+ /**
+ * Set the position of the picture in pixels.
+ * The origin (0, 0) is at the bottom-left of the screen.
+ *
+ * @param x The x coordinate
+ * @param y The y coordinate
+ */
+ public void setPosition(float x, float y){
+ float z = getLocalTranslation().getZ();
+ setLocalTranslation(x, y, z);
+ }
+
+ /**
+ * Set the image to put on the picture.
+ *
+ * @param assetManager The {@link AssetManager} to use to load the image.
+ * @param imgName The image name.
+ * @param useAlpha If true, the picture will appear transparent and allow
+ * objects behind it to appear through. If false, the transparent
+ * portions will be the image's color at that pixel.
+ */
+ public void setImage(AssetManager assetManager, String imgName, boolean useAlpha){
+ TextureKey key = new TextureKey(imgName, true);
+ Texture2D tex = (Texture2D) assetManager.loadTexture(key);
+ setTexture(assetManager, tex, useAlpha);
+ }
+
+ /**
+ * Set the texture to put on the picture.
+ *
+ * @param assetManager The {@link AssetManager} to use to load the material.
+ * @param tex The texture
+ * @param useAlpha If true, the picture will appear transparent and allow
+ * objects behind it to appear through. If false, the transparent
+ * portions will be the image's color at that pixel.
+ */
+ public void setTexture(AssetManager assetManager, Texture2D tex, boolean useAlpha){
+ if (getMaterial() == null){
+ Material mat = new Material(assetManager, "Common/MatDefs/Gui/Gui.j3md");
+ mat.setColor("Color", ColorRGBA.White);
+ setMaterial(mat);
+ }
+ material.getAdditionalRenderState().setBlendMode(useAlpha ? BlendMode.Alpha : BlendMode.Off);
+ material.setTexture("Texture", tex);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/util/BufferUtils.java b/engine/src/core/com/jme3/util/BufferUtils.java
new file mode 100644
index 0000000..f0cc698
--- /dev/null
+++ b/engine/src/core/com/jme3/util/BufferUtils.java
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>BufferUtils</code> is a helper class for generating nio buffers from
+ * jME data classes such as Vectors and ColorRGBA.
+ *
+ * @author Joshua Slack
+ * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
+ */
+public final class BufferUtils {
+
+ private static final Map<Buffer, Object> trackingHash = Collections.synchronizedMap(new WeakHashMap<Buffer, Object>());
+ private static final Object ref = new Object();
+
+ // Note: a WeakHashMap is really bad here since the hashCode() and
+ // equals() behavior of buffers will vary based on their contents.
+ // As it stands, put()'ing an empty buffer will wipe out the last
+ // empty buffer with the same size. So any tracked memory calculations
+ // could be lying.
+ // Besides, the hashmap behavior isn't even being used here and
+ // yet the expense is still incurred. For example, a newly allocated
+ // 10,000 byte buffer will iterate through the whole buffer of 0's
+ // to calculate the hashCode and then potentially do it again to
+ // calculate the equals()... which by the way is guaranteed for
+ // every empty buffer of an existing size since they will always produce
+ // the same hashCode().
+ // It would be better to just keep a straight list of weak references
+ // and clean out the dead every time a new buffer is allocated.
+ // WeakHashMap is doing that anyway... so there is no extra expense
+ // incurred.
+ // Recommend a ConcurrentLinkedQueue of WeakReferences since it
+ // supports the threading semantics required with little extra overhead.
+ private static final boolean trackDirectMemory = false;
+
+ /**
+ * Creates a clone of the given buffer. The clone's capacity is
+ * equal to the given buffer's limit.
+ *
+ * @param buf The buffer to clone
+ * @return The cloned buffer
+ */
+ public static Buffer clone(Buffer buf) {
+ if (buf instanceof FloatBuffer) {
+ return clone((FloatBuffer) buf);
+ } else if (buf instanceof ShortBuffer) {
+ return clone((ShortBuffer) buf);
+ } else if (buf instanceof ByteBuffer) {
+ return clone((ByteBuffer) buf);
+ } else if (buf instanceof IntBuffer) {
+ return clone((IntBuffer) buf);
+ } else if (buf instanceof DoubleBuffer) {
+ return clone((DoubleBuffer) buf);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static void onBufferAllocated(Buffer buffer){
+ /*
+ StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+ int initialIndex = 0;
+
+ for (int i = 0; i < stackTrace.length; i++){
+ if (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){
+ initialIndex = i;
+ break;
+ }
+ }
+
+ int allocated = buffer.capacity();
+ int size = 0;
+
+ if (buffer instanceof FloatBuffer){
+ size = 4;
+ }else if (buffer instanceof ShortBuffer){
+ size = 2;
+ }else if (buffer instanceof ByteBuffer){
+ size = 1;
+ }else if (buffer instanceof IntBuffer){
+ size = 4;
+ }else if (buffer instanceof DoubleBuffer){
+ size = 8;
+ }
+
+ allocated *= size;
+
+ for (int i = initialIndex; i < stackTrace.length; i++){
+ StackTraceElement element = stackTrace[i];
+ if (element.getClassName().startsWith("java")){
+ break;
+ }
+
+ try {
+ Class clazz = Class.forName(element.getClassName());
+ if (i == initialIndex){
+ System.out.println(clazz.getSimpleName()+"."+element.getMethodName()+"():" + element.getLineNumber() + " allocated " + allocated);
+ }else{
+ System.out.println(" at " + clazz.getSimpleName()+"."+element.getMethodName()+"()");
+ }
+ } catch (ClassNotFoundException ex) {
+ }
+ }*/
+
+ if (trackDirectMemory){
+ trackingHash.put(buffer, ref);
+ }
+ }
+
+ /**
+ * Generate a new FloatBuffer using the given array of Vector3f objects.
+ * The FloatBuffer will be 3 * data.length long and contain the vector data
+ * as data[0].x, data[0].y, data[0].z, data[1].x... etc.
+ *
+ * @param data array of Vector3f objects to place into a new FloatBuffer
+ */
+ public static FloatBuffer createFloatBuffer(Vector3f... data) {
+ if (data == null) {
+ return null;
+ }
+ FloatBuffer buff = createFloatBuffer(3 * data.length);
+ for (int x = 0; x < data.length; x++) {
+ if (data[x] != null) {
+ buff.put(data[x].x).put(data[x].y).put(data[x].z);
+ } else {
+ buff.put(0).put(0).put(0);
+ }
+ }
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Generate a new FloatBuffer using the given array of Quaternion objects.
+ * The FloatBuffer will be 4 * data.length long and contain the vector data.
+ *
+ * @param data array of Quaternion objects to place into a new FloatBuffer
+ */
+ public static FloatBuffer createFloatBuffer(Quaternion... data) {
+ if (data == null) {
+ return null;
+ }
+ FloatBuffer buff = createFloatBuffer(4 * data.length);
+ for (int x = 0; x < data.length; x++) {
+ if (data[x] != null) {
+ buff.put(data[x].getX()).put(data[x].getY()).put(data[x].getZ()).put(data[x].getW());
+ } else {
+ buff.put(0).put(0).put(0);
+ }
+ }
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Generate a new FloatBuffer using the given array of float primitives.
+ * @param data array of float primitives to place into a new FloatBuffer
+ */
+ public static FloatBuffer createFloatBuffer(float... data) {
+ if (data == null) {
+ return null;
+ }
+ FloatBuffer buff = createFloatBuffer(data.length);
+ buff.clear();
+ buff.put(data);
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Create a new FloatBuffer of an appropriate size to hold the specified
+ * number of Vector3f object data.
+ *
+ * @param vertices
+ * number of vertices that need to be held by the newly created
+ * buffer
+ * @return the requested new FloatBuffer
+ */
+ public static FloatBuffer createVector3Buffer(int vertices) {
+ FloatBuffer vBuff = createFloatBuffer(3 * vertices);
+ return vBuff;
+ }
+
+ /**
+ * Create a new FloatBuffer of an appropriate size to hold the specified
+ * number of Vector3f object data only if the given buffer if not already
+ * the right size.
+ *
+ * @param buf
+ * the buffer to first check and rewind
+ * @param vertices
+ * number of vertices that need to be held by the newly created
+ * buffer
+ * @return the requested new FloatBuffer
+ */
+ public static FloatBuffer createVector3Buffer(FloatBuffer buf, int vertices) {
+ if (buf != null && buf.limit() == 3 * vertices) {
+ buf.rewind();
+ return buf;
+ }
+
+ return createFloatBuffer(3 * vertices);
+ }
+
+ /**
+ * Sets the data contained in the given color into the FloatBuffer at the
+ * specified index.
+ *
+ * @param color
+ * the data to insert
+ * @param buf
+ * the buffer to insert into
+ * @param index
+ * the postion to place the data; in terms of colors not floats
+ */
+ public static void setInBuffer(ColorRGBA color, FloatBuffer buf,
+ int index) {
+ buf.position(index * 4);
+ buf.put(color.r);
+ buf.put(color.g);
+ buf.put(color.b);
+ buf.put(color.a);
+ }
+
+ /**
+ * Sets the data contained in the given quaternion into the FloatBuffer at the
+ * specified index.
+ *
+ * @param quat
+ * the {@link Quaternion} to insert
+ * @param buf
+ * the buffer to insert into
+ * @param index
+ * the postion to place the data; in terms of quaternions not floats
+ */
+ public static void setInBuffer(Quaternion quat, FloatBuffer buf,
+ int index) {
+ buf.position(index * 4);
+ buf.put(quat.getX());
+ buf.put(quat.getY());
+ buf.put(quat.getZ());
+ buf.put(quat.getW());
+ }
+
+ /**
+ * Sets the data contained in the given Vector3F into the FloatBuffer at the
+ * specified index.
+ *
+ * @param vector
+ * the data to insert
+ * @param buf
+ * the buffer to insert into
+ * @param index
+ * the postion to place the data; in terms of vectors not floats
+ */
+ public static void setInBuffer(Vector3f vector, FloatBuffer buf, int index) {
+ if (buf == null) {
+ return;
+ }
+ if (vector == null) {
+ buf.put(index * 3, 0);
+ buf.put((index * 3) + 1, 0);
+ buf.put((index * 3) + 2, 0);
+ } else {
+ buf.put(index * 3, vector.x);
+ buf.put((index * 3) + 1, vector.y);
+ buf.put((index * 3) + 2, vector.z);
+ }
+ }
+
+ /**
+ * Updates the values of the given vector from the specified buffer at the
+ * index provided.
+ *
+ * @param vector
+ * the vector to set data on
+ * @param buf
+ * the buffer to read from
+ * @param index
+ * the position (in terms of vectors, not floats) to read from
+ * the buf
+ */
+ public static void populateFromBuffer(Vector3f vector, FloatBuffer buf, int index) {
+ vector.x = buf.get(index * 3);
+ vector.y = buf.get(index * 3 + 1);
+ vector.z = buf.get(index * 3 + 2);
+ }
+
+ /**
+ * Generates a Vector3f array from the given FloatBuffer.
+ *
+ * @param buff
+ * the FloatBuffer to read from
+ * @return a newly generated array of Vector3f objects
+ */
+ public static Vector3f[] getVector3Array(FloatBuffer buff) {
+ buff.clear();
+ Vector3f[] verts = new Vector3f[buff.limit() / 3];
+ for (int x = 0; x < verts.length; x++) {
+ Vector3f v = new Vector3f(buff.get(), buff.get(), buff.get());
+ verts[x] = v;
+ }
+ return verts;
+ }
+
+ /**
+ * Copies a Vector3f from one position in the buffer to another. The index
+ * values are in terms of vector number (eg, vector number 0 is postions 0-2
+ * in the FloatBuffer.)
+ *
+ * @param buf
+ * the buffer to copy from/to
+ * @param fromPos
+ * the index of the vector to copy
+ * @param toPos
+ * the index to copy the vector to
+ */
+ public static void copyInternalVector3(FloatBuffer buf, int fromPos, int toPos) {
+ copyInternal(buf, fromPos * 3, toPos * 3, 3);
+ }
+
+ /**
+ * Normalize a Vector3f in-buffer.
+ *
+ * @param buf
+ * the buffer to find the Vector3f within
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * to normalize
+ */
+ public static void normalizeVector3(FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector3f tempVec3 = vars.vect1;
+ populateFromBuffer(tempVec3, buf, index);
+ tempVec3.normalizeLocal();
+ setInBuffer(tempVec3, buf, index);
+ vars.release();
+ }
+
+ /**
+ * Add to a Vector3f in-buffer.
+ *
+ * @param toAdd
+ * the vector to add from
+ * @param buf
+ * the buffer to find the Vector3f within
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * to add to
+ */
+ public static void addInBuffer(Vector3f toAdd, FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector3f tempVec3 = vars.vect1;
+ populateFromBuffer(tempVec3, buf, index);
+ tempVec3.addLocal(toAdd);
+ setInBuffer(tempVec3, buf, index);
+ vars.release();
+ }
+
+ /**
+ * Multiply and store a Vector3f in-buffer.
+ *
+ * @param toMult
+ * the vector to multiply against
+ * @param buf
+ * the buffer to find the Vector3f within
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * to multiply
+ */
+ public static void multInBuffer(Vector3f toMult, FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector3f tempVec3 = vars.vect1;
+ populateFromBuffer(tempVec3, buf, index);
+ tempVec3.multLocal(toMult);
+ setInBuffer(tempVec3, buf, index);
+ vars.release();
+ }
+
+ /**
+ * Checks to see if the given Vector3f is equals to the data stored in the
+ * buffer at the given data index.
+ *
+ * @param check
+ * the vector to check against - null will return false.
+ * @param buf
+ * the buffer to compare data with
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * in the buffer to check against
+ * @return
+ */
+ public static boolean equals(Vector3f check, FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector3f tempVec3 = vars.vect1;
+ populateFromBuffer(tempVec3, buf, index);
+ boolean eq = tempVec3.equals(check);
+ vars.release();
+ return eq;
+ }
+
+ // // -- VECTOR2F METHODS -- ////
+ /**
+ * Generate a new FloatBuffer using the given array of Vector2f objects.
+ * The FloatBuffer will be 2 * data.length long and contain the vector data
+ * as data[0].x, data[0].y, data[1].x... etc.
+ *
+ * @param data array of Vector2f objects to place into a new FloatBuffer
+ */
+ public static FloatBuffer createFloatBuffer(Vector2f... data) {
+ if (data == null) {
+ return null;
+ }
+ FloatBuffer buff = createFloatBuffer(2 * data.length);
+ for (int x = 0; x < data.length; x++) {
+ if (data[x] != null) {
+ buff.put(data[x].x).put(data[x].y);
+ } else {
+ buff.put(0).put(0);
+ }
+ }
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Create a new FloatBuffer of an appropriate size to hold the specified
+ * number of Vector2f object data.
+ *
+ * @param vertices
+ * number of vertices that need to be held by the newly created
+ * buffer
+ * @return the requested new FloatBuffer
+ */
+ public static FloatBuffer createVector2Buffer(int vertices) {
+ FloatBuffer vBuff = createFloatBuffer(2 * vertices);
+ return vBuff;
+ }
+
+ /**
+ * Create a new FloatBuffer of an appropriate size to hold the specified
+ * number of Vector2f object data only if the given buffer if not already
+ * the right size.
+ *
+ * @param buf
+ * the buffer to first check and rewind
+ * @param vertices
+ * number of vertices that need to be held by the newly created
+ * buffer
+ * @return the requested new FloatBuffer
+ */
+ public static FloatBuffer createVector2Buffer(FloatBuffer buf, int vertices) {
+ if (buf != null && buf.limit() == 2 * vertices) {
+ buf.rewind();
+ return buf;
+ }
+
+ return createFloatBuffer(2 * vertices);
+ }
+
+ /**
+ * Sets the data contained in the given Vector2F into the FloatBuffer at the
+ * specified index.
+ *
+ * @param vector
+ * the data to insert
+ * @param buf
+ * the buffer to insert into
+ * @param index
+ * the postion to place the data; in terms of vectors not floats
+ */
+ public static void setInBuffer(Vector2f vector, FloatBuffer buf, int index) {
+ buf.put(index * 2, vector.x);
+ buf.put((index * 2) + 1, vector.y);
+ }
+
+ /**
+ * Updates the values of the given vector from the specified buffer at the
+ * index provided.
+ *
+ * @param vector
+ * the vector to set data on
+ * @param buf
+ * the buffer to read from
+ * @param index
+ * the position (in terms of vectors, not floats) to read from
+ * the buf
+ */
+ public static void populateFromBuffer(Vector2f vector, FloatBuffer buf, int index) {
+ vector.x = buf.get(index * 2);
+ vector.y = buf.get(index * 2 + 1);
+ }
+
+ /**
+ * Generates a Vector2f array from the given FloatBuffer.
+ *
+ * @param buff
+ * the FloatBuffer to read from
+ * @return a newly generated array of Vector2f objects
+ */
+ public static Vector2f[] getVector2Array(FloatBuffer buff) {
+ buff.clear();
+ Vector2f[] verts = new Vector2f[buff.limit() / 2];
+ for (int x = 0; x < verts.length; x++) {
+ Vector2f v = new Vector2f(buff.get(), buff.get());
+ verts[x] = v;
+ }
+ return verts;
+ }
+
+ /**
+ * Copies a Vector2f from one position in the buffer to another. The index
+ * values are in terms of vector number (eg, vector number 0 is postions 0-1
+ * in the FloatBuffer.)
+ *
+ * @param buf
+ * the buffer to copy from/to
+ * @param fromPos
+ * the index of the vector to copy
+ * @param toPos
+ * the index to copy the vector to
+ */
+ public static void copyInternalVector2(FloatBuffer buf, int fromPos, int toPos) {
+ copyInternal(buf, fromPos * 2, toPos * 2, 2);
+ }
+
+ /**
+ * Normalize a Vector2f in-buffer.
+ *
+ * @param buf
+ * the buffer to find the Vector2f within
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * to normalize
+ */
+ public static void normalizeVector2(FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector2f tempVec2 = vars.vect2d;
+ populateFromBuffer(tempVec2, buf, index);
+ tempVec2.normalizeLocal();
+ setInBuffer(tempVec2, buf, index);
+ vars.release();
+ }
+
+ /**
+ * Add to a Vector2f in-buffer.
+ *
+ * @param toAdd
+ * the vector to add from
+ * @param buf
+ * the buffer to find the Vector2f within
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * to add to
+ */
+ public static void addInBuffer(Vector2f toAdd, FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector2f tempVec2 = vars.vect2d;
+ populateFromBuffer(tempVec2, buf, index);
+ tempVec2.addLocal(toAdd);
+ setInBuffer(tempVec2, buf, index);
+ vars.release();
+ }
+
+ /**
+ * Multiply and store a Vector2f in-buffer.
+ *
+ * @param toMult
+ * the vector to multiply against
+ * @param buf
+ * the buffer to find the Vector2f within
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * to multiply
+ */
+ public static void multInBuffer(Vector2f toMult, FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector2f tempVec2 = vars.vect2d;
+ populateFromBuffer(tempVec2, buf, index);
+ tempVec2.multLocal(toMult);
+ setInBuffer(tempVec2, buf, index);
+ vars.release();
+ }
+
+ /**
+ * Checks to see if the given Vector2f is equals to the data stored in the
+ * buffer at the given data index.
+ *
+ * @param check
+ * the vector to check against - null will return false.
+ * @param buf
+ * the buffer to compare data with
+ * @param index
+ * the position (in terms of vectors, not floats) of the vector
+ * in the buffer to check against
+ * @return
+ */
+ public static boolean equals(Vector2f check, FloatBuffer buf, int index) {
+ TempVars vars = TempVars.get();
+ Vector2f tempVec2 = vars.vect2d;
+ populateFromBuffer(tempVec2, buf, index);
+ boolean eq = tempVec2.equals(check);
+ vars.release();
+ return eq;
+ }
+
+ //// -- INT METHODS -- ////
+ /**
+ * Generate a new IntBuffer using the given array of ints. The IntBuffer
+ * will be data.length long and contain the int data as data[0], data[1]...
+ * etc.
+ *
+ * @param data
+ * array of ints to place into a new IntBuffer
+ */
+ public static IntBuffer createIntBuffer(int... data) {
+ if (data == null) {
+ return null;
+ }
+ IntBuffer buff = createIntBuffer(data.length);
+ buff.clear();
+ buff.put(data);
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Create a new int[] array and populate it with the given IntBuffer's
+ * contents.
+ *
+ * @param buff
+ * the IntBuffer to read from
+ * @return a new int array populated from the IntBuffer
+ */
+ public static int[] getIntArray(IntBuffer buff) {
+ if (buff == null) {
+ return null;
+ }
+ buff.clear();
+ int[] inds = new int[buff.limit()];
+ for (int x = 0; x < inds.length; x++) {
+ inds[x] = buff.get();
+ }
+ return inds;
+ }
+
+ /**
+ * Create a new float[] array and populate it with the given FloatBuffer's
+ * contents.
+ *
+ * @param buff
+ * the FloatBuffer to read from
+ * @return a new float array populated from the FloatBuffer
+ */
+ public static float[] getFloatArray(FloatBuffer buff) {
+ if (buff == null) {
+ return null;
+ }
+ buff.clear();
+ float[] inds = new float[buff.limit()];
+ for (int x = 0; x < inds.length; x++) {
+ inds[x] = buff.get();
+ }
+ return inds;
+ }
+
+ //// -- GENERAL DOUBLE ROUTINES -- ////
+ /**
+ * Create a new DoubleBuffer of the specified size.
+ *
+ * @param size
+ * required number of double to store.
+ * @return the new DoubleBuffer
+ */
+ public static DoubleBuffer createDoubleBuffer(int size) {
+ DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
+ buf.clear();
+ onBufferAllocated(buf);
+ return buf;
+ }
+
+ /**
+ * Create a new DoubleBuffer of an appropriate size to hold the specified
+ * number of doubles only if the given buffer if not already the right size.
+ *
+ * @param buf
+ * the buffer to first check and rewind
+ * @param size
+ * number of doubles that need to be held by the newly created
+ * buffer
+ * @return the requested new DoubleBuffer
+ */
+ public static DoubleBuffer createDoubleBuffer(DoubleBuffer buf, int size) {
+ if (buf != null && buf.limit() == size) {
+ buf.rewind();
+ return buf;
+ }
+
+ buf = createDoubleBuffer(size);
+ return buf;
+ }
+
+ /**
+ * Creates a new DoubleBuffer with the same contents as the given
+ * DoubleBuffer. The new DoubleBuffer is seperate from the old one and
+ * changes are not reflected across. If you want to reflect changes,
+ * consider using Buffer.duplicate().
+ *
+ * @param buf
+ * the DoubleBuffer to copy
+ * @return the copy
+ */
+ public static DoubleBuffer clone(DoubleBuffer buf) {
+ if (buf == null) {
+ return null;
+ }
+ buf.rewind();
+
+ DoubleBuffer copy;
+ if (buf.isDirect()) {
+ copy = createDoubleBuffer(buf.limit());
+ } else {
+ copy = DoubleBuffer.allocate(buf.limit());
+ }
+ copy.put(buf);
+
+ return copy;
+ }
+
+ //// -- GENERAL FLOAT ROUTINES -- ////
+ /**
+ * Create a new FloatBuffer of the specified size.
+ *
+ * @param size
+ * required number of floats to store.
+ * @return the new FloatBuffer
+ */
+ public static FloatBuffer createFloatBuffer(int size) {
+ FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ buf.clear();
+ onBufferAllocated(buf);
+ return buf;
+ }
+
+ /**
+ * Copies floats from one position in the buffer to another.
+ *
+ * @param buf
+ * the buffer to copy from/to
+ * @param fromPos
+ * the starting point to copy from
+ * @param toPos
+ * the starting point to copy to
+ * @param length
+ * the number of floats to copy
+ */
+ public static void copyInternal(FloatBuffer buf, int fromPos, int toPos, int length) {
+ float[] data = new float[length];
+ buf.position(fromPos);
+ buf.get(data);
+ buf.position(toPos);
+ buf.put(data);
+ }
+
+ /**
+ * Creates a new FloatBuffer with the same contents as the given
+ * FloatBuffer. The new FloatBuffer is seperate from the old one and changes
+ * are not reflected across. If you want to reflect changes, consider using
+ * Buffer.duplicate().
+ *
+ * @param buf
+ * the FloatBuffer to copy
+ * @return the copy
+ */
+ public static FloatBuffer clone(FloatBuffer buf) {
+ if (buf == null) {
+ return null;
+ }
+ buf.rewind();
+
+ FloatBuffer copy;
+ if (buf.isDirect()) {
+ copy = createFloatBuffer(buf.limit());
+ } else {
+ copy = FloatBuffer.allocate(buf.limit());
+ }
+ copy.put(buf);
+
+ return copy;
+ }
+
+ //// -- GENERAL INT ROUTINES -- ////
+ /**
+ * Create a new IntBuffer of the specified size.
+ *
+ * @param size
+ * required number of ints to store.
+ * @return the new IntBuffer
+ */
+ public static IntBuffer createIntBuffer(int size) {
+ IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
+ buf.clear();
+ onBufferAllocated(buf);
+ return buf;
+ }
+
+ /**
+ * Create a new IntBuffer of an appropriate size to hold the specified
+ * number of ints only if the given buffer if not already the right size.
+ *
+ * @param buf
+ * the buffer to first check and rewind
+ * @param size
+ * number of ints that need to be held by the newly created
+ * buffer
+ * @return the requested new IntBuffer
+ */
+ public static IntBuffer createIntBuffer(IntBuffer buf, int size) {
+ if (buf != null && buf.limit() == size) {
+ buf.rewind();
+ return buf;
+ }
+
+ buf = createIntBuffer(size);
+ return buf;
+ }
+
+ /**
+ * Creates a new IntBuffer with the same contents as the given IntBuffer.
+ * The new IntBuffer is seperate from the old one and changes are not
+ * reflected across. If you want to reflect changes, consider using
+ * Buffer.duplicate().
+ *
+ * @param buf
+ * the IntBuffer to copy
+ * @return the copy
+ */
+ public static IntBuffer clone(IntBuffer buf) {
+ if (buf == null) {
+ return null;
+ }
+ buf.rewind();
+
+ IntBuffer copy;
+ if (buf.isDirect()) {
+ copy = createIntBuffer(buf.limit());
+ } else {
+ copy = IntBuffer.allocate(buf.limit());
+ }
+ copy.put(buf);
+
+ return copy;
+ }
+
+ //// -- GENERAL BYTE ROUTINES -- ////
+ /**
+ * Create a new ByteBuffer of the specified size.
+ *
+ * @param size
+ * required number of ints to store.
+ * @return the new IntBuffer
+ */
+ public static ByteBuffer createByteBuffer(int size) {
+ ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+ buf.clear();
+ onBufferAllocated(buf);
+ return buf;
+ }
+
+ /**
+ * Create a new ByteBuffer of an appropriate size to hold the specified
+ * number of ints only if the given buffer if not already the right size.
+ *
+ * @param buf
+ * the buffer to first check and rewind
+ * @param size
+ * number of bytes that need to be held by the newly created
+ * buffer
+ * @return the requested new IntBuffer
+ */
+ public static ByteBuffer createByteBuffer(ByteBuffer buf, int size) {
+ if (buf != null && buf.limit() == size) {
+ buf.rewind();
+ return buf;
+ }
+
+ buf = createByteBuffer(size);
+ return buf;
+ }
+
+ public static ByteBuffer createByteBuffer(byte... data) {
+ ByteBuffer bb = createByteBuffer(data.length);
+ bb.put(data);
+ bb.flip();
+ return bb;
+ }
+
+ public static ByteBuffer createByteBuffer(String data) {
+ byte[] bytes = data.getBytes();
+ ByteBuffer bb = createByteBuffer(bytes.length);
+ bb.put(bytes);
+ bb.flip();
+ return bb;
+ }
+
+ /**
+ * Creates a new ByteBuffer with the same contents as the given ByteBuffer.
+ * The new ByteBuffer is seperate from the old one and changes are not
+ * reflected across. If you want to reflect changes, consider using
+ * Buffer.duplicate().
+ *
+ * @param buf
+ * the ByteBuffer to copy
+ * @return the copy
+ */
+ public static ByteBuffer clone(ByteBuffer buf) {
+ if (buf == null) {
+ return null;
+ }
+ buf.rewind();
+
+ ByteBuffer copy;
+ if (buf.isDirect()) {
+ copy = createByteBuffer(buf.limit());
+ } else {
+ copy = ByteBuffer.allocate(buf.limit());
+ }
+ copy.put(buf);
+
+ return copy;
+ }
+
+ //// -- GENERAL SHORT ROUTINES -- ////
+ /**
+ * Create a new ShortBuffer of the specified size.
+ *
+ * @param size
+ * required number of shorts to store.
+ * @return the new ShortBuffer
+ */
+ public static ShortBuffer createShortBuffer(int size) {
+ ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
+ buf.clear();
+ onBufferAllocated(buf);
+ return buf;
+ }
+
+ /**
+ * Create a new ShortBuffer of an appropriate size to hold the specified
+ * number of shorts only if the given buffer if not already the right size.
+ *
+ * @param buf
+ * the buffer to first check and rewind
+ * @param size
+ * number of shorts that need to be held by the newly created
+ * buffer
+ * @return the requested new ShortBuffer
+ */
+ public static ShortBuffer createShortBuffer(ShortBuffer buf, int size) {
+ if (buf != null && buf.limit() == size) {
+ buf.rewind();
+ return buf;
+ }
+
+ buf = createShortBuffer(size);
+ return buf;
+ }
+
+ public static ShortBuffer createShortBuffer(short... data) {
+ if (data == null) {
+ return null;
+ }
+ ShortBuffer buff = createShortBuffer(data.length);
+ buff.clear();
+ buff.put(data);
+ buff.flip();
+ return buff;
+ }
+
+ /**
+ * Creates a new ShortBuffer with the same contents as the given ShortBuffer.
+ * The new ShortBuffer is seperate from the old one and changes are not
+ * reflected across. If you want to reflect changes, consider using
+ * Buffer.duplicate().
+ *
+ * @param buf
+ * the ShortBuffer to copy
+ * @return the copy
+ */
+ public static ShortBuffer clone(ShortBuffer buf) {
+ if (buf == null) {
+ return null;
+ }
+ buf.rewind();
+
+ ShortBuffer copy;
+ if (buf.isDirect()) {
+ copy = createShortBuffer(buf.limit());
+ } else {
+ copy = ShortBuffer.allocate(buf.limit());
+ }
+ copy.put(buf);
+
+ return copy;
+ }
+
+ /**
+ * Ensures there is at least the <code>required</code> number of entries left after the current position of the
+ * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.
+ * @param buffer buffer that should be checked/copied (may be null)
+ * @param required minimum number of elements that should be remaining in the returned buffer
+ * @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as
+ * the input buffer, not null
+ */
+ public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
+ if (buffer == null || (buffer.remaining() < required)) {
+ int position = (buffer != null ? buffer.position() : 0);
+ FloatBuffer newVerts = createFloatBuffer(position + required);
+ if (buffer != null) {
+ buffer.rewind();
+ newVerts.put(buffer);
+ newVerts.position(position);
+ }
+ buffer = newVerts;
+ }
+ return buffer;
+ }
+
+ public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
+ if (buffer == null || (buffer.remaining() < required)) {
+ int position = (buffer != null ? buffer.position() : 0);
+ ShortBuffer newVerts = createShortBuffer(position + required);
+ if (buffer != null) {
+ buffer.rewind();
+ newVerts.put(buffer);
+ newVerts.position(position);
+ }
+ buffer = newVerts;
+ }
+ return buffer;
+ }
+
+ public static ByteBuffer ensureLargeEnough(ByteBuffer buffer, int required) {
+ if (buffer == null || (buffer.remaining() < required)) {
+ int position = (buffer != null ? buffer.position() : 0);
+ ByteBuffer newVerts = createByteBuffer(position + required);
+ if (buffer != null) {
+ buffer.rewind();
+ newVerts.put(buffer);
+ newVerts.position(position);
+ }
+ buffer = newVerts;
+ }
+ return buffer;
+ }
+
+ public static void printCurrentDirectMemory(StringBuilder store) {
+ long totalHeld = 0;
+ // make a new set to hold the keys to prevent concurrency issues.
+ ArrayList<Buffer> bufs = new ArrayList<Buffer>(trackingHash.keySet());
+ int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;
+ int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0;
+ for (Buffer b : bufs) {
+ if (b instanceof ByteBuffer) {
+ totalHeld += b.capacity();
+ bBufsM += b.capacity();
+ bBufs++;
+ } else if (b instanceof FloatBuffer) {
+ totalHeld += b.capacity() * 4;
+ fBufsM += b.capacity() * 4;
+ fBufs++;
+ } else if (b instanceof IntBuffer) {
+ totalHeld += b.capacity() * 4;
+ iBufsM += b.capacity() * 4;
+ iBufs++;
+ } else if (b instanceof ShortBuffer) {
+ totalHeld += b.capacity() * 2;
+ sBufsM += b.capacity() * 2;
+ sBufs++;
+ } else if (b instanceof DoubleBuffer) {
+ totalHeld += b.capacity() * 8;
+ dBufsM += b.capacity() * 8;
+ dBufs++;
+ }
+ }
+ long heapMem = Runtime.getRuntime().totalMemory()
+ - Runtime.getRuntime().freeMemory();
+
+ boolean printStout = store == null;
+ if (store == null) {
+ store = new StringBuilder();
+ }
+ store.append("Existing buffers: ").append(bufs.size()).append("\n");
+ store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n");
+ store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
+ store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
+ store.append("(b: ").append(bBufsM / 1024).append("kb f: ").append(fBufsM / 1024).append("kb i: ").append(iBufsM / 1024).append("kb s: ").append(sBufsM / 1024).append("kb d: ").append(dBufsM / 1024).append("kb)").append("\n");
+ if (printStout) {
+ System.out.println(store.toString());
+ }
+ }
+
+ /**
+ * Direct buffers are garbage collected by using a phantom reference and a
+ * reference queue. Every once a while, the JVM checks the reference queue and
+ * cleans the direct buffers. However, as this doesn't happen
+ * immediately after discarding all references to a direct buffer, it's
+ * easy to OutOfMemoryError yourself using direct buffers. This function
+ * explicitly calls the Cleaner method of a direct buffer.
+ *
+ * @param toBeDestroyed
+ * The direct buffer that will be "cleaned". Utilizes reflection.
+ *
+ */
+ public static void destroyDirectBuffer(Buffer toBeDestroyed) {
+
+ if (!toBeDestroyed.isDirect()) {
+ return;
+ }
+ try {
+ Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");
+ cleanerMethod.setAccessible(true);
+ Object cleaner = cleanerMethod.invoke(toBeDestroyed);
+ if (cleaner != null) {
+ Method cleanMethod = cleaner.getClass().getMethod("clean");
+ cleanMethod.setAccessible(true);
+ cleanMethod.invoke(cleaner);
+ } else {
+ // Try the alternate approach of getting the viewed buffer
+ Method viewedBufferMethod = toBeDestroyed.getClass().getMethod("viewedBuffer");
+ viewedBufferMethod.setAccessible(true);
+ Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
+ if (viewedBuffer != null) {
+ destroyDirectBuffer( (Buffer)viewedBuffer );
+ } else {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);
+ }
+ }
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (IllegalArgumentException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (InvocationTargetException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (NoSuchMethodException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (SecurityException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/util/IntMap.java b/engine/src/core/com/jme3/util/IntMap.java
new file mode 100644
index 0000000..edf659b
--- /dev/null
+++ b/engine/src/core/com/jme3/util/IntMap.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+import com.jme3.util.IntMap.Entry;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Similar to a {@link Map} except that ints are used as keys.
+ *
+ * Taken from <a href="http://code.google.com/p/skorpios/">http://code.google.com/p/skorpios/</a>
+ *
+ * @author Nate
+ */
+public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
+
+ private final IntMapIterator iterator = new IntMapIterator();
+
+ private Entry[] table;
+ private final float loadFactor;
+ private int size, mask, capacity, threshold;
+
+ public IntMap() {
+ this(16, 0.75f);
+ }
+
+ public IntMap(int initialCapacity) {
+ this(initialCapacity, 0.75f);
+ }
+
+ public IntMap(int initialCapacity, float loadFactor) {
+ if (initialCapacity > 1 << 30){
+ throw new IllegalArgumentException("initialCapacity is too large.");
+ }
+ if (initialCapacity < 0){
+ throw new IllegalArgumentException("initialCapacity must be greater than zero.");
+ }
+ if (loadFactor <= 0){
+ throw new IllegalArgumentException("initialCapacity must be greater than zero.");
+ }
+ capacity = 1;
+ while (capacity < initialCapacity){
+ capacity <<= 1;
+ }
+ this.loadFactor = loadFactor;
+ this.threshold = (int) (capacity * loadFactor);
+ this.table = new Entry[capacity];
+ this.mask = capacity - 1;
+ }
+
+ @Override
+ public IntMap<T> clone(){
+ try{
+ IntMap<T> clone = (IntMap<T>) super.clone();
+ Entry[] newTable = new Entry[table.length];
+ for (int i = table.length - 1; i >= 0; i--){
+ if (table[i] != null)
+ newTable[i] = table[i].clone();
+ }
+ clone.table = newTable;
+ return clone;
+ }catch (CloneNotSupportedException ex){
+ }
+ return null;
+ }
+
+ public boolean containsValue(Object value) {
+ Entry[] table = this.table;
+ for (int i = table.length; i-- > 0;){
+ for (Entry e = table[i]; e != null; e = e.next){
+ if (e.value.equals(value)){
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean containsKey(int key) {
+ int index = ((int) key) & mask;
+ for (Entry e = table[index]; e != null; e = e.next){
+ if (e.key == key){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public T get(int key) {
+ int index = key & mask;
+ for (Entry e = table[index]; e != null; e = e.next){
+ if (e.key == key){
+ return (T) e.value;
+ }
+ }
+ return null;
+ }
+
+ public T put(int key, T value) {
+ int index = key & mask;
+ // Check if key already exists.
+ for (Entry e = table[index]; e != null; e = e.next){
+ if (e.key != key){
+ continue;
+ }
+ Object oldValue = e.value;
+ e.value = value;
+ return (T) oldValue;
+ }
+ table[index] = new Entry(key, value, table[index]);
+ if (size++ >= threshold){
+ // Rehash.
+ int newCapacity = 2 * capacity;
+ Entry[] newTable = new Entry[newCapacity];
+ Entry[] src = table;
+ int bucketmask = newCapacity - 1;
+ for (int j = 0; j < src.length; j++){
+ Entry e = src[j];
+ if (e != null){
+ src[j] = null;
+ do{
+ Entry next = e.next;
+ index = e.key & bucketmask;
+ e.next = newTable[index];
+ newTable[index] = e;
+ e = next;
+ }while (e != null);
+ }
+ }
+ table = newTable;
+ capacity = newCapacity;
+ threshold = (int) (newCapacity * loadFactor);
+ mask = capacity - 1;
+ }
+ return null;
+ }
+
+ public T remove(int key) {
+ int index = key & mask;
+ Entry prev = table[index];
+ Entry e = prev;
+ while (e != null){
+ Entry next = e.next;
+ if (e.key == key){
+ size--;
+ if (prev == e){
+ table[index] = next;
+ }else{
+ prev.next = next;
+ }
+ return (T) e.value;
+ }
+ prev = e;
+ e = next;
+ }
+ return null;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public void clear() {
+ Entry[] table = this.table;
+ for (int index = table.length; --index >= 0;){
+ table[index] = null;
+ }
+ size = 0;
+ }
+
+ public Iterator<Entry<T>> iterator() {
+ iterator.beginUse();
+ return iterator;
+ }
+
+ final class IntMapIterator implements Iterator<Entry<T>> {
+
+ /**
+ * Current entry.
+ */
+ private Entry cur;
+
+ /**
+ * Entry in the table
+ */
+ private int idx = 0;
+
+ /**
+ * Element in the entry
+ */
+ private int el = 0;
+
+ public IntMapIterator() {
+ }
+
+ public void beginUse(){
+ cur = table[0];
+ idx = 0;
+ el = 0;
+ }
+
+ public boolean hasNext() {
+ return el < size;
+ }
+
+ public Entry next() {
+ if (el >= size)
+ throw new IllegalStateException("No more elements!");
+
+ if (cur != null){
+ Entry e = cur;
+ cur = cur.next;
+ el++;
+ return e;
+ }
+// if (cur != null && cur.next != null){
+ // if we have a current entry, continue to the next entry in the list
+// cur = cur.next;
+// el++;
+// return cur;
+// }
+
+ do {
+ // either we exhausted the current entry list, or
+ // the entry was null. find another non-null entry.
+ cur = table[++idx];
+ } while (cur == null);
+
+ Entry e = cur;
+ cur = cur.next;
+ el ++;
+
+ return e;
+ }
+
+ public void remove() {
+ }
+
+ }
+
+ public static final class Entry<T> implements Cloneable {
+
+ final int key;
+ T value;
+ Entry next;
+
+ Entry(int k, T v, Entry n) {
+ key = k;
+ value = v;
+ next = n;
+ }
+
+ public int getKey(){
+ return key;
+ }
+
+ public T getValue(){
+ return value;
+ }
+
+ @Override
+ public String toString(){
+ return key + " => " + value;
+ }
+
+ @Override
+ public Entry<T> clone(){
+ try{
+ Entry<T> clone = (Entry<T>) super.clone();
+ clone.next = next != null ? next.clone() : null;
+ return clone;
+ }catch (CloneNotSupportedException ex){
+ }
+ return null;
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/util/JmeFormatter.java b/engine/src/core/com/jme3/util/JmeFormatter.java
new file mode 100644
index 0000000..998438a
--- /dev/null
+++ b/engine/src/core/com/jme3/util/JmeFormatter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.logging.Formatter;
+import java.util.logging.LogRecord;
+
+/**
+ * More simple formatter than the default one used in Java logging.
+ * Example output: <br/>
+ * INFO Display3D 12:00 PM: Display created.
+ */
+public class JmeFormatter extends Formatter {
+
+ private Date calendar = new Date();
+ private String lineSeperator;
+ private MessageFormat format;
+ private Object args[] = new Object[1];
+ private StringBuffer store = new StringBuffer();
+
+ public JmeFormatter(){
+ lineSeperator = System.getProperty("line.separator");
+ format = new MessageFormat("{0,time}");
+ }
+
+ @Override
+ public String format(LogRecord record) {
+ StringBuffer sb = new StringBuffer();
+
+ calendar.setTime(record.getMillis());
+ args[0] = calendar;
+ store.setLength(0);
+ format.format(args, store, null);
+
+ String clazz = null;
+ try{
+ clazz = Class.forName(record.getSourceClassName()).getSimpleName();
+ } catch (ClassNotFoundException ex){
+ }
+
+ sb.append(record.getLevel().getLocalizedName()).append(" ");
+ sb.append(clazz).append(" ");
+ sb.append(store.toString()).append(" ");
+ sb.append(formatMessage(record)).append(lineSeperator);
+
+ if (record.getThrown() != null) {
+ try {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ record.getThrown().printStackTrace(pw);
+ pw.close();
+ sb.append(sw.toString());
+ } catch (Exception ex) {
+ }
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/engine/src/core/com/jme3/util/ListMap.java b/engine/src/core/com/jme3/util/ListMap.java
new file mode 100644
index 0000000..c5b6de4
--- /dev/null
+++ b/engine/src/core/com/jme3/util/ListMap.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Implementation of a Map that favors iteration speed rather than
+ * get/put speed.
+ *
+ * @author Kirill Vainer
+ */
+public final class ListMap<K, V> implements Map<K, V>, Cloneable, Serializable {
+
+ public static void main(String[] args){
+ Map<String, String> map = new ListMap<String, String>();
+ map.put( "bob", "hello");
+ System.out.println(map.get("bob"));
+ map.remove("bob");
+ System.out.println(map.size());
+
+ map.put("abc", "1");
+ map.put("def", "2");
+ map.put("ghi", "3");
+ map.put("jkl", "4");
+ map.put("mno", "5");
+ System.out.println(map.get("ghi"));
+ }
+
+ private final static class ListMapEntry<K, V> implements Map.Entry<K, V>, Cloneable {
+
+ private final K key;
+ private V value;
+
+ public ListMapEntry(K key, V value){
+ this.key = key;
+ this.value = value;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public V setValue(V v) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListMapEntry<K, V> clone(){
+ return new ListMapEntry<K, V>(key, value);
+ }
+
+ }
+
+ private final HashMap<K, V> backingMap;
+ private ListMapEntry<K, V>[] entries;
+
+// private final ArrayList<ListMapEntry<K,V>> entries;
+
+ public ListMap(){
+ entries = new ListMapEntry[4];
+ backingMap = new HashMap<K, V>(4);
+// entries = new ArrayList<ListMapEntry<K,V>>();
+ }
+
+ public ListMap(int initialCapacity){
+ entries = new ListMapEntry[initialCapacity];
+ backingMap = new HashMap<K, V>(initialCapacity);
+// entries = new ArrayList<ListMapEntry<K, V>>(initialCapacity);
+ }
+
+ public ListMap(Map<? extends K, ? extends V> map){
+ entries = new ListMapEntry[map.size()];
+ backingMap = new HashMap<K, V>(map.size());
+// entries = new ArrayList<ListMapEntry<K, V>>(map.size());
+ putAll(map);
+ }
+
+ public int size() {
+// return entries.size();
+ return backingMap.size();
+ }
+
+ public Entry<K, V> getEntry(int index){
+// return entries.get(index);
+ return entries[index];
+ }
+
+ public V getValue(int index){
+// return entries.get(index).value;
+ return entries[index].value;
+ }
+
+ public K getKey(int index){
+// return entries.get(index).key;
+ return entries[index].key;
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ private static boolean keyEq(Object keyA, Object keyB){
+ return keyA.hashCode() == keyB.hashCode() ? (keyA == keyB) || keyA.equals(keyB) : false;
+ }
+//
+// private static boolean valEq(Object a, Object b){
+// return a == null ? (b == null) : a.equals(b);
+// }
+
+ public boolean containsKey(Object key) {
+ return backingMap.containsKey( (K) key);
+// if (key == null)
+// throw new IllegalArgumentException();
+//
+// for (int i = 0; i < entries.size(); i++){
+// ListMapEntry<K,V> entry = entries.get(i);
+// if (keyEq(entry.key, key))
+// return true;
+// }
+// return false;
+ }
+
+ public boolean containsValue(Object value) {
+ return backingMap.containsValue( (V) value);
+// for (int i = 0; i < entries.size(); i++){
+// if (valEq(entries.get(i).value, value))
+// return true;
+// }
+// return false;
+ }
+
+ public V get(Object key) {
+ return backingMap.get( (K) key);
+// if (key == null)
+// throw new IllegalArgumentException();
+//
+// for (int i = 0; i < entries.size(); i++){
+// ListMapEntry<K,V> entry = entries.get(i);
+// if (keyEq(entry.key, key))
+// return entry.value;
+// }
+// return null;
+ }
+
+ public V put(K key, V value) {
+ if (backingMap.containsKey(key)){
+ // set the value on the entry
+ int size = size();
+ for (int i = 0; i < size; i++){
+ ListMapEntry<K, V> entry = entries[i];
+ if (keyEq(entry.key, key)){
+ entry.value = value;
+ break;
+ }
+ }
+ }else{
+ int size = size();
+ // expand list as necessary
+ if (size == entries.length){
+ ListMapEntry<K, V>[] tmpEntries = entries;
+ entries = new ListMapEntry[size * 2];
+ System.arraycopy(tmpEntries, 0, entries, 0, size);
+ }
+ entries[size] = new ListMapEntry<K, V>(key, value);
+ }
+ return backingMap.put(key, value);
+// if (key == null)
+// throw new IllegalArgumentException();
+//
+// // check if entry exists, if yes, overwrite it with new value
+// for (int i = 0; i < entries.size(); i++){
+// ListMapEntry<K,V> entry = entries.get(i);
+// if (keyEq(entry.key, key)){
+// V prevValue = entry.value;
+// entry.value = value;
+// return prevValue;
+// }
+// }
+//
+// // add a new entry
+// entries.add(new ListMapEntry<K, V>(key, value));
+// return null;
+ }
+
+ public V remove(Object key) {
+ V element = backingMap.remove( (K) key);
+ if (element != null){
+ // find removed element
+ int size = size() + 1; // includes removed element
+ int removedIndex = -1;
+ for (int i = 0; i < size; i++){
+ ListMapEntry<K, V> entry = entries[i];
+ if (keyEq(entry.key, key)){
+ removedIndex = i;
+ break;
+ }
+ }
+ assert removedIndex >= 0;
+
+ size --;
+ for (int i = removedIndex; i < size; i++){
+ entries[i] = entries[i+1];
+ }
+ }
+ return element;
+// if (key == null)
+// throw new IllegalArgumentException();
+//
+// for (int i = 0; i < entries.size(); i++){
+// ListMapEntry<K,V> entry = entries.get(i);
+// if (keyEq(entry.key, key)){
+// return entries.remove(i).value;
+// }
+// }
+// return null;
+ }
+
+ public void putAll(Map<? extends K, ? extends V> map) {
+ for (Entry<? extends K, ? extends V> entry : map.entrySet()){
+ put(entry.getKey(), entry.getValue());
+ }
+
+
+// if (map instanceof ListMap){
+// ListMap<K, V> listMap = (ListMap<K, V>) map;
+// ArrayList<ListMapEntry<K, V>> otherEntries = listMap.entries;
+// for (int i = 0; i < otherEntries.size(); i++){
+// ListMapEntry<K, V> entry = otherEntries.get(i);
+// put(entry.key, entry.value);
+// }
+// }else{
+// for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()){
+// put(entry.getKey(), entry.getValue());
+// }
+// }
+ }
+
+ public void clear() {
+ backingMap.clear();
+// entries.clear();
+ }
+
+ @Override
+ public ListMap<K, V> clone(){
+ ListMap<K, V> clone = new ListMap<K, V>(size());
+ clone.putAll(this);
+ return clone;
+ }
+
+ public Set<K> keySet() {
+ return backingMap.keySet();
+// HashSet<K> keys = new HashSet<K>();
+// for (int i = 0; i < entries.size(); i++){
+// ListMapEntry<K,V> entry = entries.get(i);
+// keys.add(entry.key);
+// }
+// return keys;
+ }
+
+ public Collection<V> values() {
+ return backingMap.values();
+// ArrayList<V> values = new ArrayList<V>();
+// for (int i = 0; i < entries.size(); i++){
+// ListMapEntry<K,V> entry = entries.get(i);
+// values.add(entry.value);
+// }
+// return values;
+ }
+
+ public Set<Entry<K, V>> entrySet() {
+ return backingMap.entrySet();
+// HashSet<Entry<K, V>> entryset = new HashSet<Entry<K, V>>();
+// entryset.addAll(entries);
+// return entryset;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/util/LittleEndien.java b/engine/src/core/com/jme3/util/LittleEndien.java
new file mode 100644
index 0000000..0f71596
--- /dev/null
+++ b/engine/src/core/com/jme3/util/LittleEndien.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import java.io.*;
+
+/**
+ * <code>LittleEndien</code> is a class to read littleendien stored data
+ * via a InputStream. All functions work as defined in DataInput, but
+ * assume they come from a LittleEndien input stream. Currently used to read .ms3d and .3ds files.
+ * @author Jack Lindamood
+ */
+public class LittleEndien extends InputStream implements DataInput {
+
+ private BufferedInputStream in;
+ private BufferedReader inRead;
+
+ /**
+ * Creates a new LittleEndien reader from the given input stream. The
+ * stream is wrapped in a BufferedReader automatically.
+ * @param in The input stream to read from.
+ */
+ public LittleEndien(InputStream in) {
+ this.in = new BufferedInputStream(in);
+ inRead = new BufferedReader(new InputStreamReader(in));
+ }
+
+ public int read() throws IOException {
+ return in.read();
+ }
+
+ @Override
+ public int read(byte[] buf) throws IOException {
+ return in.read(buf);
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws IOException {
+ return in.read(buf, off, len);
+ }
+
+ public int readUnsignedShort() throws IOException {
+ return (in.read() & 0xff) | ((in.read() & 0xff) << 8);
+ }
+
+ /**
+ * read an unsigned int as a long
+ */
+ public long readUInt() throws IOException {
+ return ((in.read() & 0xff)
+ | ((in.read() & 0xff) << 8)
+ | ((in.read() & 0xff) << 16)
+ | (((long) (in.read() & 0xff)) << 24));
+ }
+
+ public boolean readBoolean() throws IOException {
+ return (in.read() != 0);
+ }
+
+ public byte readByte() throws IOException {
+ return (byte) in.read();
+ }
+
+ public int readUnsignedByte() throws IOException {
+ return in.read();
+ }
+
+ public short readShort() throws IOException {
+ return (short) this.readUnsignedShort();
+ }
+
+ public char readChar() throws IOException {
+ return (char) this.readUnsignedShort();
+ }
+
+ public int readInt() throws IOException {
+ return ((in.read() & 0xff)
+ | ((in.read() & 0xff) << 8)
+ | ((in.read() & 0xff) << 16)
+ | ((in.read() & 0xff) << 24));
+ }
+
+ public long readLong() throws IOException {
+ return ((in.read() & 0xff)
+ | ((long) (in.read() & 0xff) << 8)
+ | ((long) (in.read() & 0xff) << 16)
+ | ((long) (in.read() & 0xff) << 24)
+ | ((long) (in.read() & 0xff) << 32)
+ | ((long) (in.read() & 0xff) << 40)
+ | ((long) (in.read() & 0xff) << 48)
+ | ((long) (in.read() & 0xff) << 56));
+ }
+
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public void readFully(byte b[]) throws IOException {
+ in.read(b, 0, b.length);
+ }
+
+ public void readFully(byte b[], int off, int len) throws IOException {
+ in.read(b, off, len);
+ }
+
+ public int skipBytes(int n) throws IOException {
+ return (int) in.skip(n);
+ }
+
+ public String readLine() throws IOException {
+ return inRead.readLine();
+ }
+
+ public String readUTF() throws IOException {
+ throw new IOException("Unsupported operation");
+ }
+
+ @Override
+ public void close() throws IOException {
+ in.close();
+ }
+
+ @Override
+ public int available() throws IOException {
+ return in.available();
+ }
+}
diff --git a/engine/src/core/com/jme3/util/NativeObject.java b/engine/src/core/com/jme3/util/NativeObject.java
new file mode 100644
index 0000000..a59cb05
--- /dev/null
+++ b/engine/src/core/com/jme3/util/NativeObject.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+/**
+ * Describes a native object. An encapsulation of a certain object
+ * on the native side of the graphics or audio library.
+ *
+ * This class is used to track when OpenGL and OpenAL native objects are
+ * collected by the garbage collector, and then invoke the proper destructor
+ * on the OpenGL library to delete it from memory.
+ */
+public abstract class NativeObject implements Cloneable {
+
+ /**
+ * The ID of the object, usually depends on its type.
+ * Typically returned from calls like glGenTextures, glGenBuffers, etc.
+ */
+ protected int id = -1;
+
+ /**
+ * A reference to a "handle". By hard referencing a certain object, it's
+ * possible to find when a certain GLObject is no longer used, and to delete
+ * its instance from the graphics library.
+ */
+ protected Object handleRef = null;
+
+ /**
+ * True if the data represented by this GLObject has been changed
+ * and needs to be updated before used.
+ */
+ protected boolean updateNeeded = true;
+
+ /**
+ * The type of the GLObject, usually specified by a subclass.
+ */
+ protected final Class<?> type;
+
+ /**
+ * Creates a new GLObject with the given type. Should be
+ * called by the subclasses.
+ *
+ * @param type The type that the subclass represents.
+ */
+ public NativeObject(Class<?> type){
+ this.type = type;
+ this.handleRef = new Object();
+ }
+
+ /**
+ * Protected constructor that doesn't allocate handle ref.
+ * This is used in subclasses for the createDestructableClone().
+ */
+ protected NativeObject(Class<?> type, int id){
+ this.type = type;
+ this.id = id;
+ }
+
+ /**
+ * Sets the ID of the GLObject. This method is used in Renderer and must
+ * not be called by the user.
+ * @param id The ID to set
+ */
+ public void setId(int id){
+ if (this.id != -1)
+ throw new IllegalStateException("ID has already been set for this GL object.");
+
+ this.id = id;
+ }
+
+ /**
+ * @return The ID of the object. Should not be used by user code in most
+ * cases.
+ */
+ public int getId(){
+ return id;
+ }
+
+ /**
+ * Internal use only. Indicates that the object has changed
+ * and its state needs to be updated.
+ */
+ public void setUpdateNeeded(){
+ updateNeeded = true;
+ }
+
+ /**
+ * Internal use only. Indicates that the state changes were applied.
+ */
+ public void clearUpdateNeeded(){
+ updateNeeded = false;
+ }
+
+ /**
+ * Internal use only. Check if {@link #setUpdateNeeded()} was called before.
+ */
+ public boolean isUpdateNeeded(){
+ return updateNeeded;
+ }
+
+ @Override
+ public String toString(){
+ return "Native" + type.getSimpleName() + " " + id;
+ }
+
+ /**
+ * This should create a deep clone. For a shallow clone, use
+ * createDestructableClone().
+ */
+ @Override
+ protected NativeObject clone(){
+ try{
+ NativeObject obj = (NativeObject) super.clone();
+ obj.handleRef = new Object();
+ obj.id = -1;
+ obj.updateNeeded = true;
+ return obj;
+ }catch (CloneNotSupportedException ex){
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called when the GL context is restarted to reset all IDs. Prevents
+ * "white textures" on display restart.
+ */
+ public abstract void resetObject();
+
+ /**
+ * Deletes the GL object from the GPU when it is no longer used. Called
+ * automatically by the GL object manager.
+ *
+ * @param rendererObject The renderer to be used to delete the object
+ */
+ public abstract void deleteObject(Object rendererObject);
+
+ /**
+ * Creates a shallow clone of this GL Object. The deleteObject method
+ * should be functional for this object.
+ */
+ public abstract NativeObject createDestructableClone();
+}
diff --git a/engine/src/core/com/jme3/util/NativeObjectManager.java b/engine/src/core/com/jme3/util/NativeObjectManager.java
new file mode 100644
index 0000000..f8d1d18
--- /dev/null
+++ b/engine/src/core/com/jme3/util/NativeObjectManager.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * GLObjectManager tracks all GLObjects used by the Renderer. Using a
+ * <code>ReferenceQueue</code> the <code>GLObjectManager</code> can delete
+ * unused objects from GPU when their counterparts on the CPU are no longer used.
+ *
+ * On restart, the renderer may request the objects to be reset, thus allowing
+ * the GLObjects to re-initialize with the new display context.
+ */
+public class NativeObjectManager {
+
+ private static final Logger logger = Logger.getLogger(NativeObjectManager.class.getName());
+
+ /**
+ * The queue will receive notifications of {@link NativeObject}s which are no longer
+ * referenced.
+ */
+ private ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
+
+ /**
+ * List of currently active GLObjects.
+ */
+ private ArrayList<NativeObjectRef> refList
+ = new ArrayList<NativeObjectRef>();
+
+ private class NativeObjectRef extends PhantomReference<Object>{
+
+ private NativeObject objClone;
+ private WeakReference<NativeObject> realObj;
+
+ public NativeObjectRef(NativeObject obj){
+ super(obj.handleRef, refQueue);
+ assert obj.handleRef != null;
+
+ this.realObj = new WeakReference<NativeObject>(obj);
+ this.objClone = obj.createDestructableClone();
+ }
+ }
+
+ /**
+ * Register a GLObject with the manager.
+ */
+ public void registerForCleanup(NativeObject obj){
+ NativeObjectRef ref = new NativeObjectRef(obj);
+ refList.add(ref);
+ if (logger.isLoggable(Level.FINEST))
+ logger.log(Level.FINEST, "Registered: {0}", new String[]{obj.toString()});
+ }
+
+ /**
+ * Deletes unused GLObjects
+ */
+ public void deleteUnused(Object rendererObject){
+ while (true){
+ NativeObjectRef ref = (NativeObjectRef) refQueue.poll();
+ if (ref == null)
+ return;
+
+ refList.remove(ref);
+ ref.objClone.deleteObject(rendererObject);
+ if (logger.isLoggable(Level.FINEST))
+ logger.log(Level.FINEST, "Deleted: {0}", ref.objClone);
+ }
+ }
+
+ /**
+ * Deletes all objects. Must only be called when display is destroyed.
+ */
+ public void deleteAllObjects(Object rendererObject){
+ deleteUnused(rendererObject);
+ for (NativeObjectRef ref : refList){
+ ref.objClone.deleteObject(rendererObject);
+ NativeObject realObj = ref.realObj.get();
+ if (realObj != null){
+ // Note: make sure to reset them as well
+ // They may get used in a new renderer in the future
+ realObj.resetObject();
+ }
+ }
+ refList.clear();
+ }
+
+ /**
+ * Resets all {@link NativeObject}s.
+ */
+ public void resetObjects(){
+ for (NativeObjectRef ref : refList){
+ // here we use the actual obj not the clone,
+ // otherwise its useless
+ NativeObject realObj = ref.realObj.get();
+ if (realObj == null)
+ continue;
+
+ realObj.resetObject();
+ if (logger.isLoggable(Level.FINEST))
+ logger.log(Level.FINEST, "Reset: {0}", realObj);
+ }
+ refList.clear();
+ }
+
+// public void printObjects(){
+// System.out.println(" ------------------- ");
+// System.out.println(" GL Object count: "+ objectList.size());
+// for (GLObject obj : objectList){
+// System.out.println(obj);
+// }
+// }
+}
diff --git a/engine/src/core/com/jme3/util/PlaceholderAssets.java b/engine/src/core/com/jme3/util/PlaceholderAssets.java
new file mode 100644
index 0000000..c36abc9
--- /dev/null
+++ b/engine/src/core/com/jme3/util/PlaceholderAssets.java
@@ -0,0 +1,72 @@
+package com.jme3.util;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioBuffer;
+import com.jme3.audio.AudioData;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.nio.ByteBuffer;
+
+public class PlaceholderAssets {
+
+ /**
+ * Checkerboard of white and red squares
+ */
+ private static final byte[] imageData = {
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ };
+
+ public static Image getPlaceholderImage(){
+ ByteBuffer tempData = BufferUtils.createByteBuffer(3 * 4 * 4);
+ tempData.put(imageData).flip();
+ return new Image(Format.RGB8, 4, 4, tempData);
+ }
+
+ public static Material getPlaceholderMaterial(AssetManager assetManager){
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Red);
+ return mat;
+ }
+
+ public static Spatial getPlaceholderModel(AssetManager assetManager){
+ // What should be the size? Nobody knows
+ // the user's expected scale...
+ Box box = new Box(1, 1, 1);
+ Geometry geom = new Geometry("placeholder", box);
+ geom.setMaterial(getPlaceholderMaterial(assetManager));
+ return geom;
+ }
+
+ public static AudioData getPlaceholderAudio(){
+ AudioBuffer audioBuf = new AudioBuffer();
+ audioBuf.setupFormat(1, 8, 44100);
+ ByteBuffer bb = BufferUtils.createByteBuffer(1);
+ bb.put((byte)0).flip();
+ audioBuf.updateData(bb);
+ return audioBuf;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/util/SafeArrayList.java b/engine/src/core/com/jme3/util/SafeArrayList.java
new file mode 100644
index 0000000..fcd6971
--- /dev/null
+++ b/engine/src/core/com/jme3/util/SafeArrayList.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2009-2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+import java.util.*;
+
+/**
+ * <p>Provides a list with similar modification semantics to java.util.concurrent's
+ * CopyOnWriteArrayList except that it is not concurrent and also provides
+ * direct access to the current array. This List allows modification of the
+ * contents while iterating as any iterators will be looking at a snapshot of
+ * the list at the time they were created. Similarly, access the raw internal
+ * array is only presenting a snap shot and so can be safely iterated while
+ * the list is changing.</p>
+ *
+ * <p>All modifications, including set() operations will cause a copy of the
+ * data to be created that replaces the old version. Because this list is
+ * not designed for threading concurrency it further optimizes the "many modifications"
+ * case by buffering them as a normal ArrayList until the next time the contents
+ * are accessed.</p>
+ *
+ * <p>Normal list modification performance should be equal to ArrayList in a
+ * many situations and always better than CopyOnWriteArrayList. Optimum usage
+ * is when modifications are done infrequently or in batches... as is often the
+ * case in a scene graph. Read operations perform superior to all other methods
+ * as the array can be accessed directly.</p>
+ *
+ * <p>Important caveats over normal java.util.Lists:</p>
+ * <ul>
+ * <li>Even though this class supports modifying the list, the subList() method
+ * returns a read-only list. This technically breaks the List contract.</li>
+ * <li>The ListIterators returned by this class only support the remove()
+ * modification method. add() and set() are not supported on the iterator.
+ * Even after ListIterator.remove() or Iterator.remove() is called, this change
+ * is not reflected in the iterator instance as it is still refering to its
+ * original snapshot.
+ * </ul>
+ *
+ * @version $Revision: 8940 $
+ * @author Paul Speed
+ */
+public class SafeArrayList<E> implements List<E> {
+
+ // Implementing List directly to avoid accidentally acquiring
+ // incorrect or non-optimal behavior from AbstractList. For
+ // example, the default iterator() method will not work for
+ // this list.
+
+ // Note: given the particular use-cases this was intended,
+ // it would make sense to nerf the public mutators and
+ // make this publicly act like a read-only list.
+ // SafeArrayList-specific methods could then be exposed
+ // for the classes like Node and Spatial to use to manage
+ // the list. This was the callers couldn't remove a child
+ // without it being detached properly, for example.
+
+ private Class<E> elementType;
+ private List<E> buffer;
+ private E[] backingArray;
+ private int size = 0;
+
+ public SafeArrayList(Class<E> elementType) {
+ this.elementType = elementType;
+ }
+
+ public SafeArrayList(Class<E> elementType, Collection<? extends E> c) {
+ this.elementType = elementType;
+ addAll(c);
+ }
+
+ protected final <T> T[] createArray(Class<T> type, int size) {
+ return (T[])java.lang.reflect.Array.newInstance(type, size);
+ }
+
+ protected final E[] createArray(int size) {
+ return createArray(elementType, size);
+ }
+
+ /**
+ * Returns a current snapshot of this List's backing array that
+ * is guaranteed not to change through further List manipulation.
+ * Changes to this array may or may not be reflected in the list and
+ * should be avoided.
+ */
+ public final E[] getArray() {
+ if( backingArray != null )
+ return backingArray;
+
+ if( buffer == null ) {
+ backingArray = createArray(0);
+ } else {
+ // Only keep the array or the buffer but never both at
+ // the same time. 1) it saves space, 2) it keeps the rest
+ // of the code safer.
+ backingArray = buffer.toArray( createArray(buffer.size()) );
+ buffer = null;
+ }
+ return backingArray;
+ }
+
+ protected final List<E> getBuffer() {
+ if( buffer != null )
+ return buffer;
+
+ if( backingArray == null ) {
+ buffer = new ArrayList();
+ } else {
+ // Only keep the array or the buffer but never both at
+ // the same time. 1) it saves space, 2) it keeps the rest
+ // of the code safer.
+ buffer = new ArrayList( Arrays.asList(backingArray) );
+ backingArray = null;
+ }
+ return buffer;
+ }
+
+ public final int size() {
+ return size;
+ }
+
+ public final boolean isEmpty() {
+ return size == 0;
+ }
+
+ public boolean contains(Object o) {
+ return indexOf(o) >= 0;
+ }
+
+ public Iterator<E> iterator() {
+ return listIterator();
+ }
+
+ public Object[] toArray() {
+ return getArray();
+ }
+
+ public <T> T[] toArray(T[] a) {
+
+ E[] array = getArray();
+ if (a.length < array.length) {
+ return (T[])Arrays.copyOf(array, array.length, a.getClass());
+ }
+
+ System.arraycopy( array, 0, a, 0, array.length );
+
+ if (a.length > array.length) {
+ a[array.length] = null;
+ }
+
+ return a;
+ }
+
+ public boolean add(E e) {
+ boolean result = getBuffer().add(e);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public boolean remove(Object o) {
+ boolean result = getBuffer().remove(o);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public boolean containsAll(Collection<?> c) {
+ return Arrays.asList(getArray()).containsAll(c);
+ }
+
+ public boolean addAll(Collection<? extends E> c) {
+ boolean result = getBuffer().addAll(c);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public boolean addAll(int index, Collection<? extends E> c) {
+ boolean result = getBuffer().addAll(index, c);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public boolean removeAll(Collection<?> c) {
+ boolean result = getBuffer().removeAll(c);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public boolean retainAll(Collection<?> c) {
+ boolean result = getBuffer().retainAll(c);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public void clear() {
+ getBuffer().clear();
+ size = 0;
+ }
+
+ public boolean equals(Object o) {
+ if( o == this )
+ return true;
+ if( !(o instanceof List) ) //covers null too
+ return false;
+ List other = (List)o;
+ Iterator i1 = iterator();
+ Iterator i2 = other.iterator();
+ while( i1.hasNext() && i2.hasNext() ) {
+ Object o1 = i1.next();
+ Object o2 = i2.next();
+ if( o1 == o2 )
+ continue;
+ if( o1 == null || !o1.equals(o2) )
+ return false;
+ }
+ return !(i1.hasNext() || !i2.hasNext());
+ }
+
+ public int hashCode() {
+ // Exactly the hash code described in the List interface, basically
+ E[] array = getArray();
+ int result = 1;
+ for( E e : array ) {
+ result = 31 * result + (e == null ? 0 : e.hashCode());
+ }
+ return result;
+ }
+
+ public final E get(int index) {
+ if( backingArray != null )
+ return backingArray[index];
+ if( buffer != null )
+ return buffer.get(index);
+ throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" );
+ }
+
+ public E set(int index, E element) {
+ return getBuffer().set(index, element);
+ }
+
+ public void add(int index, E element) {
+ getBuffer().add(index, element);
+ size = getBuffer().size();
+ }
+
+ public E remove(int index) {
+ E result = getBuffer().remove(index);
+ size = getBuffer().size();
+ return result;
+ }
+
+ public int indexOf(Object o) {
+ E[] array = getArray();
+ for( int i = 0; i < array.length; i++ ) {
+ E element = array[i];
+ if( element == o ) {
+ return i;
+ }
+ if( element != null && element.equals(o) ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int lastIndexOf(Object o) {
+ E[] array = getArray();
+ for( int i = array.length - 1; i >= 0; i-- ) {
+ E element = array[i];
+ if( element == o ) {
+ return i;
+ }
+ if( element != null && element.equals(o) ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public ListIterator<E> listIterator() {
+ return new ArrayIterator<E>(getArray(), 0);
+ }
+
+ public ListIterator<E> listIterator(int index) {
+ return new ArrayIterator<E>(getArray(), index);
+ }
+
+ public List<E> subList(int fromIndex, int toIndex) {
+
+ // So far JME doesn't use subList that I can see so I'm nerfing it.
+ List<E> raw = Arrays.asList(getArray()).subList(fromIndex, toIndex);
+ return Collections.unmodifiableList(raw);
+ }
+
+ public String toString() {
+
+ E[] array = getArray();
+ if( array.length == 0 ) {
+ return "[]";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ for( int i = 0; i < array.length; i++ ) {
+ if( i > 0 )
+ sb.append( ", " );
+ E e = array[i];
+ sb.append( e == this ? "(this Collection)" : e );
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ protected class ArrayIterator<E> implements ListIterator<E> {
+ private E[] array;
+ private int next;
+ private int lastReturned;
+
+ protected ArrayIterator( E[] array, int index ) {
+ this.array = array;
+ this.next = index;
+ this.lastReturned = -1;
+ }
+
+ public boolean hasNext() {
+ return next != array.length;
+ }
+
+ public E next() {
+ if( !hasNext() )
+ throw new NoSuchElementException();
+ lastReturned = next++;
+ return array[lastReturned];
+ }
+
+ public boolean hasPrevious() {
+ return next != 0;
+ }
+
+ public E previous() {
+ if( !hasPrevious() )
+ throw new NoSuchElementException();
+ lastReturned = --next;
+ return array[lastReturned];
+ }
+
+ public int nextIndex() {
+ return next;
+ }
+
+ public int previousIndex() {
+ return next - 1;
+ }
+
+ public void remove() {
+ // This operation is not so easy to do but we will fake it.
+ // The issue is that the backing list could be completely
+ // different than the one this iterator is a snapshot of.
+ // We'll just remove(element) which in most cases will be
+ // correct. If the list had earlier .equals() equivalent
+ // elements then we'll remove one of those instead. Either
+ // way, none of those changes are reflected in this iterator.
+ SafeArrayList.this.remove( array[lastReturned] );
+ }
+
+ public void set(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void add(E e) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/util/SkyFactory.java b/engine/src/core/com/jme3/util/SkyFactory.java
new file mode 100644
index 0000000..2808696
--- /dev/null
+++ b/engine/src/core/com/jme3/util/SkyFactory.java
@@ -0,0 +1,214 @@
+package com.jme3.util;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.TextureCubeMap;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * <code>SkyFactory</code> is used to create jME {@link Spatial}s that can
+ * be attached to the scene to display a sky image in the background.
+ *
+ * @author Kirill Vainer
+ */
+public class SkyFactory {
+
+ /**
+ * Creates a sky using the given texture (cubemap or spheremap).
+ *
+ * @param assetManager The asset manager to use to load materials
+ * @param texture Texture to use for the sky
+ * @param normalScale The normal scale is multiplied by the 3D normal
+ * to get a texture coordinate. Use Vector3f.UNIT_XYZ to not apply
+ * and transformation to the normal.
+ * @param sphereMap The way the texture is used
+ * depends on this value:<br>
+ * <ul>
+ * <li>true: Its a Texture2D with the pixels arranged for
+ * <a href="http://en.wikipedia.org/wiki/Sphere_mapping">sphere mapping</a>.</li>
+ * <li>false: Its either a TextureCubeMap or Texture2D. If its a Texture2D
+ * then the image is taken from it and is inserted into a TextureCubeMap</li>
+ * </ul>
+ * @return A spatial representing the sky
+ */
+ public static Spatial createSky(AssetManager assetManager, Texture texture, Vector3f normalScale, boolean sphereMap) {
+ return createSky(assetManager, texture, normalScale, sphereMap, 10);
+ }
+
+ /**
+ * Creates a sky using the given texture (cubemap or spheremap).
+ *
+ * @param assetManager The asset manager to use to load materials
+ * @param texture Texture to use for the sky
+ * @param normalScale The normal scale is multiplied by the 3D normal
+ * to get a texture coordinate. Use Vector3f.UNIT_XYZ to not apply
+ * and transformation to the normal.
+ * @param sphereMap The way the texture is used
+ * depends on this value:<br>
+ * <ul>
+ * <li>true: Its a Texture2D with the pixels arranged for
+ * <a href="http://en.wikipedia.org/wiki/Sphere_mapping">sphere mapping</a>.</li>
+ * <li>false: Its either a TextureCubeMap or Texture2D. If its a Texture2D
+ * then the image is taken from it and is inserted into a TextureCubeMap</li>
+ * </ul>
+ * @param sphereRadius If specified, this will be the sky sphere's radius.
+ * This should be the camera's near plane for optimal quality.
+ * @return A spatial representing the sky
+ */
+ public static Spatial createSky(AssetManager assetManager, Texture texture, Vector3f normalScale, boolean sphereMap, int sphereRadius) {
+ if (texture == null) {
+ throw new IllegalArgumentException("texture cannot be null");
+ }
+ final Sphere sphereMesh = new Sphere(10, 10, sphereRadius, false, true);
+
+ Geometry sky = new Geometry("Sky", sphereMesh);
+ sky.setQueueBucket(Bucket.Sky);
+ sky.setCullHint(Spatial.CullHint.Never);
+ sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO));
+
+ Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md");
+
+ skyMat.setVector3("NormalScale", normalScale);
+ if (sphereMap) {
+ skyMat.setBoolean("SphereMap", sphereMap);
+ } else if (!(texture instanceof TextureCubeMap)) {
+ // make sure its a cubemap
+ Image img = texture.getImage();
+ texture = new TextureCubeMap();
+ texture.setImage(img);
+ }
+ skyMat.setTexture("Texture", texture);
+ sky.setMaterial(skyMat);
+
+ return sky;
+ }
+
+ private static void checkImage(Image image) {
+// if (image.getDepth() != 1)
+// throw new IllegalArgumentException("3D/Array images not allowed");
+
+ if (image.getWidth() != image.getHeight()) {
+ throw new IllegalArgumentException("Image width and height must be the same");
+ }
+
+ if (image.getMultiSamples() != 1) {
+ throw new IllegalArgumentException("Multisample textures not allowed");
+ }
+ }
+
+ private static void checkImagesForCubeMap(Image... images) {
+ if (images.length == 1) {
+ return;
+ }
+
+ Format fmt = images[0].getFormat();
+ int width = images[0].getWidth();
+ int height = images[0].getHeight();
+
+ ByteBuffer data = images[0].getData(0);
+ int size = data != null ? data.capacity() : 0;
+
+ checkImage(images[0]);
+
+ for (int i = 1; i < images.length; i++) {
+ Image image = images[i];
+ checkImage(images[i]);
+ if (image.getFormat() != fmt) {
+ throw new IllegalArgumentException("Images must have same format");
+ }
+ if (image.getWidth() != width || image.getHeight() != height) {
+ throw new IllegalArgumentException("Images must have same resolution");
+ }
+ ByteBuffer data2 = image.getData(0);
+ if (data2 != null){
+ if (data2.capacity() != size) {
+ throw new IllegalArgumentException("Images must have same size");
+ }
+ }
+ }
+ }
+
+ public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down, Vector3f normalScale) {
+ return createSky(assetManager, west, east, north, south, up, down, normalScale, 10);
+ }
+
+ public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down, Vector3f normalScale, int sphereRadius) {
+ final Sphere sphereMesh = new Sphere(10, 10, sphereRadius, false, true);
+ Geometry sky = new Geometry("Sky", sphereMesh);
+ sky.setQueueBucket(Bucket.Sky);
+ sky.setCullHint(Spatial.CullHint.Never);
+ sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO));
+
+ Image westImg = west.getImage();
+ Image eastImg = east.getImage();
+ Image northImg = north.getImage();
+ Image southImg = south.getImage();
+ Image upImg = up.getImage();
+ Image downImg = down.getImage();
+
+ checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg);
+
+ Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null);
+
+ cubeImage.addData(westImg.getData(0));
+ cubeImage.addData(eastImg.getData(0));
+
+ cubeImage.addData(downImg.getData(0));
+ cubeImage.addData(upImg.getData(0));
+
+ cubeImage.addData(southImg.getData(0));
+ cubeImage.addData(northImg.getData(0));
+
+ if (westImg.getEfficentData() != null){
+ // also consilidate efficient data
+ ArrayList<Object> efficientData = new ArrayList<Object>(6);
+ efficientData.add(westImg.getEfficentData());
+ efficientData.add(eastImg.getEfficentData());
+ efficientData.add(downImg.getEfficentData());
+ efficientData.add(upImg.getEfficentData());
+ efficientData.add(southImg.getEfficentData());
+ efficientData.add(northImg.getEfficentData());
+ cubeImage.setEfficentData(efficientData);
+ }
+
+ TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
+ cubeMap.setAnisotropicFilter(0);
+ cubeMap.setMagFilter(Texture.MagFilter.Bilinear);
+ cubeMap.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+ cubeMap.setWrap(Texture.WrapMode.EdgeClamp);
+
+ Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md");
+ skyMat.setTexture("Texture", cubeMap);
+ skyMat.setVector3("NormalScale", normalScale);
+ sky.setMaterial(skyMat);
+
+ return sky;
+ }
+
+ public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down) {
+ return createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ);
+ }
+
+ public static Spatial createSky(AssetManager assetManager, Texture texture, boolean sphereMap) {
+ return createSky(assetManager, texture, Vector3f.UNIT_XYZ, sphereMap);
+ }
+
+ public static Spatial createSky(AssetManager assetManager, String textureName, boolean sphereMap) {
+ TextureKey key = new TextureKey(textureName, true);
+ key.setGenerateMips(true);
+ key.setAsCube(!sphereMap);
+ Texture tex = assetManager.loadTexture(key);
+ return createSky(assetManager, tex, sphereMap);
+ }
+}
diff --git a/engine/src/core/com/jme3/util/SortUtil.java b/engine/src/core/com/jme3/util/SortUtil.java
new file mode 100644
index 0000000..fabe3bf
--- /dev/null
+++ b/engine/src/core/com/jme3/util/SortUtil.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Quick and merge sort implementations that create no garbage, unlike {@link
+ * Arrays#sort}. The merge sort is stable, the quick sort is not.
+ */
+public class SortUtil {
+
+ /**
+ * The size at or below which we will use insertion sort because it's
+ * probably faster.
+ */
+ private static final int INSERTION_SORT_THRESHOLD = 7;
+
+
+ /**
+ procedure optimizedGnomeSort(a[])
+ pos := 1
+ last := 0
+ while pos < length(a)
+ if (a[pos] >= a[pos-1])
+ if (last != 0)
+ pos := last
+ last := 0
+ end if
+ pos := pos + 1
+ else
+ swap a[pos] and a[pos-1]
+ if (pos > 1)
+ if (last == 0)
+ last := pos
+ end if
+ pos := pos - 1
+ else
+ pos := pos + 1
+ end if
+ end if
+ end while
+end procedure
+ */
+
+ public static void gsort(Object[] a, Comparator comp) {
+ int pos = 1;
+ int last = 0;
+ int length = a.length;
+
+ while (pos < length){
+ if ( comp.compare(a[pos], a[pos-1]) >= 0 ){
+ if (last != 0){
+ pos = last;
+ last = 0;
+ }
+ pos ++;
+ }else{
+ Object tmp = a[pos];
+ a[pos] = a[pos-1];
+ a[pos-1] = tmp;
+
+ if (pos > 1){
+ if (last == 0){
+ last = pos;
+ }
+ pos --;
+ }else{
+ pos ++;
+ }
+ }
+ }
+
+// int p = 0;
+// int l = a.length;
+// while (p < l) {
+// int pm1 = p - 1;
+// if (p == 0 || comp.compare(a[p], a[pm1]) >= 0) {
+// p++;
+// } else {
+// Object t = a[p];
+// a[p] = a[pm1];
+// a[pm1] = t;
+// p--;
+// }
+// }
+ }
+
+ private static void test(Float[] original, Float[] sorted, Comparator<Float> ic) {
+ long time, dt;
+
+ time = System.nanoTime();
+ for (int i = 0; i < 1000000; i++) {
+ System.arraycopy(original, 0, sorted, 0, original.length);
+ gsort(sorted, ic);
+ }
+ dt = System.nanoTime() - time;
+ System.out.println("GSort " + (dt/1000000.0) + " ms");
+
+ time = System.nanoTime();
+ for (int i = 0; i < 1000000; i++) {
+ System.arraycopy(original, 0, sorted, 0, original.length);
+ qsort(sorted, ic);
+ }
+ dt = System.nanoTime() - time;
+ System.out.println("QSort " + (dt/1000000.0) + " ms");
+
+ time = System.nanoTime();
+ for (int i = 0; i < 1000000; i++) {
+ System.arraycopy(original, 0, sorted, 0, original.length);
+ msort(original, sorted, ic);
+ }
+ dt = System.nanoTime() - time;
+ System.out.println("MSort " + (dt/1000000.0) + " ms");
+
+ time = System.nanoTime();
+ for (int i = 0; i < 1000000; i++) {
+ System.arraycopy(original, 0, sorted, 0, original.length);
+ Arrays.sort(sorted, ic);
+ }
+ dt = System.nanoTime() - time;
+ System.out.println("ASort " + (dt/1000000.0) + " ms");
+ }
+
+ public static void main(String[] args) {
+ Comparator<Float> ic = new Comparator<Float>() {
+
+ public int compare(Float o1, Float o2) {
+ return (int) (o1 - o2);
+ }
+ };
+ Float[] original = new Float[]{2f, 1f, 5f, 3f, 4f, 6f, 8f, 9f,
+ 11f, 10f, 12f, 13f, 14f, 15f, 7f, 19f, 20f, 18f, 16f, 17f,
+ 21f, 23f, 22f, 24f, 25f, 27f, 26f, 29f, 28f, 30f, 31f};
+ Float[] sorted = new Float[original.length];
+
+ while (true) {
+ test(original, sorted, ic);
+ }
+ }
+
+ /**
+ * Quick sorts the supplied array using the specified comparator.
+ */
+ public static void qsort(Object[] a, Comparator comp) {
+ qsort(a, 0, a.length - 1, comp);
+ }
+
+ /**
+ * Quick sorts the supplied array using the specified comparator.
+ *
+ * @param lo0 the index of the lowest element to include in the sort.
+ * @param hi0 the index of the highest element to include in the sort.
+ */
+ @SuppressWarnings("unchecked")
+ public static void qsort(Object[] a, int lo0, int hi0, Comparator comp) {
+ // bail out if we're already done
+ if (hi0 <= lo0) {
+ return;
+ }
+
+ // if this is a two element list, do a simple sort on it
+ Object t;
+ if (hi0 - lo0 == 1) {
+ // if they're not already sorted, swap them
+ if (comp.compare(a[hi0], a[lo0]) < 0) {
+ t = a[lo0];
+ a[lo0] = a[hi0];
+ a[hi0] = t;
+ }
+ return;
+ }
+
+ // the middle element in the array is our partitioning element
+ Object mid = a[(lo0 + hi0) / 2];
+
+ // set up our partitioning boundaries
+ int lo = lo0 - 1, hi = hi0 + 1;
+
+ // loop through the array until indices cross
+ for (;;) {
+ // find the first element that is greater than or equal to
+ // the partition element starting from the left Index.
+ while (comp.compare(a[++lo], mid) < 0);
+
+ // find an element that is smaller than or equal to
+ // the partition element starting from the right Index.
+ while (comp.compare(mid, a[--hi]) < 0);
+
+ // swap the two elements or bail out of the loop
+ if (hi > lo) {
+ t = a[lo];
+ a[lo] = a[hi];
+ a[hi] = t;
+ } else {
+ break;
+ }
+ }
+
+ // if the right index has not reached the left side of array
+ // must now sort the left partition
+ if (lo0 < lo - 1) {
+ qsort(a, lo0, lo - 1, comp);
+ }
+
+ // if the left index has not reached the right side of array
+ // must now sort the right partition
+ if (hi + 1 < hi0) {
+ qsort(a, hi + 1, hi0, comp);
+ }
+ }
+
+ public static void qsort(int[] a, int lo0, int hi0, Comparator comp) {
+ // bail out if we're already done
+ if (hi0 <= lo0) {
+ return;
+ }
+
+ // if this is a two element list, do a simple sort on it
+ int t;
+ if (hi0 - lo0 == 1) {
+ // if they're not already sorted, swap them
+ if (comp.compare(a[hi0], a[lo0]) < 0) {
+ t = a[lo0];
+ a[lo0] = a[hi0];
+ a[hi0] = t;
+ }
+ return;
+ }
+
+ // the middle element in the array is our partitioning element
+ int mid = a[(lo0 + hi0) / 2];
+
+ // set up our partitioning boundaries
+ int lo = lo0 - 1, hi = hi0 + 1;
+
+ // loop through the array until indices cross
+ for (;;) {
+ // find the first element that is greater than or equal to
+ // the partition element starting from the left Index.
+ while (comp.compare(a[++lo], mid) < 0);
+
+ // find an element that is smaller than or equal to
+ // the partition element starting from the right Index.
+ while (comp.compare(mid, a[--hi]) < 0);
+
+ // swap the two elements or bail out of the loop
+ if (hi > lo) {
+ t = a[lo];
+ a[lo] = a[hi];
+ a[hi] = t;
+ } else {
+ break;
+ }
+ }
+
+ // if the right index has not reached the left side of array
+ // must now sort the left partition
+ if (lo0 < lo - 1) {
+ qsort(a, lo0, lo - 1, comp);
+ }
+
+ // if the left index has not reached the right side of array
+ // must now sort the right partition
+ if (hi + 1 < hi0) {
+ qsort(a, hi + 1, hi0, comp);
+ }
+ }
+
+ /**
+ * Merge sort
+ */
+ public static void msort(Object[] src, Object[] dest, Comparator comp){
+ msort(src, dest, 0, src.length - 1, comp);
+ }
+
+ /**
+ * Merge sort
+ *
+ * @param src Source array
+ * @param dest Destination array
+ * @param low Index of beginning element
+ * @param high Index of end element
+ * @param comp Comparator
+ */
+ public static void msort(Object[] src, Object[] dest, int low, int high,
+ Comparator comp) {
+ if(low < high) {
+ int center = (low + high) / 2;
+ msort(src, dest, low, center, comp);
+ msort(src, dest, center + 1, high, comp);
+ merge(src, dest, low, center + 1, high, comp);
+ }
+ }
+
+ private static void merge(Object[] src, Object[] dest,
+ int low, int middle, int high, Comparator comp) {
+ int leftEnd = middle - 1;
+ int pos = low;
+ int numElements = high - low + 1;
+
+ while (low <= leftEnd && middle <= high) {
+ if (comp.compare(src[low], src[middle]) <= 0) {
+ dest[pos++] = src[low++];
+ } else {
+ dest[pos++] = src[middle++];
+ }
+ }
+
+ while (low <= leftEnd) {
+ dest[pos++] = src[low++];
+ }
+
+ while (middle <= high) {
+ dest[pos++] = src[middle++];
+ }
+
+ for (int i = 0; i < numElements; i++, high--) {
+ src[high] = dest[high];
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java
new file mode 100644
index 0000000..88f6822
--- /dev/null
+++ b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java
@@ -0,0 +1,739 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.mesh.IndexBuffer;
+import static com.jme3.util.BufferUtils.*;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Lex (Aleksey Nikiforov)
+ */
+public class TangentBinormalGenerator {
+
+ private static final float ZERO_TOLERANCE = 0.0000001f;
+ private static final Logger log = Logger.getLogger(
+ TangentBinormalGenerator.class.getName());
+ private static float toleranceAngle;
+ private static float toleranceDot;
+
+ static {
+ setToleranceAngle(45);
+ }
+
+
+ private static class VertexInfo {
+ public final Vector3f position;
+ public final Vector3f normal;
+ public final ArrayList<Integer> indices = new ArrayList<Integer>();
+
+ public VertexInfo(Vector3f position, Vector3f normal) {
+ this.position = position;
+ this.normal = normal;
+ }
+ }
+
+ /** Collects all the triangle data for one vertex.
+ */
+ private static class VertexData {
+ public final ArrayList<TriangleData> triangles = new ArrayList<TriangleData>();
+
+ public VertexData() { }
+ }
+
+ /** Keeps track of tangent, binormal, and normal for one triangle.
+ */
+ public static class TriangleData {
+ public final Vector3f tangent;
+ public final Vector3f binormal;
+ public final Vector3f normal;
+
+ public TriangleData(Vector3f tangent, Vector3f binormal, Vector3f normal) {
+ this.tangent = tangent;
+ this.binormal = binormal;
+ this.normal = normal;
+ }
+ }
+
+ private static VertexData[] initVertexData(int size) {
+ VertexData[] vertices = new VertexData[size];
+ for (int i = 0; i < size; i++) {
+ vertices[i] = new VertexData();
+ }
+ return vertices;
+ }
+
+ public static void generate(Mesh mesh) {
+ generate(mesh, true);
+ }
+
+ public static void generate(Spatial scene) {
+ if (scene instanceof Node) {
+ Node node = (Node) scene;
+ for (Spatial child : node.getChildren()) {
+ generate(child);
+ }
+ } else {
+ Geometry geom = (Geometry) scene;
+ generate(geom.getMesh());
+ }
+ }
+
+ public static void generate(Mesh mesh, boolean approxTangents) {
+ int[] index = new int[3];
+ Vector3f[] v = new Vector3f[3];
+ Vector2f[] t = new Vector2f[3];
+ for (int i = 0; i < 3; i++) {
+ v[i] = new Vector3f();
+ t[i] = new Vector2f();
+ }
+
+ if (mesh.getBuffer(Type.Normal) == null) {
+ throw new IllegalArgumentException("The given mesh has no normal data!");
+ }
+
+ VertexData[] vertices;
+ switch (mesh.getMode()) {
+ case Triangles:
+ vertices = processTriangles(mesh, index, v, t);
+ break;
+ case TriangleStrip:
+ vertices = processTriangleStrip(mesh, index, v, t);
+ break;
+ case TriangleFan:
+ vertices = processTriangleFan(mesh, index, v, t);
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ mesh.getMode() + " is not supported.");
+ }
+
+ processTriangleData(mesh, vertices, approxTangents);
+
+ //if the mesh has a bind pose, we need to generate the bind pose for the tangent buffer
+ if (mesh.getBuffer(Type.BindPosePosition) != null) {
+
+ VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
+ if (tangents != null) {
+ VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent);
+ bindTangents.setupData(Usage.CpuOnly,
+ 4,
+ Format.Float,
+ BufferUtils.clone(tangents.getData()));
+
+ if (mesh.getBuffer(Type.BindPoseTangent) != null) {
+ mesh.clearBuffer(Type.BindPoseTangent);
+ }
+ mesh.setBuffer(bindTangents);
+ tangents.setUsage(Usage.Stream);
+ }
+ }
+ }
+
+ private static VertexData[] processTriangles(Mesh mesh,
+ int[] index, Vector3f[] v, Vector2f[] t) {
+ IndexBuffer indexBuffer = mesh.getIndexBuffer();
+ FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ if (mesh.getBuffer(Type.TexCoord) == null) {
+ throw new IllegalArgumentException("Can only generate tangents for "
+ + "meshes with texture coordinates");
+ }
+
+ FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
+
+ VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3);
+
+ for (int i = 0; i < indexBuffer.size() / 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ index[j] = indexBuffer.get(i * 3 + j);
+ populateFromBuffer(v[j], vertexBuffer, index[j]);
+ populateFromBuffer(t[j], textureBuffer, index[j]);
+ }
+
+ TriangleData triData = processTriangle(index, v, t);
+ if (triData != null) {
+ vertices[index[0]].triangles.add(triData);
+ vertices[index[1]].triangles.add(triData);
+ vertices[index[2]].triangles.add(triData);
+ }
+ }
+
+ return vertices;
+ }
+
+ private static VertexData[] processTriangleStrip(Mesh mesh,
+ int[] index, Vector3f[] v, Vector2f[] t) {
+ IndexBuffer indexBuffer = mesh.getIndexBuffer();
+ FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
+
+ VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3);
+
+ index[0] = indexBuffer.get(0);
+ index[1] = indexBuffer.get(1);
+
+ populateFromBuffer(v[0], vertexBuffer, index[0]);
+ populateFromBuffer(v[1], vertexBuffer, index[1]);
+
+ populateFromBuffer(t[0], textureBuffer, index[0]);
+ populateFromBuffer(t[1], textureBuffer, index[1]);
+
+ for (int i = 2; i < indexBuffer.size(); i++) {
+ index[2] = indexBuffer.get(i);
+ BufferUtils.populateFromBuffer(v[2], vertexBuffer, index[2]);
+ BufferUtils.populateFromBuffer(t[2], textureBuffer, index[2]);
+
+ boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]);
+ TriangleData triData = processTriangle(index, v, t);
+
+ if (triData != null && !isDegenerate) {
+ vertices[index[0]].triangles.add(triData);
+ vertices[index[1]].triangles.add(triData);
+ vertices[index[2]].triangles.add(triData);
+ }
+
+ Vector3f vTemp = v[0];
+ v[0] = v[1];
+ v[1] = v[2];
+ v[2] = vTemp;
+
+ Vector2f tTemp = t[0];
+ t[0] = t[1];
+ t[1] = t[2];
+ t[2] = tTemp;
+
+ index[0] = index[1];
+ index[1] = index[2];
+ }
+
+ return vertices;
+ }
+
+ private static VertexData[] processTriangleFan(Mesh mesh,
+ int[] index, Vector3f[] v, Vector2f[] t) {
+ IndexBuffer indexBuffer = mesh.getIndexBuffer();
+ FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
+
+ VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3);
+
+ index[0] = indexBuffer.get(0);
+ index[1] = indexBuffer.get(1);
+
+ populateFromBuffer(v[0], vertexBuffer, index[0]);
+ populateFromBuffer(v[1], vertexBuffer, index[1]);
+
+ populateFromBuffer(t[0], textureBuffer, index[0]);
+ populateFromBuffer(t[1], textureBuffer, index[1]);
+
+ for (int i = 2; i < vertexBuffer.capacity() / 3; i++) {
+ index[2] = indexBuffer.get(i);
+ populateFromBuffer(v[2], vertexBuffer, index[2]);
+ populateFromBuffer(t[2], textureBuffer, index[2]);
+
+ TriangleData triData = processTriangle(index, v, t);
+ if (triData != null) {
+ vertices[index[0]].triangles.add(triData);
+ vertices[index[1]].triangles.add(triData);
+ vertices[index[2]].triangles.add(triData);
+ }
+
+ Vector3f vTemp = v[1];
+ v[1] = v[2];
+ v[2] = vTemp;
+
+ Vector2f tTemp = t[1];
+ t[1] = t[2];
+ t[2] = tTemp;
+
+ index[1] = index[2];
+ }
+
+ return vertices;
+ }
+
+ // check if the area is greater than zero
+ private static boolean isDegenerateTriangle(Vector3f a, Vector3f b, Vector3f c) {
+ return (a.subtract(b).cross(c.subtract(b))).lengthSquared() == 0;
+ }
+
+ public static TriangleData processTriangle(int[] index,
+ Vector3f[] v, Vector2f[] t) {
+ Vector3f edge1 = new Vector3f();
+ Vector3f edge2 = new Vector3f();
+ Vector2f edge1uv = new Vector2f();
+ Vector2f edge2uv = new Vector2f();
+
+ Vector3f tangent = new Vector3f();
+ Vector3f binormal = new Vector3f();
+ Vector3f normal = new Vector3f();
+
+ t[1].subtract(t[0], edge1uv);
+ t[2].subtract(t[0], edge2uv);
+ float det = edge1uv.x * edge2uv.y - edge1uv.y * edge2uv.x;
+
+ boolean normalize = false;
+ if (Math.abs(det) < ZERO_TOLERANCE) {
+ log.log(Level.WARNING, "Colinear uv coordinates for triangle "
+ + "[{0}, {1}, {2}]; tex0 = [{3}, {4}], "
+ + "tex1 = [{5}, {6}], tex2 = [{7}, {8}]",
+ new Object[]{index[0], index[1], index[2],
+ t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y});
+ det = 1;
+ normalize = true;
+ }
+
+ v[1].subtract(v[0], edge1);
+ v[2].subtract(v[0], edge2);
+
+ tangent.set(edge1);
+ tangent.normalizeLocal();
+ binormal.set(edge2);
+ binormal.normalizeLocal();
+
+ if (Math.abs(Math.abs(tangent.dot(binormal)) - 1)
+ < ZERO_TOLERANCE) {
+ log.log(Level.WARNING, "Vertices are on the same line "
+ + "for triangle [{0}, {1}, {2}].",
+ new Object[]{index[0], index[1], index[2]});
+ }
+
+ float factor = 1 / det;
+ tangent.x = (edge2uv.y * edge1.x - edge1uv.y * edge2.x) * factor;
+ tangent.y = (edge2uv.y * edge1.y - edge1uv.y * edge2.y) * factor;
+ tangent.z = (edge2uv.y * edge1.z - edge1uv.y * edge2.z) * factor;
+ if (normalize) {
+ tangent.normalizeLocal();
+ }
+
+ binormal.x = (edge1uv.x * edge2.x - edge2uv.x * edge1.x) * factor;
+ binormal.y = (edge1uv.x * edge2.y - edge2uv.x * edge1.y) * factor;
+ binormal.z = (edge1uv.x * edge2.z - edge2uv.x * edge1.z) * factor;
+ if (normalize) {
+ binormal.normalizeLocal();
+ }
+
+ tangent.cross(binormal, normal);
+ normal.normalizeLocal();
+
+ return new TriangleData(
+ tangent,
+ binormal,
+ normal);
+ }
+
+ public static void setToleranceAngle(float angle) {
+ if (angle < 0 || angle > 179) {
+ throw new IllegalArgumentException(
+ "The angle must be between 0 and 179 degrees.");
+ }
+ toleranceDot = FastMath.cos(angle * FastMath.DEG_TO_RAD);
+ toleranceAngle = angle;
+ }
+
+
+ private static boolean approxEqual(Vector3f u, Vector3f v) {
+ float tolerance = 1E-4f;
+ return (FastMath.abs(u.x - v.x) < tolerance) &&
+ (FastMath.abs(u.y - v.y) < tolerance) &&
+ (FastMath.abs(u.z - v.z) < tolerance);
+ }
+
+ private static ArrayList<VertexInfo> linkVertices(Mesh mesh) {
+ ArrayList<VertexInfo> vertexMap = new ArrayList<VertexInfo>();
+
+ FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
+
+ Vector3f position = new Vector3f();
+ Vector3f normal = new Vector3f();
+
+ final int size = vertexBuffer.capacity() / 3;
+ for (int i = 0; i < size; i++) {
+
+ populateFromBuffer(position, vertexBuffer, i);
+ populateFromBuffer(normal, normalBuffer, i);
+
+ boolean found = false;
+
+ for (int j = 0; j < vertexMap.size(); j++) {
+ VertexInfo vertexInfo = vertexMap.get(j);
+ if (approxEqual(vertexInfo.position, position) &&
+ approxEqual(vertexInfo.normal, normal))
+ {
+ vertexInfo.indices.add(i);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ VertexInfo vertexInfo = new VertexInfo(position.clone(), normal.clone());
+ vertexInfo.indices.add(i);
+ vertexMap.add(vertexInfo);
+ }
+ }
+
+ return vertexMap;
+ }
+
+ private static void processTriangleData(Mesh mesh, VertexData[] vertices,
+ boolean approxTangent)
+ {
+ ArrayList<VertexInfo> vertexMap = linkVertices(mesh);
+
+ FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
+
+ FloatBuffer tangents = BufferUtils.createFloatBuffer(vertices.length * 4);
+// FloatBuffer binormals = BufferUtils.createFloatBuffer(vertices.length * 3);
+
+ Vector3f tangent = new Vector3f();
+ Vector3f binormal = new Vector3f();
+ Vector3f normal = new Vector3f();
+ Vector3f givenNormal = new Vector3f();
+
+ Vector3f tangentUnit = new Vector3f();
+ Vector3f binormalUnit = new Vector3f();
+
+ for (int k = 0; k < vertexMap.size(); k++) {
+ float wCoord = -1;
+
+ VertexInfo vertexInfo = vertexMap.get(k);
+
+ givenNormal.set(vertexInfo.normal);
+ givenNormal.normalizeLocal();
+
+ TriangleData firstTriangle = vertices[vertexInfo.indices.get(0)].triangles.get(0);
+
+ // check tangent and binormal consistency
+ tangent.set(firstTriangle.tangent);
+ tangent.normalizeLocal();
+ binormal.set(firstTriangle.binormal);
+ binormal.normalizeLocal();
+
+ for (int i : vertexInfo.indices) {
+ ArrayList<TriangleData> triangles = vertices[i].triangles;
+
+ for (int j = 0; j < triangles.size(); j++) {
+ TriangleData triangleData = triangles.get(j);
+
+ tangentUnit.set(triangleData.tangent);
+ tangentUnit.normalizeLocal();
+ if (tangent.dot(tangentUnit) < toleranceDot) {
+ log.log(Level.WARNING,
+ "Angle between tangents exceeds tolerance "
+ + "for vertex {0}.", i);
+ break;
+ }
+
+ if (!approxTangent) {
+ binormalUnit.set(triangleData.binormal);
+ binormalUnit.normalizeLocal();
+ if (binormal.dot(binormalUnit) < toleranceDot) {
+ log.log(Level.WARNING,
+ "Angle between binormals exceeds tolerance "
+ + "for vertex {0}.", i);
+ break;
+ }
+ }
+ }
+ }
+
+
+ // find average tangent
+ tangent.set(0, 0, 0);
+ binormal.set(0, 0, 0);
+
+ int triangleCount = 0;
+ for (int i : vertexInfo.indices) {
+ ArrayList<TriangleData> triangles = vertices[i].triangles;
+ triangleCount += triangles.size();
+
+ boolean flippedNormal = false;
+ for (int j = 0; j < triangles.size(); j++) {
+ TriangleData triangleData = triangles.get(j);
+ tangent.addLocal(triangleData.tangent);
+ binormal.addLocal(triangleData.binormal);
+
+ if (givenNormal.dot(triangleData.normal) < 0) {
+ flippedNormal = true;
+ }
+ }
+ if (flippedNormal /*&& approxTangent*/) {
+ // Generated normal is flipped for this vertex,
+ // so binormal = normal.cross(tangent) will be flipped in the shader
+ // log.log(Level.WARNING,
+ // "Binormal is flipped for vertex {0}.", i);
+
+ wCoord = 1;
+ }
+ }
+
+
+ int blameVertex = vertexInfo.indices.get(0);
+
+ if (tangent.length() < ZERO_TOLERANCE) {
+ log.log(Level.WARNING,
+ "Shared tangent is zero for vertex {0}.", blameVertex);
+ // attempt to fix from binormal
+ if (binormal.length() >= ZERO_TOLERANCE) {
+ binormal.cross(givenNormal, tangent);
+ tangent.normalizeLocal();
+ } // if all fails use the tangent from the first triangle
+ else {
+ tangent.set(firstTriangle.tangent);
+ }
+ } else {
+ tangent.divideLocal(triangleCount);
+ }
+
+ tangentUnit.set(tangent);
+ tangentUnit.normalizeLocal();
+ if (Math.abs(Math.abs(tangentUnit.dot(givenNormal)) - 1)
+ < ZERO_TOLERANCE) {
+ log.log(Level.WARNING,
+ "Normal and tangent are parallel for vertex {0}.", blameVertex);
+ }
+
+
+ if (!approxTangent) {
+ if (binormal.length() < ZERO_TOLERANCE) {
+ log.log(Level.WARNING,
+ "Shared binormal is zero for vertex {0}.", blameVertex);
+ // attempt to fix from tangent
+ if (tangent.length() >= ZERO_TOLERANCE) {
+ givenNormal.cross(tangent, binormal);
+ binormal.normalizeLocal();
+ } // if all fails use the binormal from the first triangle
+ else {
+ binormal.set(firstTriangle.binormal);
+ }
+ } else {
+ binormal.divideLocal(triangleCount);
+ }
+
+ binormalUnit.set(binormal);
+ binormalUnit.normalizeLocal();
+ if (Math.abs(Math.abs(binormalUnit.dot(givenNormal)) - 1)
+ < ZERO_TOLERANCE) {
+ log.log(Level.WARNING,
+ "Normal and binormal are parallel for vertex {0}.", blameVertex);
+ }
+
+ if (Math.abs(Math.abs(binormalUnit.dot(tangentUnit)) - 1)
+ < ZERO_TOLERANCE) {
+ log.log(Level.WARNING,
+ "Tangent and binormal are parallel for vertex {0}.", blameVertex);
+ }
+ }
+
+ for (int i : vertexInfo.indices) {
+ if (approxTangent) {
+ // This calculation ensures that normal and tagent have a 90 degree angle.
+ // Removing this will lead to visual artifacts.
+ givenNormal.cross(tangent, binormal);
+ binormal.cross(givenNormal, tangent);
+
+ tangent.normalizeLocal();
+
+ tangents.put((i * 4), tangent.x);
+ tangents.put((i * 4) + 1, tangent.y);
+ tangents.put((i * 4) + 2, tangent.z);
+ tangents.put((i * 4) + 3, wCoord);
+ } else {
+ tangents.put((i * 4), tangent.x);
+ tangents.put((i * 4) + 1, tangent.y);
+ tangents.put((i * 4) + 2, tangent.z);
+ tangents.put((i * 4) + 3, wCoord);
+
+ //setInBuffer(binormal, binormals, i);
+ }
+ }
+ }
+
+ mesh.setBuffer(Type.Tangent, 4, tangents);
+// if (!approxTangent) mesh.setBuffer(Type.Binormal, 3, binormals);
+ }
+
+ public static Mesh genTbnLines(Mesh mesh, float scale) {
+ if (mesh.getBuffer(Type.Tangent) == null) {
+ return genNormalLines(mesh, scale);
+ } else {
+ return genTangentLines(mesh, scale);
+ }
+ }
+
+ public static Mesh genNormalLines(Mesh mesh, float scale) {
+ FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
+
+ ColorRGBA originColor = ColorRGBA.White;
+ ColorRGBA normalColor = ColorRGBA.Blue;
+
+ Mesh lineMesh = new Mesh();
+ lineMesh.setMode(Mesh.Mode.Lines);
+
+ Vector3f origin = new Vector3f();
+ Vector3f point = new Vector3f();
+
+ FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.capacity() * 2);
+ FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.capacity() / 3 * 4 * 2);
+
+ for (int i = 0; i < vertexBuffer.capacity() / 3; i++) {
+ populateFromBuffer(origin, vertexBuffer, i);
+ populateFromBuffer(point, normalBuffer, i);
+
+ int index = i * 2;
+
+ setInBuffer(origin, lineVertex, index);
+ setInBuffer(originColor, lineColor, index);
+
+ point.multLocal(scale);
+ point.addLocal(origin);
+ setInBuffer(point, lineVertex, index + 1);
+ setInBuffer(normalColor, lineColor, index + 1);
+ }
+
+ lineMesh.setBuffer(Type.Position, 3, lineVertex);
+ lineMesh.setBuffer(Type.Color, 4, lineColor);
+
+ lineMesh.setStatic();
+ lineMesh.setInterleaved();
+ return lineMesh;
+ }
+
+ private static Mesh genTangentLines(Mesh mesh, float scale) {
+ FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
+ FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
+ FloatBuffer tangentBuffer = (FloatBuffer) mesh.getBuffer(Type.Tangent).getData();
+
+ FloatBuffer binormalBuffer = null;
+ if (mesh.getBuffer(Type.Binormal) != null) {
+ binormalBuffer = (FloatBuffer) mesh.getBuffer(Type.Binormal).getData();
+ }
+
+ ColorRGBA originColor = ColorRGBA.White;
+ ColorRGBA tangentColor = ColorRGBA.Red;
+ ColorRGBA binormalColor = ColorRGBA.Green;
+ ColorRGBA normalColor = ColorRGBA.Blue;
+
+ Mesh lineMesh = new Mesh();
+ lineMesh.setMode(Mesh.Mode.Lines);
+
+ Vector3f origin = new Vector3f();
+ Vector3f point = new Vector3f();
+ Vector3f tangent = new Vector3f();
+ Vector3f normal = new Vector3f();
+
+ IntBuffer lineIndex = BufferUtils.createIntBuffer(vertexBuffer.capacity() / 3 * 6);
+ FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.capacity() * 4);
+ FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.capacity() / 3 * 4 * 4);
+
+ boolean hasParity = mesh.getBuffer(Type.Tangent).getNumComponents() == 4;
+ float tangentW = 1;
+
+ for (int i = 0; i < vertexBuffer.capacity() / 3; i++) {
+ populateFromBuffer(origin, vertexBuffer, i);
+ populateFromBuffer(normal, normalBuffer, i);
+
+ if (hasParity) {
+ tangent.x = tangentBuffer.get(i * 4);
+ tangent.y = tangentBuffer.get(i * 4 + 1);
+ tangent.z = tangentBuffer.get(i * 4 + 2);
+ tangentW = tangentBuffer.get(i * 4 + 3);
+ } else {
+ populateFromBuffer(tangent, tangentBuffer, i);
+ }
+
+ int index = i * 4;
+
+ int id = i * 6;
+ lineIndex.put(id, index);
+ lineIndex.put(id + 1, index + 1);
+ lineIndex.put(id + 2, index);
+ lineIndex.put(id + 3, index + 2);
+ lineIndex.put(id + 4, index);
+ lineIndex.put(id + 5, index + 3);
+
+ setInBuffer(origin, lineVertex, index);
+ setInBuffer(originColor, lineColor, index);
+
+ point.set(tangent);
+ point.multLocal(scale);
+ point.addLocal(origin);
+ setInBuffer(point, lineVertex, index + 1);
+ setInBuffer(tangentColor, lineColor, index + 1);
+
+ // wvBinormal = cross(wvNormal, wvTangent) * -inTangent.w
+
+ if (binormalBuffer == null) {
+ normal.cross(tangent, point);
+ point.multLocal(-tangentW);
+ point.normalizeLocal();
+ } else {
+ populateFromBuffer(point, binormalBuffer, i);
+ }
+
+ point.multLocal(scale);
+ point.addLocal(origin);
+ setInBuffer(point, lineVertex, index + 2);
+ setInBuffer(binormalColor, lineColor, index + 2);
+
+ point.set(normal);
+ point.multLocal(scale);
+ point.addLocal(origin);
+ setInBuffer(point, lineVertex, index + 3);
+ setInBuffer(normalColor, lineColor, index + 3);
+ }
+
+ lineMesh.setBuffer(Type.Index, 1, lineIndex);
+ lineMesh.setBuffer(Type.Position, 3, lineVertex);
+ lineMesh.setBuffer(Type.Color, 4, lineColor);
+
+ lineMesh.setStatic();
+ lineMesh.setInterleaved();
+ return lineMesh;
+ }
+}
diff --git a/engine/src/core/com/jme3/util/TempVars.java b/engine/src/core/com/jme3/util/TempVars.java
new file mode 100644
index 0000000..2fdea36
--- /dev/null
+++ b/engine/src/core/com/jme3/util/TempVars.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import com.jme3.collision.bih.BIHNode.BIHStackData;
+import com.jme3.math.*;
+import com.jme3.scene.Spatial;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+
+/**
+ * Temporary variables assigned to each thread. Engine classes may access
+ * these temp variables with TempVars.get(), all retrieved TempVars
+ * instances must be returned via TempVars.release().
+ * This returns an available instance of the TempVar class ensuring this
+ * particular instance is never used elsewhere in the mean time.
+ */
+public class TempVars {
+
+ /**
+ * Allow X instances of TempVars in a single thread.
+ */
+ private static final int STACK_SIZE = 5;
+
+ /**
+ * <code>TempVarsStack</code> contains a stack of TempVars.
+ * Every time TempVars.get() is called, a new entry is added to the stack,
+ * and the index incremented.
+ * When TempVars.release() is called, the entry is checked against
+ * the current instance and then the index is decremented.
+ */
+ private static class TempVarsStack {
+
+ int index = 0;
+ TempVars[] tempVars = new TempVars[STACK_SIZE];
+ }
+ /**
+ * ThreadLocal to store a TempVarsStack for each thread.
+ * This ensures each thread has a single TempVarsStack that is
+ * used only in method calls in that thread.
+ */
+ private static final ThreadLocal<TempVarsStack> varsLocal = new ThreadLocal<TempVarsStack>() {
+
+ @Override
+ public TempVarsStack initialValue() {
+ return new TempVarsStack();
+ }
+ };
+ /**
+ * This instance of TempVars has been retrieved but not released yet.
+ */
+ private boolean isUsed = false;
+
+ private TempVars() {
+ }
+
+ /**
+ * Acquire an instance of the TempVar class.
+ * You have to release the instance after use by calling the
+ * release() method.
+ * If more than STACK_SIZE (currently 5) instances are requested
+ * in a single thread then an ArrayIndexOutOfBoundsException will be thrown.
+ *
+ * @return A TempVar instance
+ */
+ public static TempVars get() {
+ TempVarsStack stack = varsLocal.get();
+
+ TempVars instance = stack.tempVars[stack.index];
+
+ if (instance == null) {
+ // Create new
+ instance = new TempVars();
+
+ // Put it in there
+ stack.tempVars[stack.index] = instance;
+ }
+
+ stack.index++;
+
+ instance.isUsed = true;
+
+ return instance;
+ }
+
+ /**
+ * Releases this instance of TempVars.
+ * Once released, the contents of the TempVars are undefined.
+ * The TempVars must be released in the opposite order that they are retrieved,
+ * e.g. Acquiring vars1, then acquiring vars2, vars2 MUST be released
+ * first otherwise an exception will be thrown.
+ */
+ public void release() {
+ if (!isUsed) {
+ throw new IllegalStateException("This instance of TempVars was already released!");
+ }
+
+ isUsed = false;
+
+ TempVarsStack stack = varsLocal.get();
+
+ // Return it to the stack
+ stack.index--;
+
+ // Check if it is actually there
+ if (stack.tempVars[stack.index] != this) {
+ throw new IllegalStateException("An instance of TempVars has not been released in a called method!");
+ }
+ }
+ /**
+ * For interfacing with OpenGL in Renderer.
+ */
+ public final IntBuffer intBuffer1 = BufferUtils.createIntBuffer(1);
+ public final IntBuffer intBuffer16 = BufferUtils.createIntBuffer(16);
+ public final FloatBuffer floatBuffer16 = BufferUtils.createFloatBuffer(16);
+ /**
+ * Skinning buffers
+ */
+ public final float[] skinPositions = new float[512 * 3];
+ public final float[] skinNormals = new float[512 * 3];
+ //tangent buffer as 4 components by elements
+ public final float[] skinTangents = new float[512 * 4];
+ /**
+ * Fetching triangle from mesh
+ */
+ public final Triangle triangle = new Triangle();
+ /**
+ * Color
+ */
+ public final ColorRGBA color = new ColorRGBA();
+ /**
+ * General vectors.
+ */
+ public final Vector3f vect1 = new Vector3f();
+ public final Vector3f vect2 = new Vector3f();
+ public final Vector3f vect3 = new Vector3f();
+ public final Vector3f vect4 = new Vector3f();
+ public final Vector3f vect5 = new Vector3f();
+ public final Vector3f vect6 = new Vector3f();
+ public final Vector3f vect7 = new Vector3f();
+ //seems the maximum number of vector used is 7 in com.jme3.bounding.java
+ public final Vector3f vect8 = new Vector3f();
+ public final Vector3f vect9 = new Vector3f();
+ public final Vector3f vect10 = new Vector3f();
+ public final Vector4f vect4f = new Vector4f();
+ public final Vector3f[] tri = {new Vector3f(),
+ new Vector3f(),
+ new Vector3f()};
+ /**
+ * 2D vector
+ */
+ public final Vector2f vect2d = new Vector2f();
+ public final Vector2f vect2d2 = new Vector2f();
+ /**
+ * General matrices.
+ */
+ public final Matrix3f tempMat3 = new Matrix3f();
+ public final Matrix4f tempMat4 = new Matrix4f();
+ public final Matrix4f tempMat42 = new Matrix4f();
+ /**
+ * General quaternions.
+ */
+ public final Quaternion quat1 = new Quaternion();
+ public final Quaternion quat2 = new Quaternion();
+ /**
+ * Eigen
+ */
+ public final Eigen3f eigen = new Eigen3f();
+ /**
+ * Plane
+ */
+ public final Plane plane = new Plane();
+ /**
+ * BoundingBox ray collision
+ */
+ public final float[] fWdU = new float[3];
+ public final float[] fAWdU = new float[3];
+ public final float[] fDdU = new float[3];
+ public final float[] fADdU = new float[3];
+ public final float[] fAWxDdU = new float[3];
+ /**
+ * Maximum tree depth .. 32 levels??
+ */
+ public final Spatial[] spatialStack = new Spatial[32];
+ public final float[] matrixWrite = new float[16];
+ /**
+ * BIHTree
+ */
+ public final float[] bihSwapTmp = new float[9];
+ public final ArrayList<BIHStackData> bihStack = new ArrayList<BIHStackData>();
+}
diff --git a/engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java b/engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java
new file mode 100644
index 0000000..6dfa6b8
--- /dev/null
+++ b/engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java
@@ -0,0 +1,92 @@
+package com.jme3.util.blockparser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BlockLanguageParser {
+
+ private Reader reader;
+ private ArrayList<Statement> statementStack = new ArrayList<Statement>();
+ private Statement lastStatement;
+ private int lineNumber = 1;
+
+ private BlockLanguageParser(){
+ }
+
+ private void reset(){
+ statementStack.clear();
+ statementStack.add(new Statement(0, "<root>"));
+ lastStatement = null;
+ lineNumber = 1;
+ }
+
+ private void pushStatement(StringBuilder buffer){
+ String content = buffer.toString().trim();
+ if (content.length() > 0){
+ // push last statement onto the list
+ lastStatement = new Statement(lineNumber, content);
+
+ Statement parent = statementStack.get(statementStack.size()-1);
+ parent.addStatement(lastStatement);
+
+ buffer.setLength(0);
+ }
+ }
+
+ private void load(InputStream in) throws IOException{
+ reset();
+
+ reader = new InputStreamReader(in);
+
+ StringBuilder buffer = new StringBuilder();
+ boolean insideComment = false;
+ char lastChar = '\0';
+
+ while (true){
+ int ci = reader.read();
+ char c = (char) ci;
+ if (c == '\r'){
+ continue;
+ }
+ if (insideComment && c == '\n'){
+ insideComment = false;
+ }else if (c == '/' && lastChar == '/'){
+ buffer.deleteCharAt(buffer.length()-1);
+ insideComment = true;
+ pushStatement(buffer);
+ lastChar = '\0';
+ }else if (!insideComment){
+ if (ci == -1 || c == '{' || c == '}' || c == '\n' || c == ';'){
+ pushStatement(buffer);
+ lastChar = '\0';
+ if (c == '{'){
+ // push last statement onto the stack
+ statementStack.add(lastStatement);
+ continue;
+ }else if (c == '}'){
+ // pop statement from stack
+ statementStack.remove(statementStack.size()-1);
+ continue;
+ }else if (c == '\n'){
+ lineNumber++;
+ }else if (ci == -1){
+ break;
+ }
+ }else{
+ buffer.append(c);
+ lastChar = c;
+ }
+ }
+ }
+ }
+
+ public static List<Statement> parse(InputStream in) throws IOException {
+ BlockLanguageParser parser = new BlockLanguageParser();
+ parser.load(in);
+ return parser.statementStack.get(0).getContents();
+ }
+}
diff --git a/engine/src/core/com/jme3/util/blockparser/Statement.java b/engine/src/core/com/jme3/util/blockparser/Statement.java
new file mode 100644
index 0000000..d1309ad
--- /dev/null
+++ b/engine/src/core/com/jme3/util/blockparser/Statement.java
@@ -0,0 +1,61 @@
+package com.jme3.util.blockparser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Statement {
+
+ private int lineNumber;
+ private String line;
+ private List<Statement> contents = new ArrayList<Statement>();
+
+ Statement(int lineNumber, String line) {
+ this.lineNumber = lineNumber;
+ this.line = line;
+ }
+
+ void addStatement(Statement statement){
+// if (contents == null){
+// contents = new ArrayList<Statement>();
+// }
+ contents.add(statement);
+ }
+
+ public int getLineNumber(){
+ return lineNumber;
+ }
+
+ public String getLine() {
+ return line;
+ }
+
+ public List<Statement> getContents() {
+ return contents;
+ }
+
+ private String getIndent(int indent){
+ return " ".substring(0, indent);
+ }
+
+ private String toString(int indent){
+ StringBuilder sb = new StringBuilder();
+ sb.append(getIndent(indent));
+ sb.append(line);
+ if (contents != null){
+ sb.append(" {\n");
+ for (Statement statement : contents){
+ sb.append(statement.toString(indent+4));
+ sb.append("\n");
+ }
+ sb.append(getIndent(indent));
+ sb.append("}");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString(){
+ return toString(0);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/util/xml/SAXUtil.java b/engine/src/core/com/jme3/util/xml/SAXUtil.java
new file mode 100644
index 0000000..1ac4936
--- /dev/null
+++ b/engine/src/core/com/jme3/util/xml/SAXUtil.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util.xml;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * Utility methods for parsing XML data using SAX.
+ */
+public final class SAXUtil {
+
+ /**
+ * Parses an integer from a string, if the string is null returns
+ * def.
+ *
+ * @param i
+ * @param def
+ * @return
+ * @throws SAXException
+ */
+ public static int parseInt(String i, int def) throws SAXException{
+ if (i == null)
+ return def;
+ else{
+ try {
+ return Integer.parseInt(i);
+ } catch (NumberFormatException ex){
+ throw new SAXException("Expected an integer, got '"+i+"'");
+ }
+ }
+ }
+
+ public static int parseInt(String i) throws SAXException{
+ if (i == null)
+ throw new SAXException("Expected an integer");
+ else{
+ try {
+ return Integer.parseInt(i);
+ } catch (NumberFormatException ex){
+ throw new SAXException("Expected an integer, got '"+i+"'");
+ }
+ }
+ }
+
+ public static float parseFloat(String f, float def) throws SAXException{
+ if (f == null)
+ return def;
+ else{
+ try {
+ return Float.parseFloat(f);
+ } catch (NumberFormatException ex){
+ throw new SAXException("Expected a decimal, got '"+f+"'");
+ }
+ }
+ }
+
+ public static float parseFloat(String f) throws SAXException{
+ if (f == null)
+ throw new SAXException("Expected a decimal");
+ else{
+ try {
+ return Float.parseFloat(f);
+ } catch (NumberFormatException ex){
+ throw new SAXException("Expected a decimal, got '"+f+"'");
+ }
+ }
+ }
+
+ public static boolean parseBool(String bool, boolean def) throws SAXException{
+ if (bool == null || bool.equals(""))
+ return def;
+ else
+ return Boolean.valueOf(bool);
+ //else
+ //else
+ // throw new SAXException("Expected a boolean, got'"+bool+"'");
+ }
+
+ public static String parseString(String str, String def){
+ if (str == null)
+ return def;
+ else
+ return str;
+ }
+
+ public static String parseString(String str) throws SAXException{
+ if (str == null)
+ throw new SAXException("Expected a string");
+ else
+ return str;
+ }
+
+ public static Vector3f parseVector3(Attributes attribs) throws SAXException{
+ float x = parseFloat(attribs.getValue("x"));
+ float y = parseFloat(attribs.getValue("y"));
+ float z = parseFloat(attribs.getValue("z"));
+ return new Vector3f(x,y,z);
+ }
+
+ public static ColorRGBA parseColor(Attributes attribs) throws SAXException{
+ float r = parseFloat(attribs.getValue("r"));
+ float g = parseFloat(attribs.getValue("g"));
+ float b = parseFloat(attribs.getValue("b"));
+ return new ColorRGBA(r, g, b, 1f);
+ }
+
+}
diff --git a/engine/src/desktop/com/jme3/app/AppletHarness.java b/engine/src/desktop/com/jme3/app/AppletHarness.java
new file mode 100644
index 0000000..7e14c39
--- /dev/null
+++ b/engine/src/desktop/com/jme3/app/AppletHarness.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeSystem;
+import java.applet.Applet;
+import java.awt.Canvas;
+import java.awt.Graphics;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+/**
+ * @author Kirill Vainer
+ */
+public class AppletHarness extends Applet {
+
+ public static final HashMap<Application, Applet> appToApplet
+ = new HashMap<Application, Applet>();
+
+ private JmeCanvasContext context;
+ private Canvas canvas;
+ private Application app;
+
+ private String appClass;
+ private URL appCfg = null;
+ private URL assetCfg = null;
+
+ public static Applet getApplet(Application app){
+ return appToApplet.get(app);
+ }
+
+ private void createCanvas(){
+ AppSettings settings = new AppSettings(true);
+
+ // load app cfg
+ if (appCfg != null){
+ InputStream in = null;
+ try {
+ in = appCfg.openStream();
+ settings.load(in);
+ in.close();
+ } catch (IOException ex){
+ // Called before application has been created ....
+ // Display error message through AWT
+ JOptionPane.showMessageDialog(this, "An error has occured while "
+ + "loading applet configuration"
+ + ex.getMessage(),
+ "jME3 Applet",
+ JOptionPane.ERROR_MESSAGE);
+ ex.printStackTrace();
+ } finally {
+ if (in != null)
+ try {
+ in.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+
+ if (assetCfg != null){
+ settings.putString("AssetConfigURL", assetCfg.toString());
+ }
+
+ settings.setWidth(getWidth());
+ settings.setHeight(getHeight());
+
+ JmeSystem.setLowPermissions(true);
+
+ try{
+ Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }catch (ClassNotFoundException ex){
+ ex.printStackTrace();
+ }catch (InstantiationException ex){
+ ex.printStackTrace();
+ }catch (IllegalAccessException ex){
+ ex.printStackTrace();
+ }
+
+ appToApplet.put(app, this);
+ app.setSettings(settings);
+ app.createCanvas();
+
+ context = (JmeCanvasContext) app.getContext();
+ canvas = context.getCanvas();
+ canvas.setSize(getWidth(), getHeight());
+
+ add(canvas);
+ app.startCanvas();
+ }
+
+ @Override
+ public final void update(Graphics g) {
+ canvas.setSize(getWidth(), getHeight());
+ }
+
+ @Override
+ public void init(){
+ appClass = getParameter("AppClass");
+ if (appClass == null)
+ throw new RuntimeException("The required parameter AppClass isn't specified!");
+
+ try {
+ appCfg = new URL(getParameter("AppSettingsURL"));
+ } catch (MalformedURLException ex) {
+ System.out.println(ex.getMessage());
+ appCfg = null;
+ }
+
+ try {
+ assetCfg = new URL(getParameter("AssetConfigURL"));
+ } catch (MalformedURLException ex){
+ System.out.println(ex.getMessage());
+ assetCfg = getClass().getResource("/com/jme3/asset/Desktop.cfg");
+ }
+
+ createCanvas();
+ System.out.println("applet:init");
+ }
+
+ @Override
+ public void start(){
+ context.setAutoFlushFrames(true);
+ System.out.println("applet:start");
+ }
+
+ @Override
+ public void stop(){
+ context.setAutoFlushFrames(false);
+ System.out.println("applet:stop");
+ }
+
+ @Override
+ public void destroy(){
+ System.out.println("applet:destroyStart");
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ removeAll();
+ System.out.println("applet:destroyRemoved");
+ }
+ });
+ app.stop(true);
+ System.out.println("applet:destroyDone");
+
+ appToApplet.remove(app);
+ }
+
+}
diff --git a/engine/src/desktop/com/jme3/app/Monkey.png b/engine/src/desktop/com/jme3/app/Monkey.png
new file mode 100644
index 0000000..e1c8c3d
--- /dev/null
+++ b/engine/src/desktop/com/jme3/app/Monkey.png
Binary files differ
diff --git a/engine/src/desktop/com/jme3/app/SettingsDialog.java b/engine/src/desktop/com/jme3/app/SettingsDialog.java
new file mode 100644
index 0000000..4402604
--- /dev/null
+++ b/engine/src/desktop/com/jme3/app/SettingsDialog.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import com.jme3.system.AppSettings;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.BackingStoreException;
+import javax.swing.*;
+
+/**
+ * <code>PropertiesDialog</code> provides an interface to make use of the
+ * <code>GameSettings</code> class. The <code>GameSettings</code> object
+ * is still created by the client application, and passed during construction.
+ *
+ * @see com.jme.system.GameSettings
+ * @author Mark Powell
+ * @author Eric Woroshow
+ * @author Joshua Slack - reworked for proper use of GL commands.
+ * @version $Id: LWJGLPropertiesDialog.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ */
+public final class SettingsDialog extends JDialog {
+
+ public static interface SelectionListener {
+
+ public void onSelection(int selection);
+ }
+ private static final Logger logger = Logger.getLogger(SettingsDialog.class.getName());
+ private static final long serialVersionUID = 1L;
+ public static final int NO_SELECTION = 0,
+ APPROVE_SELECTION = 1,
+ CANCEL_SELECTION = 2;
+ // connection to properties file.
+ private final AppSettings source;
+ // Title Image
+ private URL imageFile = null;
+ // Array of supported display modes
+ private DisplayMode[] modes = null;
+ // Array of windowed resolutions
+ private String[] windowedResolutions = {"320 x 240", "640 x 480", "800 x 600",
+ "1024 x 768", "1152 x 864", "1280 x 720"};
+ // UI components
+ private JCheckBox vsyncBox = null;
+ private JCheckBox fullscreenBox = null;
+ private JComboBox displayResCombo = null;
+ private JComboBox colorDepthCombo = null;
+ private JComboBox displayFreqCombo = null;
+// private JComboBox rendererCombo = null;
+ private JComboBox antialiasCombo = null;
+ private JLabel icon = null;
+ private int selection = 0;
+ private SelectionListener selectionListener = null;
+
+ /**
+ * Constructor for the <code>PropertiesDialog</code>. Creates a
+ * properties dialog initialized for the primary display.
+ *
+ * @param source
+ * the <code>AppSettings</code> object to use for working with
+ * the properties file.
+ * @param imageFile
+ * the image file to use as the title of the dialog;
+ * <code>null</code> will result in to image being displayed
+ * @throws NullPointerException
+ * if the source is <code>null</code>
+ */
+ public SettingsDialog(AppSettings source, String imageFile, boolean loadSettings) {
+ this(source, getURL(imageFile), loadSettings);
+ }
+
+ /**
+ * Constructor for the <code>PropertiesDialog</code>. Creates a
+ * properties dialog initialized for the primary display.
+ *
+ * @param source
+ * the <code>GameSettings</code> object to use for working with
+ * the properties file.
+ * @param imageFile
+ * the image file to use as the title of the dialog;
+ * <code>null</code> will result in to image being displayed
+ * @param loadSettings
+ * @throws JmeException
+ * if the source is <code>null</code>
+ */
+ public SettingsDialog(AppSettings source, URL imageFile, boolean loadSettings) {
+ if (source == null) {
+ throw new NullPointerException("Settings source cannot be null");
+ }
+
+ this.source = source;
+ this.imageFile = imageFile;
+
+// setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
+ setModal(true);
+
+ AppSettings registrySettings = new AppSettings(true);
+
+ String appTitle;
+ if(source.getTitle()!=null){
+ appTitle = source.getTitle();
+ }else{
+ appTitle = registrySettings.getTitle();
+ }
+ try {
+ registrySettings.load(appTitle);
+ } catch (BackingStoreException ex) {
+ logger.log(Level.WARNING,
+ "Failed to load settings", ex);
+ }
+
+ if (loadSettings) {
+ source.copyFrom(registrySettings);
+ } else if(!registrySettings.isEmpty()) {
+ source.mergeFrom(registrySettings);
+ }
+
+ GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
+
+ modes = device.getDisplayModes();
+ Arrays.sort(modes, new DisplayModeSorter());
+
+ createUI();
+ }
+
+ public void setSelectionListener(SelectionListener sl) {
+ this.selectionListener = sl;
+ }
+
+ public int getUserSelection() {
+ return selection;
+ }
+
+ private void setUserSelection(int selection) {
+ this.selection = selection;
+ selectionListener.onSelection(selection);
+ }
+
+ /**
+ * <code>setImage</code> sets the background image of the dialog.
+ *
+ * @param image
+ * <code>String</code> representing the image file.
+ */
+ public void setImage(String image) {
+ try {
+ URL file = new URL("file:" + image);
+ setImage(file);
+ // We can safely ignore the exception - it just means that the user
+ // gave us a bogus file
+ } catch (MalformedURLException e) {
+ }
+ }
+
+ /**
+ * <code>setImage</code> sets the background image of this dialog.
+ *
+ * @param image
+ * <code>URL</code> pointing to the image file.
+ */
+ public void setImage(URL image) {
+ icon.setIcon(new ImageIcon(image));
+ pack(); // Resize to accomodate the new image
+ setLocationRelativeTo(null); // put in center
+ }
+
+ /**
+ * <code>showDialog</code> sets this dialog as visble, and brings it to
+ * the front.
+ */
+ public void showDialog() {
+ setLocationRelativeTo(null);
+ setVisible(true);
+ toFront();
+ }
+
+ /**
+ * <code>init</code> creates the components to use the dialog.
+ */
+ private void createUI() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ logger.warning("Could not set native look and feel.");
+ }
+
+ addWindowListener(new WindowAdapter() {
+
+ public void windowClosing(WindowEvent e) {
+ setUserSelection(CANCEL_SELECTION);
+ dispose();
+ }
+ });
+
+ if (source.getIcons() != null) {
+ safeSetIconImages( (List<BufferedImage>) Arrays.asList((BufferedImage[]) source.getIcons()) );
+ }
+
+ setTitle("Select Display Settings");
+
+ // The panels...
+ JPanel mainPanel = new JPanel();
+ JPanel centerPanel = new JPanel();
+ JPanel optionsPanel = new JPanel();
+ JPanel buttonPanel = new JPanel();
+ // The buttons...
+ JButton ok = new JButton("Ok");
+ JButton cancel = new JButton("Cancel");
+
+ icon = new JLabel(imageFile != null ? new ImageIcon(imageFile) : null);
+
+ mainPanel.setLayout(new BorderLayout());
+
+ centerPanel.setLayout(new BorderLayout());
+
+ KeyListener aListener = new KeyAdapter() {
+
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+ if (verifyAndSaveCurrentSelection()) {
+ setUserSelection(APPROVE_SELECTION);
+ dispose();
+ }
+ }
+ else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ setUserSelection(CANCEL_SELECTION);
+ dispose();
+ }
+ }
+ };
+
+ displayResCombo = setUpResolutionChooser();
+ displayResCombo.addKeyListener(aListener);
+ colorDepthCombo = new JComboBox();
+ colorDepthCombo.addKeyListener(aListener);
+ displayFreqCombo = new JComboBox();
+ displayFreqCombo.addKeyListener(aListener);
+ antialiasCombo = new JComboBox();
+ antialiasCombo.addKeyListener(aListener);
+ fullscreenBox = new JCheckBox("Fullscreen?");
+ fullscreenBox.setSelected(source.isFullscreen());
+ fullscreenBox.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ updateResolutionChoices();
+ }
+ });
+ vsyncBox = new JCheckBox("VSync?");
+ vsyncBox.setSelected(source.isVSync());
+// rendererCombo = setUpRendererChooser();
+// rendererCombo.addKeyListener(aListener);
+
+
+
+ updateResolutionChoices();
+ updateAntialiasChoices();
+ displayResCombo.setSelectedItem(source.getWidth() + " x " + source.getHeight());
+ colorDepthCombo.setSelectedItem(source.getBitsPerPixel() + " bpp");
+
+ optionsPanel.add(displayResCombo);
+ optionsPanel.add(colorDepthCombo);
+ optionsPanel.add(displayFreqCombo);
+ optionsPanel.add(antialiasCombo);
+ optionsPanel.add(fullscreenBox);
+ optionsPanel.add(vsyncBox);
+// optionsPanel.add(rendererCombo);
+
+ // Set the button action listeners. Cancel disposes without saving, OK
+ // saves.
+ ok.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ if (verifyAndSaveCurrentSelection()) {
+ setUserSelection(APPROVE_SELECTION);
+ dispose();
+ }
+ }
+ });
+
+ cancel.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ setUserSelection(CANCEL_SELECTION);
+ dispose();
+ }
+ });
+
+ buttonPanel.add(ok);
+ buttonPanel.add(cancel);
+
+ if (icon != null) {
+ centerPanel.add(icon, BorderLayout.NORTH);
+ }
+ centerPanel.add(optionsPanel, BorderLayout.SOUTH);
+
+ mainPanel.add(centerPanel, BorderLayout.CENTER);
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ this.getContentPane().add(mainPanel);
+
+ pack();
+ }
+
+ /* Access JDialog.setIconImages by reflection in case we're running on JRE < 1.6 */
+ private void safeSetIconImages(List<? extends Image> icons) {
+ try {
+ // Due to Java bug 6445278, we try to set icon on our shared owner frame first.
+ // Otherwise, our alt-tab icon will be the Java default under Windows.
+ Window owner = getOwner();
+ if (owner != null) {
+ Method setIconImages = owner.getClass().getMethod("setIconImages", List.class);
+ setIconImages.invoke(owner, icons);
+ return;
+ }
+
+ Method setIconImages = getClass().getMethod("setIconImages", List.class);
+ setIconImages.invoke(this, icons);
+ } catch (Exception e) {
+ return;
+ }
+ }
+
+ /**
+ * <code>verifyAndSaveCurrentSelection</code> first verifies that the
+ * display mode is valid for this system, and then saves the current
+ * selection as a properties.cfg file.
+ *
+ * @return if the selection is valid
+ */
+ private boolean verifyAndSaveCurrentSelection() {
+ String display = (String) displayResCombo.getSelectedItem();
+ boolean fullscreen = fullscreenBox.isSelected();
+ boolean vsync = vsyncBox.isSelected();
+
+ int width = Integer.parseInt(display.substring(0, display.indexOf(" x ")));
+ display = display.substring(display.indexOf(" x ") + 3);
+ int height = Integer.parseInt(display);
+
+ String depthString = (String) colorDepthCombo.getSelectedItem();
+ int depth = -1;
+ if (depthString.equals("???")) {
+ depth = 0;
+ } else {
+ depth = Integer.parseInt(depthString.substring(0, depthString.indexOf(' ')));
+ }
+
+ String freqString = (String) displayFreqCombo.getSelectedItem();
+ int freq = -1;
+ if (fullscreen) {
+ if (freqString.equals("???")) {
+ freq = 0;
+ } else {
+ freq = Integer.parseInt(freqString.substring(0, freqString.indexOf(' ')));
+ }
+ }
+
+ String aaString = (String) antialiasCombo.getSelectedItem();
+ int multisample = -1;
+ if (aaString.equals("Disabled")) {
+ multisample = 0;
+ } else {
+ multisample = Integer.parseInt(aaString.substring(0, aaString.indexOf('x')));
+ }
+
+ // FIXME: Does not work in Linux
+ /*
+ * if (!fullscreen) { //query the current bit depth of the desktop int
+ * curDepth = GraphicsEnvironment.getLocalGraphicsEnvironment()
+ * .getDefaultScreenDevice().getDisplayMode().getBitDepth(); if (depth >
+ * curDepth) { showError(this,"Cannot choose a higher bit depth in
+ * windowed " + "mode than your current desktop bit depth"); return
+ * false; } }
+ */
+
+ String renderer = "LWJGL-OpenGL2";//(String) rendererCombo.getSelectedItem();
+
+ boolean valid = false;
+
+ // test valid display mode when going full screen
+ if (!fullscreen) {
+ valid = true;
+ } else {
+ GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
+ valid = device.isFullScreenSupported();
+ }
+
+ if (valid) {
+ //use the GameSettings class to save it.
+ source.setWidth(width);
+ source.setHeight(height);
+ source.setBitsPerPixel(depth);
+ source.setFrequency(freq);
+ source.setFullscreen(fullscreen);
+ source.setVSync(vsync);
+ //source.setRenderer(renderer);
+ source.setSamples(multisample);
+
+ String appTitle = source.getTitle();
+
+ try {
+ source.save(appTitle);
+ } catch (BackingStoreException ex) {
+ logger.log(Level.WARNING,
+ "Failed to save setting changes", ex);
+ }
+ } else {
+ showError(
+ this,
+ "Your monitor claims to not support the display mode you've selected.\n"
+ + "The combination of bit depth and refresh rate is not supported.");
+ }
+
+ return valid;
+ }
+
+ /**
+ * <code>setUpChooser</code> retrieves all available display modes and
+ * places them in a <code>JComboBox</code>. The resolution specified by
+ * GameSettings is used as the default value.
+ *
+ * @return the combo box of display modes.
+ */
+ private JComboBox setUpResolutionChooser() {
+ String[] res = getResolutions(modes);
+ JComboBox resolutionBox = new JComboBox(res);
+
+ resolutionBox.setSelectedItem(source.getWidth() + " x "
+ + source.getHeight());
+ resolutionBox.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ updateDisplayChoices();
+ }
+ });
+
+ return resolutionBox;
+ }
+
+ /**
+ * <code>setUpRendererChooser</code> sets the list of available renderers.
+ * Data is obtained from the <code>DisplaySystem</code> class. The
+ * renderer specified by GameSettings is used as the default value.
+ *
+ * @return the list of renderers.
+ */
+ private JComboBox setUpRendererChooser() {
+ String modes[] = {"NULL", "JOGL-OpenGL1", "LWJGL-OpenGL2", "LWJGL-OpenGL3", "LWJGL-OpenGL3.1"};
+ JComboBox nameBox = new JComboBox(modes);
+ nameBox.setSelectedItem(source.getRenderer());
+ return nameBox;
+ }
+
+ /**
+ * <code>updateDisplayChoices</code> updates the available color depth and
+ * display frequency options to match the currently selected resolution.
+ */
+ private void updateDisplayChoices() {
+ if (!fullscreenBox.isSelected()) {
+ // don't run this function when changing windowed settings
+ return;
+ }
+ String resolution = (String) displayResCombo.getSelectedItem();
+ String colorDepth = (String) colorDepthCombo.getSelectedItem();
+ if (colorDepth == null) {
+ colorDepth = source.getBitsPerPixel() + " bpp";
+ }
+ String displayFreq = (String) displayFreqCombo.getSelectedItem();
+ if (displayFreq == null) {
+ displayFreq = source.getFrequency() + " Hz";
+ }
+
+ // grab available depths
+ String[] depths = getDepths(resolution, modes);
+ colorDepthCombo.setModel(new DefaultComboBoxModel(depths));
+ colorDepthCombo.setSelectedItem(colorDepth);
+ // grab available frequencies
+ String[] freqs = getFrequencies(resolution, modes);
+ displayFreqCombo.setModel(new DefaultComboBoxModel(freqs));
+ // Try to reset freq
+ displayFreqCombo.setSelectedItem(displayFreq);
+ }
+
+ /**
+ * <code>updateResolutionChoices</code> updates the available resolutions
+ * list to match the currently selected window mode (fullscreen or
+ * windowed). It then sets up a list of standard options (if windowed) or
+ * calls <code>updateDisplayChoices</code> (if fullscreen).
+ */
+ private void updateResolutionChoices() {
+ if (!fullscreenBox.isSelected()) {
+ displayResCombo.setModel(new DefaultComboBoxModel(
+ windowedResolutions));
+ colorDepthCombo.setModel(new DefaultComboBoxModel(new String[]{
+ "24 bpp", "16 bpp"}));
+ displayFreqCombo.setModel(new DefaultComboBoxModel(
+ new String[]{"n/a"}));
+ displayFreqCombo.setEnabled(false);
+ } else {
+ displayResCombo.setModel(new DefaultComboBoxModel(
+ getResolutions(modes)));
+ displayFreqCombo.setEnabled(true);
+ updateDisplayChoices();
+ }
+ }
+
+ private void updateAntialiasChoices() {
+ // maybe in the future will add support for determining this info
+ // through pbuffer
+ String[] choices = new String[]{"Disabled", "2x", "4x", "6x", "8x", "16x"};
+ antialiasCombo.setModel(new DefaultComboBoxModel(choices));
+ antialiasCombo.setSelectedItem(choices[Math.min(source.getSamples()/2,5)]);
+ }
+
+ //
+ // Utility methods
+ //
+ /**
+ * Utility method for converting a String denoting a file into a URL.
+ *
+ * @return a URL pointing to the file or null
+ */
+ private static URL getURL(String file) {
+ URL url = null;
+ try {
+ url = new URL("file:" + file);
+ } catch (MalformedURLException e) {
+ }
+ return url;
+ }
+
+ private static void showError(java.awt.Component parent, String message) {
+ JOptionPane.showMessageDialog(parent, message, "Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ /**
+ * Returns every unique resolution from an array of <code>DisplayMode</code>s.
+ */
+ private static String[] getResolutions(DisplayMode[] modes) {
+ ArrayList<String> resolutions = new ArrayList<String>(modes.length);
+ for (int i = 0; i < modes.length; i++) {
+ String res = modes[i].getWidth() + " x " + modes[i].getHeight();
+ if (!resolutions.contains(res)) {
+ resolutions.add(res);
+ }
+ }
+
+ String[] res = new String[resolutions.size()];
+ resolutions.toArray(res);
+ return res;
+ }
+
+ /**
+ * Returns every possible bit depth for the given resolution.
+ */
+ private static String[] getDepths(String resolution, DisplayMode[] modes) {
+ ArrayList<String> depths = new ArrayList<String>(4);
+ for (int i = 0; i < modes.length; i++) {
+ // Filter out all bit depths lower than 16 - Java incorrectly
+ // reports
+ // them as valid depths though the monitor does not support them
+ if (modes[i].getBitDepth() < 16 && modes[i].getBitDepth() > 0) {
+ continue;
+ }
+
+ String res = modes[i].getWidth() + " x " + modes[i].getHeight();
+ String depth = modes[i].getBitDepth() + " bpp";
+ if (res.equals(resolution) && !depths.contains(depth)) {
+ depths.add(depth);
+ }
+ }
+
+ if (depths.size() == 1 && depths.contains("-1 bpp")) {
+ // add some default depths, possible system is multi-depth supporting
+ depths.clear();
+ depths.add("24 bpp");
+ }
+
+ String[] res = new String[depths.size()];
+ depths.toArray(res);
+ return res;
+ }
+
+ /**
+ * Returns every possible refresh rate for the given resolution.
+ */
+ private static String[] getFrequencies(String resolution,
+ DisplayMode[] modes) {
+ ArrayList<String> freqs = new ArrayList<String>(4);
+ for (int i = 0; i < modes.length; i++) {
+ String res = modes[i].getWidth() + " x " + modes[i].getHeight();
+ String freq;
+ if (modes[i].getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN) {
+ freq = "???";
+ } else {
+ freq = modes[i].getRefreshRate() + " Hz";
+ }
+
+ if (res.equals(resolution) && !freqs.contains(freq)) {
+ freqs.add(freq);
+ }
+ }
+
+ String[] res = new String[freqs.size()];
+ freqs.toArray(res);
+ return res;
+ }
+
+ /**
+ * Utility class for sorting <code>DisplayMode</code>s. Sorts by
+ * resolution, then bit depth, and then finally refresh rate.
+ */
+ private class DisplayModeSorter implements Comparator<DisplayMode> {
+
+ /**
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(DisplayMode a, DisplayMode b) {
+ // Width
+ if (a.getWidth() != b.getWidth()) {
+ return (a.getWidth() > b.getWidth()) ? 1 : -1;
+ }
+ // Height
+ if (a.getHeight() != b.getHeight()) {
+ return (a.getHeight() > b.getHeight()) ? 1 : -1;
+ }
+ // Bit depth
+ if (a.getBitDepth() != b.getBitDepth()) {
+ return (a.getBitDepth() > b.getBitDepth()) ? 1 : -1;
+ }
+ // Refresh rate
+ if (a.getRefreshRate() != b.getRefreshRate()) {
+ return (a.getRefreshRate() > b.getRefreshRate()) ? 1 : -1;
+ }
+ // All fields are equal
+ return 0;
+ }
+ }
+}
diff --git a/engine/src/desktop/com/jme3/app/state/MjpegFileWriter.java b/engine/src/desktop/com/jme3/app/state/MjpegFileWriter.java
new file mode 100644
index 0000000..e39bca1
--- /dev/null
+++ b/engine/src/desktop/com/jme3/app/state/MjpegFileWriter.java
@@ -0,0 +1,490 @@
+package com.jme3.app.state;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.imageio.ImageIO;
+
+/**
+ * Released under BSD License
+ * @author monceaux, normenhansen
+ */
+public class MjpegFileWriter {
+
+ int width = 0;
+ int height = 0;
+ double framerate = 0;
+ int numFrames = 0;
+ File aviFile = null;
+ FileOutputStream aviOutput = null;
+ FileChannel aviChannel = null;
+ long riffOffset = 0;
+ long aviMovieOffset = 0;
+ AVIIndexList indexlist = null;
+
+ public MjpegFileWriter(File aviFile, int width, int height, double framerate) throws Exception {
+ this(aviFile, width, height, framerate, 0);
+ }
+
+ public MjpegFileWriter(File aviFile, int width, int height, double framerate, int numFrames) throws Exception {
+ this.aviFile = aviFile;
+ this.width = width;
+ this.height = height;
+ this.framerate = framerate;
+ this.numFrames = numFrames;
+ aviOutput = new FileOutputStream(aviFile);
+ aviChannel = aviOutput.getChannel();
+
+ RIFFHeader rh = new RIFFHeader();
+ aviOutput.write(rh.toBytes());
+ aviOutput.write(new AVIMainHeader().toBytes());
+ aviOutput.write(new AVIStreamList().toBytes());
+ aviOutput.write(new AVIStreamHeader().toBytes());
+ aviOutput.write(new AVIStreamFormat().toBytes());
+ aviOutput.write(new AVIJunk().toBytes());
+ aviMovieOffset = aviChannel.position();
+ aviOutput.write(new AVIMovieList().toBytes());
+ indexlist = new AVIIndexList();
+ }
+
+ public void addImage(Image image) throws Exception {
+ addImage(writeImageToBytes(image));
+ }
+
+ public void addImage(byte[] imagedata) throws Exception {
+ byte[] fcc = new byte[]{'0', '0', 'd', 'b'};
+ int useLength = imagedata.length;
+ long position = aviChannel.position();
+ int extra = (useLength + (int) position) % 4;
+ if (extra > 0) {
+ useLength = useLength + extra;
+ }
+
+ indexlist.addAVIIndex((int) position, useLength);
+
+ aviOutput.write(fcc);
+ aviOutput.write(intBytes(swapInt(useLength)));
+ aviOutput.write(imagedata);
+ if (extra > 0) {
+ for (int i = 0; i < extra; i++) {
+ aviOutput.write(0);
+ }
+ }
+ imagedata = null;
+ }
+
+ public void finishAVI() throws Exception {
+ byte[] indexlistBytes = indexlist.toBytes();
+ aviOutput.write(indexlistBytes);
+ aviOutput.close();
+ long size = aviFile.length();
+ RandomAccessFile raf = new RandomAccessFile(aviFile, "rw");
+ raf.seek(4);
+ raf.write(intBytes(swapInt((int) size - 8)));
+ raf.seek(aviMovieOffset + 4);
+ raf.write(intBytes(swapInt((int) (size - 8 - aviMovieOffset - indexlistBytes.length))));
+ raf.close();
+ }
+
+ // public void writeAVI(File file) throws Exception
+ // {
+ // OutputStream os = new FileOutputStream(file);
+ //
+ // // RIFFHeader
+ // // AVIMainHeader
+ // // AVIStreamList
+ // // AVIStreamHeader
+ // // AVIStreamFormat
+ // // write 00db and image bytes...
+ // }
+ public static int swapInt(int v) {
+ return (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000) | ((v >> 8) & 0x0000FF00);
+ }
+
+ public static short swapShort(short v) {
+ return (short) ((v >>> 8) | (v << 8));
+ }
+
+ public static byte[] intBytes(int i) {
+ byte[] b = new byte[4];
+ b[0] = (byte) (i >>> 24);
+ b[1] = (byte) ((i >>> 16) & 0x000000FF);
+ b[2] = (byte) ((i >>> 8) & 0x000000FF);
+ b[3] = (byte) (i & 0x000000FF);
+
+ return b;
+ }
+
+ public static byte[] shortBytes(short i) {
+ byte[] b = new byte[2];
+ b[0] = (byte) (i >>> 8);
+ b[1] = (byte) (i & 0x000000FF);
+
+ return b;
+ }
+
+ private class RIFFHeader {
+
+ public byte[] fcc = new byte[]{'R', 'I', 'F', 'F'};
+ public int fileSize = 0;
+ public byte[] fcc2 = new byte[]{'A', 'V', 'I', ' '};
+ public byte[] fcc3 = new byte[]{'L', 'I', 'S', 'T'};
+ public int listSize = 200;
+ public byte[] fcc4 = new byte[]{'h', 'd', 'r', 'l'};
+
+ public RIFFHeader() {
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(fileSize)));
+ baos.write(fcc2);
+ baos.write(fcc3);
+ baos.write(intBytes(swapInt(listSize)));
+ baos.write(fcc4);
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIMainHeader {
+ /*
+ *
+ * FOURCC fcc; DWORD cb; DWORD dwMicroSecPerFrame; DWORD
+ * dwMaxBytesPerSec; DWORD dwPaddingGranularity; DWORD dwFlags; DWORD
+ * dwTotalFrames; DWORD dwInitialFrames; DWORD dwStreams; DWORD
+ * dwSuggestedBufferSize; DWORD dwWidth; DWORD dwHeight; DWORD
+ * dwReserved[4];
+ */
+
+ public byte[] fcc = new byte[]{'a', 'v', 'i', 'h'};
+ public int cb = 56;
+ public int dwMicroSecPerFrame = 0; // (1
+ // /
+ // frames
+ // per
+ // sec)
+ // *
+ // 1,000,000
+ public int dwMaxBytesPerSec = 10000000;
+ public int dwPaddingGranularity = 0;
+ public int dwFlags = 65552;
+ public int dwTotalFrames = 0; // replace
+ // with
+ // correct
+ // value
+ public int dwInitialFrames = 0;
+ public int dwStreams = 1;
+ public int dwSuggestedBufferSize = 0;
+ public int dwWidth = 0; // replace
+ // with
+ // correct
+ // value
+ public int dwHeight = 0; // replace
+ // with
+ // correct
+ // value
+ public int[] dwReserved = new int[4];
+
+ public AVIMainHeader() {
+ dwMicroSecPerFrame = (int) ((1.0 / framerate) * 1000000.0);
+ dwWidth = width;
+ dwHeight = height;
+ dwTotalFrames = numFrames;
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(cb)));
+ baos.write(intBytes(swapInt(dwMicroSecPerFrame)));
+ baos.write(intBytes(swapInt(dwMaxBytesPerSec)));
+ baos.write(intBytes(swapInt(dwPaddingGranularity)));
+ baos.write(intBytes(swapInt(dwFlags)));
+ baos.write(intBytes(swapInt(dwTotalFrames)));
+ baos.write(intBytes(swapInt(dwInitialFrames)));
+ baos.write(intBytes(swapInt(dwStreams)));
+ baos.write(intBytes(swapInt(dwSuggestedBufferSize)));
+ baos.write(intBytes(swapInt(dwWidth)));
+ baos.write(intBytes(swapInt(dwHeight)));
+ baos.write(intBytes(swapInt(dwReserved[0])));
+ baos.write(intBytes(swapInt(dwReserved[1])));
+ baos.write(intBytes(swapInt(dwReserved[2])));
+ baos.write(intBytes(swapInt(dwReserved[3])));
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIStreamList {
+
+ public byte[] fcc = new byte[]{'L', 'I', 'S', 'T'};
+ public int size = 124;
+ public byte[] fcc2 = new byte[]{'s', 't', 'r', 'l'};
+
+ public AVIStreamList() {
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(size)));
+ baos.write(fcc2);
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIStreamHeader {
+ /*
+ * FOURCC fcc; DWORD cb; FOURCC fccType; FOURCC fccHandler; DWORD
+ * dwFlags; WORD wPriority; WORD wLanguage; DWORD dwInitialFrames; DWORD
+ * dwScale; DWORD dwRate; DWORD dwStart; DWORD dwLength; DWORD
+ * dwSuggestedBufferSize; DWORD dwQuality; DWORD dwSampleSize; struct {
+ * short int left; short int top; short int right; short int bottom; }
+ * rcFrame;
+ */
+
+ public byte[] fcc = new byte[]{'s', 't', 'r', 'h'};
+ public int cb = 64;
+ public byte[] fccType = new byte[]{'v', 'i', 'd', 's'};
+ public byte[] fccHandler = new byte[]{'M', 'J', 'P', 'G'};
+ public int dwFlags = 0;
+ public short wPriority = 0;
+ public short wLanguage = 0;
+ public int dwInitialFrames = 0;
+ public int dwScale = 0; // microseconds
+ // per
+ // frame
+ public int dwRate = 1000000; // dwRate
+ // /
+ // dwScale
+ // =
+ // frame
+ // rate
+ public int dwStart = 0;
+ public int dwLength = 0; // num
+ // frames
+ public int dwSuggestedBufferSize = 0;
+ public int dwQuality = -1;
+ public int dwSampleSize = 0;
+ public int left = 0;
+ public int top = 0;
+ public int right = 0;
+ public int bottom = 0;
+
+ public AVIStreamHeader() {
+ dwScale = (int) ((1.0 / framerate) * 1000000.0);
+ dwLength = numFrames;
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(cb)));
+ baos.write(fccType);
+ baos.write(fccHandler);
+ baos.write(intBytes(swapInt(dwFlags)));
+ baos.write(shortBytes(swapShort(wPriority)));
+ baos.write(shortBytes(swapShort(wLanguage)));
+ baos.write(intBytes(swapInt(dwInitialFrames)));
+ baos.write(intBytes(swapInt(dwScale)));
+ baos.write(intBytes(swapInt(dwRate)));
+ baos.write(intBytes(swapInt(dwStart)));
+ baos.write(intBytes(swapInt(dwLength)));
+ baos.write(intBytes(swapInt(dwSuggestedBufferSize)));
+ baos.write(intBytes(swapInt(dwQuality)));
+ baos.write(intBytes(swapInt(dwSampleSize)));
+ baos.write(intBytes(swapInt(left)));
+ baos.write(intBytes(swapInt(top)));
+ baos.write(intBytes(swapInt(right)));
+ baos.write(intBytes(swapInt(bottom)));
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIStreamFormat {
+ /*
+ * FOURCC fcc; DWORD cb; DWORD biSize; LONG biWidth; LONG biHeight; WORD
+ * biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage;
+ * LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD
+ * biClrImportant;
+ */
+
+ public byte[] fcc = new byte[]{'s', 't', 'r', 'f'};
+ public int cb = 40;
+ public int biSize = 40; // same
+ // as
+ // cb
+ public int biWidth = 0;
+ public int biHeight = 0;
+ public short biPlanes = 1;
+ public short biBitCount = 24;
+ public byte[] biCompression = new byte[]{'M', 'J', 'P', 'G'};
+ public int biSizeImage = 0; // width
+ // x
+ // height
+ // in
+ // pixels
+ public int biXPelsPerMeter = 0;
+ public int biYPelsPerMeter = 0;
+ public int biClrUsed = 0;
+ public int biClrImportant = 0;
+
+ public AVIStreamFormat() {
+ biWidth = width;
+ biHeight = height;
+ biSizeImage = width * height;
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(cb)));
+ baos.write(intBytes(swapInt(biSize)));
+ baos.write(intBytes(swapInt(biWidth)));
+ baos.write(intBytes(swapInt(biHeight)));
+ baos.write(shortBytes(swapShort(biPlanes)));
+ baos.write(shortBytes(swapShort(biBitCount)));
+ baos.write(biCompression);
+ baos.write(intBytes(swapInt(biSizeImage)));
+ baos.write(intBytes(swapInt(biXPelsPerMeter)));
+ baos.write(intBytes(swapInt(biYPelsPerMeter)));
+ baos.write(intBytes(swapInt(biClrUsed)));
+ baos.write(intBytes(swapInt(biClrImportant)));
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIMovieList {
+
+ public byte[] fcc = new byte[]{'L', 'I', 'S', 'T'};
+ public int listSize = 0;
+ public byte[] fcc2 = new byte[]{'m', 'o', 'v', 'i'};
+
+ // 00db size jpg image data ...
+ public AVIMovieList() {
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(listSize)));
+ baos.write(fcc2);
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIIndexList {
+
+ public byte[] fcc = new byte[]{'i', 'd', 'x', '1'};
+ public int cb = 0;
+ public List<AVIIndex> ind = new ArrayList<AVIIndex>();
+
+ public AVIIndexList() {
+ }
+
+ @SuppressWarnings("unused")
+ public void addAVIIndex(AVIIndex ai) {
+ ind.add(ai);
+ }
+
+ public void addAVIIndex(int dwOffset, int dwSize) {
+ ind.add(new AVIIndex(dwOffset, dwSize));
+ }
+
+ public byte[] toBytes() throws Exception {
+ cb = 16 * ind.size();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(cb)));
+ for (int i = 0; i < ind.size(); i++) {
+ AVIIndex in = (AVIIndex) ind.get(i);
+ baos.write(in.toBytes());
+ }
+
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIIndex {
+
+ public byte[] fcc = new byte[]{'0', '0', 'd', 'b'};
+ public int dwFlags = 16;
+ public int dwOffset = 0;
+ public int dwSize = 0;
+
+ public AVIIndex(int dwOffset, int dwSize) {
+ this.dwOffset = dwOffset;
+ this.dwSize = dwSize;
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(dwFlags)));
+ baos.write(intBytes(swapInt(dwOffset)));
+ baos.write(intBytes(swapInt(dwSize)));
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ private class AVIJunk {
+
+ public byte[] fcc = new byte[]{'J', 'U', 'N', 'K'};
+ public int size = 1808;
+ public byte[] data = new byte[size];
+
+ public AVIJunk() {
+ Arrays.fill(data, (byte) 0);
+ }
+
+ public byte[] toBytes() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(fcc);
+ baos.write(intBytes(swapInt(size)));
+ baos.write(data);
+ baos.close();
+
+ return baos.toByteArray();
+ }
+ }
+
+ public byte[] writeImageToBytes(Image image) throws Exception {
+ BufferedImage bi;
+ if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_RGB) {
+ bi = (BufferedImage) image;
+ } else {
+ bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ Graphics2D g = bi.createGraphics();
+ g.drawImage(image, 0, 0, width, height, null);
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ImageIO.write(bi, "jpg", baos);
+ baos.close();
+ return baos.toByteArray();
+ }
+}
diff --git a/engine/src/desktop/com/jme3/app/state/ScreenshotAppState.java b/engine/src/desktop/com/jme3/app/state/ScreenshotAppState.java
new file mode 100644
index 0000000..ba2556d
--- /dev/null
+++ b/engine/src/desktop/com/jme3/app/state/ScreenshotAppState.java
@@ -0,0 +1,94 @@
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.Screenshots;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.imageio.ImageIO;
+
+public class ScreenshotAppState extends AbstractAppState implements ActionListener, SceneProcessor {
+
+ private static final Logger logger = Logger.getLogger(ScreenshotAppState.class.getName());
+ private boolean capture = false;
+ private Renderer renderer;
+ private ByteBuffer outBuf;
+ private String appName;
+ private int shotIndex = 0;
+ private BufferedImage awtImage;
+
+ @Override
+ public void initialize(AppStateManager stateManager, Application app) {
+ if (!super.isInitialized()){
+ InputManager inputManager = app.getInputManager();
+ inputManager.addMapping("ScreenShot", new KeyTrigger(KeyInput.KEY_SYSRQ));
+ inputManager.addListener(this, "ScreenShot");
+
+ List<ViewPort> vps = app.getRenderManager().getPostViews();
+ ViewPort last = vps.get(vps.size()-1);
+ last.addProcessor(this);
+
+ appName = app.getClass().getSimpleName();
+ }
+
+ super.initialize(stateManager, app);
+ }
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (value){
+ capture = true;
+ }
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ renderer = rm.getRenderer();
+ reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight());
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return super.isInitialized() && renderer != null;
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ outBuf = BufferUtils.createByteBuffer(w*h*4);
+ awtImage = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ public void postFrame(FrameBuffer out) {
+ if (capture){
+ capture = false;
+ shotIndex++;
+
+ renderer.readFrameBuffer(out, outBuf);
+ Screenshots.convertScreenShot(outBuf, awtImage);
+
+ try {
+ ImageIO.write(awtImage, "png", new File(appName + shotIndex + ".png"));
+ } catch (IOException ex){
+ logger.log(Level.SEVERE, "Error while saving screenshot", ex);
+ }
+ }
+ }
+}
diff --git a/engine/src/desktop/com/jme3/app/state/VideoRecorderAppState.java b/engine/src/desktop/com/jme3/app/state/VideoRecorderAppState.java
new file mode 100644
index 0000000..6ae8019
--- /dev/null
+++ b/engine/src/desktop/com/jme3/app/state/VideoRecorderAppState.java
@@ -0,0 +1,241 @@
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.system.NanoTimer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.Screenshots;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A Video recording AppState that records the screen output into an AVI file with
+ * M-JPEG content. The file should be playable on any OS in any video player.<br/>
+ * The video recording starts when the state is attached and stops when it is detached
+ * or the application is quit. You can set the fileName of the file to be written when the
+ * state is detached, else the old file will be overwritten. If you specify no file
+ * the AppState will attempt to write a file into the user home directory, made unique
+ * by a timestamp.
+ * @author normenhansen, Robert McIntyre
+ */
+public class VideoRecorderAppState extends AbstractAppState {
+
+ private int framerate = 30;
+ private VideoProcessor processor;
+ private File file;
+ private Application app;
+ private ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() {
+
+ public Thread newThread(Runnable r) {
+ Thread th = new Thread(r);
+ th.setName("jME Video Processing Thread");
+ th.setDaemon(true);
+ return th;
+ }
+ });
+ private int numCpus = Runtime.getRuntime().availableProcessors();
+ private ViewPort lastViewPort;
+
+ public VideoRecorderAppState() {
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "JME3 VideoRecorder running on {0} CPU's", numCpus);
+ }
+
+ public VideoRecorderAppState(File file) {
+ this.file = file;
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "JME3 VideoRecorder running on {0} CPU's", numCpus);
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File file) {
+ if (isInitialized()) {
+ throw new IllegalStateException("Cannot set file while attached!");
+ }
+ this.file = file;
+ }
+
+ @Override
+ public void initialize(AppStateManager stateManager, Application app) {
+ super.initialize(stateManager, app);
+ this.app = app;
+ app.setTimer(new IsoTimer(framerate));
+ if (file == null) {
+ String filename = System.getProperty("user.home") + File.separator + "jMonkey-" + System.currentTimeMillis() / 1000 + ".avi";
+ file = new File(filename);
+ }
+ processor = new VideoProcessor();
+ List<ViewPort> vps = app.getRenderManager().getPostViews();
+ lastViewPort = vps.get(vps.size()-1);
+ lastViewPort.addProcessor(processor);
+ }
+
+ @Override
+ public void cleanup() {
+ lastViewPort.removeProcessor(processor);
+ app.setTimer(new NanoTimer());
+ initialized = false;
+ file = null;
+ super.cleanup();
+ }
+
+ private class WorkItem {
+
+ ByteBuffer buffer;
+ BufferedImage image;
+ byte[] data;
+
+ public WorkItem(int width, int height) {
+ image = new BufferedImage(width, height,
+ BufferedImage.TYPE_4BYTE_ABGR);
+ buffer = BufferUtils.createByteBuffer(width * height * 4);
+ }
+ }
+
+ private class VideoProcessor implements SceneProcessor {
+
+ private Camera camera;
+ private int width;
+ private int height;
+ private RenderManager renderManager;
+ private boolean isInitilized = false;
+ private LinkedBlockingQueue<WorkItem> freeItems;
+ private LinkedBlockingQueue<WorkItem> usedItems = new LinkedBlockingQueue<WorkItem>();
+ private MjpegFileWriter writer;
+
+ public void addImage(Renderer renderer, FrameBuffer out) {
+ if (freeItems == null) {
+ return;
+ }
+ try {
+ final WorkItem item = freeItems.take();
+ usedItems.add(item);
+ item.buffer.clear();
+ renderer.readFrameBuffer(out, item.buffer);
+ executor.submit(new Callable<Void>() {
+
+ public Void call() throws Exception {
+ Screenshots.convertScreenShot(item.buffer, item.image);
+ item.data = writer.writeImageToBytes(item.image);
+ while (usedItems.peek() != item) {
+ Thread.sleep(1);
+ }
+ writer.addImage(item.data);
+ usedItems.poll();
+ freeItems.add(item);
+ return null;
+ }
+ });
+ } catch (InterruptedException ex) {
+ Logger.getLogger(VideoRecorderAppState.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ public void initialize(RenderManager rm, ViewPort viewPort) {
+ this.camera = viewPort.getCamera();
+ this.width = camera.getWidth();
+ this.height = camera.getHeight();
+ this.renderManager = rm;
+ this.isInitilized = true;
+ if (freeItems == null) {
+ freeItems = new LinkedBlockingQueue<WorkItem>();
+ for (int i = 0; i < numCpus; i++) {
+ freeItems.add(new WorkItem(width, height));
+ }
+ }
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public boolean isInitialized() {
+ return this.isInitilized;
+ }
+
+ public void preFrame(float tpf) {
+ if (null == writer) {
+ try {
+ writer = new MjpegFileWriter(file, width, height, framerate);
+ } catch (Exception ex) {
+ Logger.getLogger(VideoRecorderAppState.class.getName()).log(Level.SEVERE, "Error creating file writer: {0}", ex);
+ }
+ }
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ public void postFrame(FrameBuffer out) {
+ addImage(renderManager.getRenderer(), out);
+ }
+
+ public void cleanup() {
+ try {
+ while (freeItems.size() < numCpus) {
+ Thread.sleep(10);
+ }
+ writer.finishAVI();
+ } catch (Exception ex) {
+ Logger.getLogger(VideoRecorderAppState.class.getName()).log(Level.SEVERE, "Error closing video: {0}", ex);
+ }
+ writer = null;
+ }
+ }
+
+ public static final class IsoTimer extends com.jme3.system.Timer {
+
+ private float framerate;
+ private int ticks;
+ private long lastTime = 0;
+
+ public IsoTimer(float framerate) {
+ this.framerate = framerate;
+ this.ticks = 0;
+ }
+
+ public long getTime() {
+ return (long) (this.ticks * (1.0f / this.framerate) * 1000f);
+ }
+
+ public long getResolution() {
+ return 1000000000L;
+ }
+
+ public float getFrameRate() {
+ return this.framerate;
+ }
+
+ public float getTimePerFrame() {
+ return (float) (1.0f / this.framerate);
+ }
+
+ public void update() {
+ long time = System.currentTimeMillis();
+ long difference = time - lastTime;
+ lastTime = time;
+ if (difference < (1.0f / this.framerate) * 1000.0f) {
+ try {
+ Thread.sleep(difference);
+ } catch (InterruptedException ex) {
+ }
+ }
+ this.ticks++;
+ }
+
+ public void reset() {
+ this.ticks = 0;
+ }
+ }
+}
diff --git a/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java b/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java
new file mode 100644
index 0000000..8667074
--- /dev/null
+++ b/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.awt;
+
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.KeyInputEvent;
+import java.awt.Component;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ * <code>AwtKeyInput</code>
+ *
+ * @author Joshua Slack
+ * @author Kirill Vainer
+ * @version $Revision: 4133 $
+ */
+public class AwtKeyInput implements KeyInput, KeyListener {
+
+ private static final Logger logger = Logger.getLogger(AwtKeyInput.class.getName());
+
+ private final ArrayList<KeyInputEvent> eventQueue = new ArrayList<KeyInputEvent>();
+ private RawInputListener listener;
+ private Component component;
+
+ public AwtKeyInput(){
+ }
+
+ public void initialize() {
+ }
+
+ public void destroy() {
+ }
+
+ public void setInputSource(Component comp){
+ synchronized (eventQueue){
+ if (component != null){
+ component.removeKeyListener(this);
+ eventQueue.clear();
+ }
+ component = comp;
+ component.addKeyListener(this);
+ }
+ }
+
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+
+ public int getKeyCount() {
+ return KeyEvent.KEY_LAST+1;
+ }
+
+ public void update() {
+ synchronized (eventQueue){
+ // flush events to listener
+ for (int i = 0; i < eventQueue.size(); i++){
+ listener.onKeyEvent(eventQueue.get(i));
+ }
+ eventQueue.clear();
+ }
+ }
+
+ public boolean isInitialized() {
+ return true;
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public void keyTyped(KeyEvent evt) {
+ // key code is zero for typed events
+// int code = 0;
+// KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), false, true);
+// keyEvent.setTime(evt.getWhen());
+// synchronized (eventQueue){
+// eventQueue.add(keyEvent);
+// }
+ }
+
+ public void keyPressed(KeyEvent evt) {
+ int code = convertAwtKey(evt.getKeyCode());
+ KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), true, false);
+ keyEvent.setTime(evt.getWhen());
+ synchronized (eventQueue){
+ eventQueue.add(keyEvent);
+ }
+ }
+
+ public void keyReleased(KeyEvent evt) {
+ int code = convertAwtKey(evt.getKeyCode());
+ KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), false, false);
+ keyEvent.setTime(evt.getWhen());
+ synchronized (eventQueue){
+ eventQueue.add(keyEvent);
+ }
+ }
+
+ /**
+ * <code>convertJmeCode</code> converts KeyInput key codes to AWT key codes.
+ *
+ * @param key jme KeyInput key code
+ * @return awt KeyEvent key code
+ */
+ public static int convertJmeCode( int key ) {
+ switch ( key ) {
+ case KEY_ESCAPE:
+ return KeyEvent.VK_ESCAPE;
+ case KEY_1:
+ return KeyEvent.VK_1;
+ case KEY_2:
+ return KeyEvent.VK_2;
+ case KEY_3:
+ return KeyEvent.VK_3;
+ case KEY_4:
+ return KeyEvent.VK_4;
+ case KEY_5:
+ return KeyEvent.VK_5;
+ case KEY_6:
+ return KeyEvent.VK_6;
+ case KEY_7:
+ return KeyEvent.VK_7;
+ case KEY_8:
+ return KeyEvent.VK_8;
+ case KEY_9:
+ return KeyEvent.VK_9;
+ case KEY_0:
+ return KeyEvent.VK_0;
+ case KEY_MINUS:
+ return KeyEvent.VK_MINUS;
+ case KEY_EQUALS:
+ return KeyEvent.VK_EQUALS;
+ case KEY_BACK:
+ return KeyEvent.VK_BACK_SPACE;
+ case KEY_TAB:
+ return KeyEvent.VK_TAB;
+ case KEY_Q:
+ return KeyEvent.VK_Q;
+ case KEY_W:
+ return KeyEvent.VK_W;
+ case KEY_E:
+ return KeyEvent.VK_E;
+ case KEY_R:
+ return KeyEvent.VK_R;
+ case KEY_T:
+ return KeyEvent.VK_T;
+ case KEY_Y:
+ return KeyEvent.VK_Y;
+ case KEY_U:
+ return KeyEvent.VK_U;
+ case KEY_I:
+ return KeyEvent.VK_I;
+ case KEY_O:
+ return KeyEvent.VK_O;
+ case KEY_P:
+ return KeyEvent.VK_P;
+ case KEY_LBRACKET:
+ return KeyEvent.VK_OPEN_BRACKET;
+ case KEY_RBRACKET:
+ return KeyEvent.VK_CLOSE_BRACKET;
+ case KEY_RETURN:
+ return KeyEvent.VK_ENTER;
+ case KEY_LCONTROL:
+ return KeyEvent.VK_CONTROL;
+ case KEY_A:
+ return KeyEvent.VK_A;
+ case KEY_S:
+ return KeyEvent.VK_S;
+ case KEY_D:
+ return KeyEvent.VK_D;
+ case KEY_F:
+ return KeyEvent.VK_F;
+ case KEY_G:
+ return KeyEvent.VK_G;
+ case KEY_H:
+ return KeyEvent.VK_H;
+ case KEY_J:
+ return KeyEvent.VK_J;
+ case KEY_K:
+ return KeyEvent.VK_K;
+ case KEY_L:
+ return KeyEvent.VK_L;
+ case KEY_SEMICOLON:
+ return KeyEvent.VK_SEMICOLON;
+ case KEY_APOSTROPHE:
+ return KeyEvent.VK_QUOTE;
+ case KEY_GRAVE:
+ return KeyEvent.VK_DEAD_GRAVE;
+ case KEY_LSHIFT:
+ return KeyEvent.VK_SHIFT;
+ case KEY_BACKSLASH:
+ return KeyEvent.VK_BACK_SLASH;
+ case KEY_Z:
+ return KeyEvent.VK_Z;
+ case KEY_X:
+ return KeyEvent.VK_X;
+ case KEY_C:
+ return KeyEvent.VK_C;
+ case KEY_V:
+ return KeyEvent.VK_V;
+ case KEY_B:
+ return KeyEvent.VK_B;
+ case KEY_N:
+ return KeyEvent.VK_N;
+ case KEY_M:
+ return KeyEvent.VK_M;
+ case KEY_COMMA:
+ return KeyEvent.VK_COMMA;
+ case KEY_PERIOD:
+ return KeyEvent.VK_PERIOD;
+ case KEY_SLASH:
+ return KeyEvent.VK_SLASH;
+ case KEY_RSHIFT:
+ return KeyEvent.VK_SHIFT;
+ case KEY_MULTIPLY:
+ return KeyEvent.VK_MULTIPLY;
+ case KEY_SPACE:
+ return KeyEvent.VK_SPACE;
+ case KEY_CAPITAL:
+ return KeyEvent.VK_CAPS_LOCK;
+ case KEY_F1:
+ return KeyEvent.VK_F1;
+ case KEY_F2:
+ return KeyEvent.VK_F2;
+ case KEY_F3:
+ return KeyEvent.VK_F3;
+ case KEY_F4:
+ return KeyEvent.VK_F4;
+ case KEY_F5:
+ return KeyEvent.VK_F5;
+ case KEY_F6:
+ return KeyEvent.VK_F6;
+ case KEY_F7:
+ return KeyEvent.VK_F7;
+ case KEY_F8:
+ return KeyEvent.VK_F8;
+ case KEY_F9:
+ return KeyEvent.VK_F9;
+ case KEY_F10:
+ return KeyEvent.VK_F10;
+ case KEY_NUMLOCK:
+ return KeyEvent.VK_NUM_LOCK;
+ case KEY_SCROLL:
+ return KeyEvent.VK_SCROLL_LOCK;
+ case KEY_NUMPAD7:
+ return KeyEvent.VK_NUMPAD7;
+ case KEY_NUMPAD8:
+ return KeyEvent.VK_NUMPAD8;
+ case KEY_NUMPAD9:
+ return KeyEvent.VK_NUMPAD9;
+ case KEY_SUBTRACT:
+ return KeyEvent.VK_SUBTRACT;
+ case KEY_NUMPAD4:
+ return KeyEvent.VK_NUMPAD4;
+ case KEY_NUMPAD5:
+ return KeyEvent.VK_NUMPAD5;
+ case KEY_NUMPAD6:
+ return KeyEvent.VK_NUMPAD6;
+ case KEY_ADD:
+ return KeyEvent.VK_ADD;
+ case KEY_NUMPAD1:
+ return KeyEvent.VK_NUMPAD1;
+ case KEY_NUMPAD2:
+ return KeyEvent.VK_NUMPAD2;
+ case KEY_NUMPAD3:
+ return KeyEvent.VK_NUMPAD3;
+ case KEY_NUMPAD0:
+ return KeyEvent.VK_NUMPAD0;
+ case KEY_DECIMAL:
+ return KeyEvent.VK_DECIMAL;
+ case KEY_F11:
+ return KeyEvent.VK_F11;
+ case KEY_F12:
+ return KeyEvent.VK_F12;
+ case KEY_F13:
+ return KeyEvent.VK_F13;
+ case KEY_F14:
+ return KeyEvent.VK_F14;
+ case KEY_F15:
+ return KeyEvent.VK_F15;
+ case KEY_KANA:
+ return KeyEvent.VK_KANA;
+ case KEY_CONVERT:
+ return KeyEvent.VK_CONVERT;
+ case KEY_NOCONVERT:
+ return KeyEvent.VK_NONCONVERT;
+ case KEY_NUMPADEQUALS:
+ return KeyEvent.VK_EQUALS;
+ case KEY_CIRCUMFLEX:
+ return KeyEvent.VK_CIRCUMFLEX;
+ case KEY_AT:
+ return KeyEvent.VK_AT;
+ case KEY_COLON:
+ return KeyEvent.VK_COLON;
+ case KEY_UNDERLINE:
+ return KeyEvent.VK_UNDERSCORE;
+ case KEY_STOP:
+ return KeyEvent.VK_STOP;
+ case KEY_NUMPADENTER:
+ return KeyEvent.VK_ENTER;
+ case KEY_RCONTROL:
+ return KeyEvent.VK_CONTROL;
+ case KEY_NUMPADCOMMA:
+ return KeyEvent.VK_COMMA;
+ case KEY_DIVIDE:
+ return KeyEvent.VK_DIVIDE;
+ case KEY_PAUSE:
+ return KeyEvent.VK_PAUSE;
+ case KEY_HOME:
+ return KeyEvent.VK_HOME;
+ case KEY_UP:
+ return KeyEvent.VK_UP;
+ case KEY_PRIOR:
+ return KeyEvent.VK_PAGE_UP;
+ case KEY_LEFT:
+ return KeyEvent.VK_LEFT;
+ case KEY_RIGHT:
+ return KeyEvent.VK_RIGHT;
+ case KEY_END:
+ return KeyEvent.VK_END;
+ case KEY_DOWN:
+ return KeyEvent.VK_DOWN;
+ case KEY_NEXT:
+ return KeyEvent.VK_PAGE_DOWN;
+ case KEY_INSERT:
+ return KeyEvent.VK_INSERT;
+ case KEY_DELETE:
+ return KeyEvent.VK_DELETE;
+ case KEY_LMENU:
+ return KeyEvent.VK_ALT; //todo: location left
+ case KEY_RMENU:
+ return KeyEvent.VK_ALT; //todo: location right
+ }
+ logger.warning("unsupported key:" + key);
+ return 0x10000 + key;
+ }
+
+ /**
+ * <code>convertAwtKey</code> converts AWT key codes to KeyInput key codes.
+ *
+ * @param key awt KeyEvent key code
+ * @return jme KeyInput key code
+ */
+ public static int convertAwtKey(int key) {
+ switch ( key ) {
+ case KeyEvent.VK_ESCAPE:
+ return KEY_ESCAPE;
+ case KeyEvent.VK_1:
+ return KEY_1;
+ case KeyEvent.VK_2:
+ return KEY_2;
+ case KeyEvent.VK_3:
+ return KEY_3;
+ case KeyEvent.VK_4:
+ return KEY_4;
+ case KeyEvent.VK_5:
+ return KEY_5;
+ case KeyEvent.VK_6:
+ return KEY_6;
+ case KeyEvent.VK_7:
+ return KEY_7;
+ case KeyEvent.VK_8:
+ return KEY_8;
+ case KeyEvent.VK_9:
+ return KEY_9;
+ case KeyEvent.VK_0:
+ return KEY_0;
+ case KeyEvent.VK_MINUS:
+ return KEY_MINUS;
+ case KeyEvent.VK_EQUALS:
+ return KEY_EQUALS;
+ case KeyEvent.VK_BACK_SPACE:
+ return KEY_BACK;
+ case KeyEvent.VK_TAB:
+ return KEY_TAB;
+ case KeyEvent.VK_Q:
+ return KEY_Q;
+ case KeyEvent.VK_W:
+ return KEY_W;
+ case KeyEvent.VK_E:
+ return KEY_E;
+ case KeyEvent.VK_R:
+ return KEY_R;
+ case KeyEvent.VK_T:
+ return KEY_T;
+ case KeyEvent.VK_Y:
+ return KEY_Y;
+ case KeyEvent.VK_U:
+ return KEY_U;
+ case KeyEvent.VK_I:
+ return KEY_I;
+ case KeyEvent.VK_O:
+ return KEY_O;
+ case KeyEvent.VK_P:
+ return KEY_P;
+ case KeyEvent.VK_OPEN_BRACKET:
+ return KEY_LBRACKET;
+ case KeyEvent.VK_CLOSE_BRACKET:
+ return KEY_RBRACKET;
+ case KeyEvent.VK_ENTER:
+ return KEY_RETURN;
+ case KeyEvent.VK_CONTROL:
+ return KEY_LCONTROL;
+ case KeyEvent.VK_A:
+ return KEY_A;
+ case KeyEvent.VK_S:
+ return KEY_S;
+ case KeyEvent.VK_D:
+ return KEY_D;
+ case KeyEvent.VK_F:
+ return KEY_F;
+ case KeyEvent.VK_G:
+ return KEY_G;
+ case KeyEvent.VK_H:
+ return KEY_H;
+ case KeyEvent.VK_J:
+ return KEY_J;
+ case KeyEvent.VK_K:
+ return KEY_K;
+ case KeyEvent.VK_L:
+ return KEY_L;
+ case KeyEvent.VK_SEMICOLON:
+ return KEY_SEMICOLON;
+ case KeyEvent.VK_QUOTE:
+ return KEY_APOSTROPHE;
+ case KeyEvent.VK_DEAD_GRAVE:
+ return KEY_GRAVE;
+ case KeyEvent.VK_SHIFT:
+ return KEY_LSHIFT;
+ case KeyEvent.VK_BACK_SLASH:
+ return KEY_BACKSLASH;
+ case KeyEvent.VK_Z:
+ return KEY_Z;
+ case KeyEvent.VK_X:
+ return KEY_X;
+ case KeyEvent.VK_C:
+ return KEY_C;
+ case KeyEvent.VK_V:
+ return KEY_V;
+ case KeyEvent.VK_B:
+ return KEY_B;
+ case KeyEvent.VK_N:
+ return KEY_N;
+ case KeyEvent.VK_M:
+ return KEY_M;
+ case KeyEvent.VK_COMMA:
+ return KEY_COMMA;
+ case KeyEvent.VK_PERIOD:
+ return KEY_PERIOD;
+ case KeyEvent.VK_SLASH:
+ return KEY_SLASH;
+ case KeyEvent.VK_MULTIPLY:
+ return KEY_MULTIPLY;
+ case KeyEvent.VK_SPACE:
+ return KEY_SPACE;
+ case KeyEvent.VK_CAPS_LOCK:
+ return KEY_CAPITAL;
+ case KeyEvent.VK_F1:
+ return KEY_F1;
+ case KeyEvent.VK_F2:
+ return KEY_F2;
+ case KeyEvent.VK_F3:
+ return KEY_F3;
+ case KeyEvent.VK_F4:
+ return KEY_F4;
+ case KeyEvent.VK_F5:
+ return KEY_F5;
+ case KeyEvent.VK_F6:
+ return KEY_F6;
+ case KeyEvent.VK_F7:
+ return KEY_F7;
+ case KeyEvent.VK_F8:
+ return KEY_F8;
+ case KeyEvent.VK_F9:
+ return KEY_F9;
+ case KeyEvent.VK_F10:
+ return KEY_F10;
+ case KeyEvent.VK_NUM_LOCK:
+ return KEY_NUMLOCK;
+ case KeyEvent.VK_SCROLL_LOCK:
+ return KEY_SCROLL;
+ case KeyEvent.VK_NUMPAD7:
+ return KEY_NUMPAD7;
+ case KeyEvent.VK_NUMPAD8:
+ return KEY_NUMPAD8;
+ case KeyEvent.VK_NUMPAD9:
+ return KEY_NUMPAD9;
+ case KeyEvent.VK_SUBTRACT:
+ return KEY_SUBTRACT;
+ case KeyEvent.VK_NUMPAD4:
+ return KEY_NUMPAD4;
+ case KeyEvent.VK_NUMPAD5:
+ return KEY_NUMPAD5;
+ case KeyEvent.VK_NUMPAD6:
+ return KEY_NUMPAD6;
+ case KeyEvent.VK_ADD:
+ return KEY_ADD;
+ case KeyEvent.VK_NUMPAD1:
+ return KEY_NUMPAD1;
+ case KeyEvent.VK_NUMPAD2:
+ return KEY_NUMPAD2;
+ case KeyEvent.VK_NUMPAD3:
+ return KEY_NUMPAD3;
+ case KeyEvent.VK_NUMPAD0:
+ return KEY_NUMPAD0;
+ case KeyEvent.VK_DECIMAL:
+ return KEY_DECIMAL;
+ case KeyEvent.VK_F11:
+ return KEY_F11;
+ case KeyEvent.VK_F12:
+ return KEY_F12;
+ case KeyEvent.VK_F13:
+ return KEY_F13;
+ case KeyEvent.VK_F14:
+ return KEY_F14;
+ case KeyEvent.VK_F15:
+ return KEY_F15;
+ case KeyEvent.VK_KANA:
+ return KEY_KANA;
+ case KeyEvent.VK_CONVERT:
+ return KEY_CONVERT;
+ case KeyEvent.VK_NONCONVERT:
+ return KEY_NOCONVERT;
+ case KeyEvent.VK_CIRCUMFLEX:
+ return KEY_CIRCUMFLEX;
+ case KeyEvent.VK_AT:
+ return KEY_AT;
+ case KeyEvent.VK_COLON:
+ return KEY_COLON;
+ case KeyEvent.VK_UNDERSCORE:
+ return KEY_UNDERLINE;
+ case KeyEvent.VK_STOP:
+ return KEY_STOP;
+ case KeyEvent.VK_DIVIDE:
+ return KEY_DIVIDE;
+ case KeyEvent.VK_PAUSE:
+ return KEY_PAUSE;
+ case KeyEvent.VK_HOME:
+ return KEY_HOME;
+ case KeyEvent.VK_UP:
+ return KEY_UP;
+ case KeyEvent.VK_PAGE_UP:
+ return KEY_PRIOR;
+ case KeyEvent.VK_LEFT:
+ return KEY_LEFT;
+ case KeyEvent.VK_RIGHT:
+ return KEY_RIGHT;
+ case KeyEvent.VK_END:
+ return KEY_END;
+ case KeyEvent.VK_DOWN:
+ return KEY_DOWN;
+ case KeyEvent.VK_PAGE_DOWN:
+ return KEY_NEXT;
+ case KeyEvent.VK_INSERT:
+ return KEY_INSERT;
+ case KeyEvent.VK_DELETE:
+ return KEY_DELETE;
+ case KeyEvent.VK_ALT:
+ return KEY_LMENU; //Left vs. Right need to improve
+ case KeyEvent.VK_META:
+ return KEY_RCONTROL;
+
+ }
+ logger.warning( "unsupported key:" + key );
+ if ( key >= 0x10000 ) {
+ return key - 0x10000;
+ }
+
+ return 0;
+ }
+
+}
diff --git a/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java b/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java
new file mode 100644
index 0000000..e83c92a
--- /dev/null
+++ b/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.awt;
+
+import com.jme3.input.MouseInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+
+/**
+ * <code>AwtMouseInput</code>
+ *
+ * @author Joshua Slack
+ * @author MHenze (cylab)
+ *
+ * @version $Revision$
+ */
+public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListener, MouseMotionListener {
+
+ public static int WHEEL_AMP = 40; // arbitrary... Java's mouse wheel seems to report something a lot lower than lwjgl's
+
+ private static final Logger logger = Logger.getLogger(AwtMouseInput.class.getName());
+
+ private boolean visible = true;
+
+ private RawInputListener listener;
+
+ private Component component;
+
+ private final ArrayList<MouseButtonEvent> eventQueue = new ArrayList<MouseButtonEvent>();
+ private final ArrayList<MouseButtonEvent> eventQueueCopy = new ArrayList<MouseButtonEvent>();
+
+ private int lastEventX;
+ private int lastEventY;
+ private int lastEventWheel;
+
+ private Cursor transparentCursor;
+
+ private Robot robot;
+ private int wheelPos;
+ private Point location;
+ private Point centerLocation;
+ private Point centerLocationOnScreen;
+ private Point lastKnownLocation;
+ private boolean isRecentering;
+ private boolean cursorMoved;
+ private int eventsSinceRecenter;
+
+ public AwtMouseInput() {
+ location = new Point();
+ centerLocation = new Point();
+ centerLocationOnScreen = new Point();
+ lastKnownLocation = new Point();
+
+ try{
+ robot = new Robot();
+ }catch (java.awt.AWTException e){
+ logger.log(Level.SEVERE, "Could not create a robot, so the mouse cannot be grabbed! ", e);
+ }
+ }
+
+ public void setInputSource(Component comp){
+ if (component != null){
+ component.removeMouseListener(this);
+ component.removeMouseMotionListener(this);
+ component.removeMouseWheelListener(this);
+
+ eventQueue.clear();
+
+ wheelPos = 0;
+ isRecentering = false;
+ eventsSinceRecenter = 0;
+ lastEventX = 0;
+ lastEventY = 0;
+ lastEventWheel = 0;
+ location = new Point();
+ centerLocation = new Point();
+ centerLocationOnScreen = new Point();
+ lastKnownLocation = new Point();
+ }
+
+ component = comp;
+ component.addMouseListener(this);
+ component.addMouseMotionListener(this);
+ component.addMouseWheelListener(this);
+ }
+
+ public void initialize() {
+ }
+
+ public void destroy() {
+ }
+
+ public boolean isInitialized() {
+ return true;
+ }
+
+ public void setInputListener(RawInputListener listener){
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+
+ public void setCursorVisible(boolean visible){
+ if (this.visible != visible){
+
+ lastKnownLocation.x = lastKnownLocation.y = 0;
+
+ this.visible = visible;
+ final boolean newVisible = visible;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ component.setCursor(newVisible ? null : getTransparentCursor());
+ if (!newVisible)
+ recenterMouse(component);
+ }
+ });
+ }
+ }
+
+ public void update() {
+ if (cursorMoved){
+ int newX = location.x;
+ int newY = location.y;
+ int newWheel = wheelPos;
+
+ // invert DY
+ int actualX = lastKnownLocation.x;
+ int actualY = component.getHeight() - lastKnownLocation.y;
+ MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY,
+ newX - lastEventX,
+ lastEventY - newY,
+ wheelPos, lastEventWheel - wheelPos);
+ listener.onMouseMotionEvent(evt);
+
+ lastEventX = newX;
+ lastEventY = newY;
+ lastEventWheel = newWheel;
+
+ cursorMoved = false;
+ }
+
+ synchronized (eventQueue){
+ eventQueueCopy.clear();
+ eventQueueCopy.addAll(eventQueue);
+ eventQueue.clear();
+ }
+
+ int size = eventQueueCopy.size();
+ for (int i = 0; i < size; i++){
+ listener.onMouseButtonEvent(eventQueueCopy.get(i));
+ }
+ }
+
+ private Cursor getTransparentCursor() {
+ if (transparentCursor == null){
+ BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+ cursorImage.setRGB(0, 0, 0);
+ transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), "empty cursor");
+ }
+ return transparentCursor;
+ }
+
+// public void setHardwareCursor(URL file, int xHotspot, int yHotspot) {
+// //Create the image from the provided url
+// java.awt.Image cursorImage = new ImageIcon( file ).getImage( );
+// //Create a custom cursor with this image
+// opaqueCursor = Toolkit.getDefaultToolkit().createCustomCursor( cursorImage , new Point( xHotspot , yHotspot ) , "custom cursor" );
+// //Use this cursor
+// setCursorVisible( isCursorVisible );
+// }
+
+
+ public int getButtonCount() {
+ return 3;
+ }
+
+ public void mouseClicked(MouseEvent arg0) {
+// MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false);
+// listener.onMouseButtonEvent(evt);
+ }
+
+ public void mousePressed(MouseEvent arg0) {
+ MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), true, arg0.getX(), arg0.getY());
+ evt.setTime(arg0.getWhen());
+ synchronized (eventQueue){
+ eventQueue.add(evt);
+ }
+ }
+
+ public void mouseReleased(MouseEvent arg0) {
+ MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false, arg0.getX(), arg0.getY());
+ evt.setTime(arg0.getWhen());
+ synchronized (eventQueue){
+ eventQueue.add(evt);
+ }
+ }
+
+ public void mouseEntered(MouseEvent arg0) {
+ if (!visible)
+ recenterMouse(arg0.getComponent());
+ }
+
+ public void mouseExited(MouseEvent arg0) {
+ if (!visible)
+ recenterMouse(arg0.getComponent());
+ }
+
+ public void mouseWheelMoved(MouseWheelEvent arg0) {
+ int dwheel = arg0.getUnitsToScroll();
+ wheelPos += dwheel * WHEEL_AMP;
+ cursorMoved = true;
+ }
+
+ public void mouseDragged(MouseEvent arg0) {
+ mouseMoved(arg0);
+ }
+
+ public void mouseMoved(MouseEvent arg0) {
+ if (isRecentering) {
+ // MHenze (cylab) Fix Issue 35:
+ // As long as the MouseInput is in recentering mode, nothing is done until the mouse is entered in the component
+ // by the events generated by the robot. If this happens, the last known location is resetted.
+ if ((centerLocation.x == arg0.getX() && centerLocation.y == arg0.getY()) || eventsSinceRecenter++ == 5) {
+ lastKnownLocation.x = arg0.getX();
+ lastKnownLocation.y = arg0.getY();
+ isRecentering = false;
+ }
+ } else {
+ // MHenze (cylab) Fix Issue 35:
+ // Compute the delta and absolute coordinates and recenter the mouse if necessary
+ int dx = arg0.getX() - lastKnownLocation.x;
+ int dy = arg0.getY() - lastKnownLocation.y;
+ location.x += dx;
+ location.y += dy;
+ if (!visible) {
+ recenterMouse(arg0.getComponent());
+ }
+ lastKnownLocation.x = arg0.getX();
+ lastKnownLocation.y = arg0.getY();
+
+ cursorMoved = true;
+ }
+ }
+
+ // MHenze (cylab) Fix Issue 35: A method to generate recenter the mouse to allow the InputSystem to "grab" the mouse
+ private void recenterMouse(final Component component) {
+ if (robot != null) {
+ eventsSinceRecenter = 0;
+ isRecentering = true;
+ centerLocation.setLocation(component.getWidth()/2, component.getHeight()/2);
+ centerLocationOnScreen.setLocation(centerLocation);
+ SwingUtilities.convertPointToScreen(centerLocationOnScreen, component);
+ robot.mouseMove(centerLocationOnScreen.x, centerLocationOnScreen.y);
+ }
+ }
+
+ private int getJMEButtonIndex( MouseEvent arg0 ) {
+ int index;
+ switch (arg0.getButton()) {
+ default:
+ case MouseEvent.BUTTON1: //left
+ index = MouseInput.BUTTON_LEFT;
+ break;
+ case MouseEvent.BUTTON2: //middle
+ index = MouseInput.BUTTON_MIDDLE;
+ break;
+ case MouseEvent.BUTTON3: //right
+ index = MouseInput.BUTTON_RIGHT;
+ break;
+ }
+ return index;
+ }
+}
diff --git a/engine/src/desktop/com/jme3/system/JmeCanvasContext.java b/engine/src/desktop/com/jme3/system/JmeCanvasContext.java
new file mode 100644
index 0000000..1d250b5
--- /dev/null
+++ b/engine/src/desktop/com/jme3/system/JmeCanvasContext.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system;
+
+import java.awt.Canvas;
+
+public interface JmeCanvasContext extends JmeContext {
+ public Canvas getCanvas();
+}
diff --git a/engine/src/desktop/com/jme3/system/JmeDesktopSystem.java b/engine/src/desktop/com/jme3/system/JmeDesktopSystem.java
new file mode 100644
index 0000000..9a8eac4
--- /dev/null
+++ b/engine/src/desktop/com/jme3/system/JmeDesktopSystem.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+import com.jme3.app.SettingsDialog;
+import com.jme3.app.SettingsDialog.SelectionListener;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.DesktopAssetManager;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.system.JmeContext.Type;
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import javax.swing.SwingUtilities;
+
+/**
+ *
+ * @author Kirill Vainer, normenhansen
+ */
+public class JmeDesktopSystem extends JmeSystemDelegate {
+
+ @Override
+ public AssetManager newAssetManager(URL configFile) {
+ return new DesktopAssetManager(configFile);
+ }
+
+ @Override
+ public AssetManager newAssetManager() {
+ return new DesktopAssetManager(null);
+ }
+
+ @Override
+ public boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) {
+ if (SwingUtilities.isEventDispatchThread()) {
+ throw new IllegalStateException("Cannot run from EDT");
+ }
+
+ final AppSettings settings = new AppSettings(false);
+ settings.copyFrom(sourceSettings);
+ String iconPath = sourceSettings.getSettingsDialogImage();
+ final URL iconUrl = JmeSystem.class.getResource(iconPath.startsWith("/") ? iconPath : "/" + iconPath);
+ if (iconUrl == null) {
+ throw new AssetNotFoundException(sourceSettings.getSettingsDialogImage());
+ }
+
+ final AtomicBoolean done = new AtomicBoolean();
+ final AtomicInteger result = new AtomicInteger();
+ final Object lock = new Object();
+
+ final SelectionListener selectionListener = new SelectionListener() {
+
+ public void onSelection(int selection) {
+ synchronized (lock) {
+ done.set(true);
+ result.set(selection);
+ lock.notifyAll();
+ }
+ }
+ };
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ synchronized (lock) {
+ SettingsDialog dialog = new SettingsDialog(settings, iconUrl, loadFromRegistry);
+ dialog.setSelectionListener(selectionListener);
+ dialog.showDialog();
+ }
+ }
+ });
+
+ synchronized (lock) {
+ while (!done.get()) {
+ try {
+ lock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+
+ sourceSettings.copyFrom(settings);
+
+ return result.get() == SettingsDialog.APPROVE_SELECTION;
+ }
+
+ private JmeContext newContextLwjgl(AppSettings settings, JmeContext.Type type) {
+ try {
+ Class<? extends JmeContext> ctxClazz = null;
+ switch (type) {
+ case Canvas:
+ ctxClazz = (Class<? extends JmeContext>) Class.forName("com.jme3.system.lwjgl.LwjglCanvas");
+ break;
+ case Display:
+ ctxClazz = (Class<? extends JmeContext>) Class.forName("com.jme3.system.lwjgl.LwjglDisplay");
+ break;
+ case OffscreenSurface:
+ ctxClazz = (Class<? extends JmeContext>) Class.forName("com.jme3.system.lwjgl.LwjglOffscreenBuffer");
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported context type " + type);
+ }
+
+ return ctxClazz.newInstance();
+ } catch (InstantiationException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (IllegalAccessException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (ClassNotFoundException ex) {
+ logger.log(Level.SEVERE, "CRITICAL ERROR: Context class is missing!\n"
+ + "Make sure jme3_lwjgl-ogl is on the classpath.", ex);
+ }
+
+ return null;
+ }
+
+ private JmeContext newContextJogl(AppSettings settings, JmeContext.Type type) {
+ try {
+ Class<? extends JmeContext> ctxClazz = null;
+ switch (type) {
+ case Display:
+ ctxClazz = (Class<? extends JmeContext>) Class.forName("com.jme3.system.jogl.JoglDisplay");
+ break;
+ case Canvas:
+ ctxClazz = (Class<? extends JmeContext>) Class.forName("com.jme3.system.jogl.JoglCanvas");
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported context type " + type);
+ }
+
+ return ctxClazz.newInstance();
+ } catch (InstantiationException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (IllegalAccessException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (ClassNotFoundException ex) {
+ logger.log(Level.SEVERE, "CRITICAL ERROR: Context class is missing!\n"
+ + "Make sure jme3_jogl is on the classpath.", ex);
+ }
+
+ return null;
+ }
+
+ private JmeContext newContextCustom(AppSettings settings, JmeContext.Type type) {
+ try {
+ String className = settings.getRenderer().substring("CUSTOM".length());
+
+ Class<? extends JmeContext> ctxClazz = null;
+ ctxClazz = (Class<? extends JmeContext>) Class.forName(className);
+ return ctxClazz.newInstance();
+ } catch (InstantiationException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (IllegalAccessException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (ClassNotFoundException ex) {
+ logger.log(Level.SEVERE, "CRITICAL ERROR: Context class is missing!", ex);
+ }
+
+ return null;
+ }
+
+ @Override
+ public JmeContext newContext(AppSettings settings, Type contextType) {
+ initialize(settings);
+ JmeContext ctx;
+ if (settings.getRenderer() == null
+ || settings.getRenderer().equals("NULL")
+ || contextType == JmeContext.Type.Headless) {
+ ctx = new NullContext();
+ ctx.setSettings(settings);
+ } else if (settings.getRenderer().startsWith("LWJGL")) {
+ ctx = newContextLwjgl(settings, contextType);
+ ctx.setSettings(settings);
+ } else if (settings.getRenderer().startsWith("JOGL")) {
+ ctx = newContextJogl(settings, contextType);
+ ctx.setSettings(settings);
+ } else if (settings.getRenderer().startsWith("CUSTOM")) {
+ ctx = newContextCustom(settings, contextType);
+ ctx.setSettings(settings);
+ } else {
+ throw new UnsupportedOperationException(
+ "Unrecognizable renderer specified: "
+ + settings.getRenderer());
+ }
+ return ctx;
+ }
+
+ @Override
+ public AudioRenderer newAudioRenderer(AppSettings settings) {
+ initialize(settings);
+ Class<? extends AudioRenderer> clazz = null;
+ try {
+ if (settings.getAudioRenderer().startsWith("LWJGL")) {
+ clazz = (Class<? extends AudioRenderer>) Class.forName("com.jme3.audio.lwjgl.LwjglAudioRenderer");
+ } else if (settings.getAudioRenderer().startsWith("JOAL")) {
+ clazz = (Class<? extends AudioRenderer>) Class.forName("com.jme3.audio.joal.JoalAudioRenderer");
+ } else {
+ throw new UnsupportedOperationException(
+ "Unrecognizable audio renderer specified: "
+ + settings.getAudioRenderer());
+ }
+
+ AudioRenderer ar = clazz.newInstance();
+ return ar;
+ } catch (InstantiationException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (IllegalAccessException ex) {
+ logger.log(Level.SEVERE, "Failed to create context", ex);
+ } catch (ClassNotFoundException ex) {
+ logger.log(Level.SEVERE, "CRITICAL ERROR: Audio implementation class is missing!\n"
+ + "Make sure jme3_lwjgl-oal or jm3_joal is on the classpath.", ex);
+ }
+ return null;
+ }
+
+ @Override
+ public void initialize(AppSettings settings) {
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+ try {
+ if (!lowPermissions) {
+ // can only modify logging settings
+ // if permissions are available
+// JmeFormatter formatter = new JmeFormatter();
+// Handler fileHandler = new FileHandler("jme.log");
+// fileHandler.setFormatter(formatter);
+// Logger.getLogger("").addHandler(fileHandler);
+// Handler consoleHandler = new ConsoleHandler();
+// consoleHandler.setFormatter(formatter);
+// Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
+// Logger.getLogger("").addHandler(consoleHandler);
+ }
+// } catch (IOException ex){
+// logger.log(Level.SEVERE, "I/O Error while creating log file", ex);
+ } catch (SecurityException ex) {
+ logger.log(Level.SEVERE, "Security error in creating log file", ex);
+ }
+ logger.log(Level.INFO, "Running on {0}", getFullName());
+
+ if (!lowPermissions) {
+ try {
+ Natives.extractNativeLibs(getPlatform(), settings);
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "Error while copying native libraries", ex);
+ }
+ }
+ }
+}
diff --git a/engine/src/desktop/com/jme3/system/Natives.java b/engine/src/desktop/com/jme3/system/Natives.java
new file mode 100644
index 0000000..979c606
--- /dev/null
+++ b/engine/src/desktop/com/jme3/system/Natives.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Helper class for extracting the natives (dll, so) from the jars.
+ * This class should only be used internally.
+ */
+public final class Natives {
+
+ private static final Logger logger = Logger.getLogger(Natives.class.getName());
+ private static final byte[] buf = new byte[1024];
+ private static File extractionDirOverride = null;
+ private static File extractionDir = null;
+
+ public static void setExtractionDir(String name) {
+ extractionDirOverride = new File(name).getAbsoluteFile();
+ }
+
+ public static File getExtractionDir() {
+ if (extractionDirOverride != null) {
+ return extractionDirOverride;
+ }
+ if (extractionDir == null) {
+ File workingFolder = new File("").getAbsoluteFile();
+ if (!workingFolder.canWrite()) {
+ setStorageExtractionDir();
+ } else {
+ try {
+ File file = new File(workingFolder.getAbsolutePath() + File.separator + ".jmetestwrite");
+ file.createNewFile();
+ file.delete();
+ extractionDir = workingFolder;
+ } catch (Exception e) {
+ setStorageExtractionDir();
+ }
+ }
+ }
+ return extractionDir;
+ }
+
+ private static void setStorageExtractionDir() {
+ logger.log(Level.WARNING, "Working directory is not writable. Using home directory instead.");
+ extractionDir = new File(JmeSystem.getStorageFolder(),
+ "natives_" + Integer.toHexString(computeNativesHash()));
+ if (!extractionDir.exists()) {
+ extractionDir.mkdir();
+ }
+ }
+
+ private static int computeNativesHash() {
+ try {
+ String classpath = System.getProperty("java.class.path");
+ URL url = Thread.currentThread().getContextClassLoader().getResource("com/jme3/system/Natives.class");
+
+ StringBuilder sb = new StringBuilder(url.toString());
+ if (sb.indexOf("jar:") == 0) {
+ sb.delete(0, 4);
+ sb.delete(sb.indexOf("!"), sb.length());
+ sb.delete(sb.lastIndexOf("/") + 1, sb.length());
+ }
+ try {
+ url = new URL(sb.toString());
+ } catch (MalformedURLException ex) {
+ throw new UnsupportedOperationException(ex);
+ }
+
+ URLConnection conn = url.openConnection();
+ int hash = classpath.hashCode() ^ (int) conn.getLastModified();
+ return hash;
+ } catch (IOException ex) {
+ throw new UnsupportedOperationException(ex);
+ }
+ }
+
+ public static void extractNativeLib(String sysName, String name) throws IOException {
+ extractNativeLib(sysName, name, false, true);
+ }
+
+ public static void extractNativeLib(String sysName, String name, boolean load) throws IOException {
+ extractNativeLib(sysName, name, load, true);
+ }
+
+ public static void extractNativeLib(String sysName, String name, boolean load, boolean warning) throws IOException {
+ String fullname = System.mapLibraryName(name);
+
+ String path = "native/" + sysName + "/" + fullname;
+ URL url = Thread.currentThread().getContextClassLoader().getResource(path);
+
+ if (url == null) {
+ if (!warning) {
+ logger.log(Level.WARNING, "Cannot locate native library: {0}/{1}",
+ new String[]{sysName, fullname});
+ }
+ return;
+ }
+
+ URLConnection conn = url.openConnection();
+ InputStream in = conn.getInputStream();
+ File targetFile = new File(getExtractionDir(), fullname);
+ OutputStream out = null;
+ try {
+ if (targetFile.exists()) {
+ // OK, compare last modified date of this file to
+ // file in jar
+ long targetLastModified = targetFile.lastModified();
+ long sourceLastModified = conn.getLastModified();
+
+ // Allow ~1 second range for OSes that only support low precision
+ if (targetLastModified + 1000 > sourceLastModified) {
+ logger.log(Level.FINE, "Not copying library {0}. Latest already extracted.", fullname);
+ return;
+ }
+ }
+
+ out = new FileOutputStream(targetFile);
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ in.close();
+ in = null;
+ out.close();
+ out = null;
+
+ // NOTE: On OSes that support "Date Created" property,
+ // this will cause the last modified date to be lower than
+ // date created which makes no sense
+ targetFile.setLastModified(conn.getLastModified());
+ } catch (FileNotFoundException ex) {
+ if (ex.getMessage().contains("used by another process")) {
+ return;
+ }
+
+ throw ex;
+ } finally {
+ if (load) {
+ System.load(targetFile.getAbsolutePath());
+ }
+ if(in != null){
+ in.close();
+ }
+ if(out != null){
+ out.close();
+ }
+ }
+ logger.log(Level.FINE, "Copied {0} to {1}", new Object[]{fullname, targetFile});
+ }
+
+ protected static boolean isUsingNativeBullet() {
+ try {
+ Class clazz = Class.forName("com.jme3.bullet.util.NativeMeshUtil");
+ return clazz != null;
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+
+ public static void extractNativeLibs(Platform platform, AppSettings settings) throws IOException {
+ String renderer = settings.getRenderer();
+ String audioRenderer = settings.getAudioRenderer();
+ boolean needLWJGL = false;
+ boolean needOAL = false;
+ boolean needJInput = false;
+ boolean needNativeBullet = isUsingNativeBullet();
+
+ if (renderer != null) {
+ if (renderer.startsWith("LWJGL")) {
+ needLWJGL = true;
+ }
+ }
+ if (audioRenderer != null) {
+ if (audioRenderer.equals("LWJGL")) {
+ needLWJGL = true;
+ needOAL = true;
+ }
+ }
+ needJInput = settings.useJoysticks();
+
+ String libraryPath = getExtractionDir().toString();
+ if (needLWJGL) {
+ logger.log(Level.INFO, "Extraction Directory: {0}", getExtractionDir().toString());
+
+ // LWJGL supports this feature where
+ // it can load libraries from this path.
+ System.setProperty("org.lwjgl.librarypath", libraryPath);
+ }
+ if (needJInput) {
+ // AND Luckily enough JInput supports the same feature.
+ System.setProperty("net.java.games.input.librarypath", libraryPath);
+ }
+
+ switch (platform) {
+ case Windows64:
+ if (needLWJGL) {
+ extractNativeLib("windows", "lwjgl64");
+ }
+ if (needOAL) {
+ extractNativeLib("windows", "OpenAL64");
+ }
+ if (needJInput) {
+ extractNativeLib("windows", "jinput-dx8_64");
+ extractNativeLib("windows", "jinput-raw_64");
+ }
+ if (needNativeBullet) {
+ extractNativeLib("windows", "bulletjme64", true, false);
+ }
+ break;
+ case Windows32:
+ if (needLWJGL) {
+ extractNativeLib("windows", "lwjgl");
+ }
+ if (needOAL) {
+ extractNativeLib("windows", "OpenAL32");
+ }
+ if (needJInput) {
+ extractNativeLib("windows", "jinput-dx8");
+ extractNativeLib("windows", "jinput-raw");
+ }
+ if (needNativeBullet) {
+ extractNativeLib("windows", "bulletjme", true, false);
+ }
+ break;
+ case Linux64:
+ if (needLWJGL) {
+ extractNativeLib("linux", "lwjgl64");
+ }
+ if (needJInput) {
+ extractNativeLib("linux", "jinput-linux64");
+ }
+ if (needOAL) {
+ extractNativeLib("linux", "openal64");
+ }
+ if (needNativeBullet) {
+ extractNativeLib("linux", "bulletjme64", true, false);
+ }
+ break;
+ case Linux32:
+ if (needLWJGL) {
+ extractNativeLib("linux", "lwjgl");
+ }
+ if (needJInput) {
+ extractNativeLib("linux", "jinput-linux");
+ }
+ if (needOAL) {
+ extractNativeLib("linux", "openal");
+ }
+ if (needNativeBullet) {
+ extractNativeLib("linux", "bulletjme", true, false);
+ }
+ break;
+ case MacOSX_PPC32:
+ case MacOSX32:
+ case MacOSX_PPC64:
+ case MacOSX64:
+ if (needLWJGL) {
+ extractNativeLib("macosx", "lwjgl");
+ }
+// if (needOAL)
+// extractNativeLib("macosx", "openal");
+ if (needJInput) {
+ extractNativeLib("macosx", "jinput-osx");
+ }
+ if (needNativeBullet) {
+ extractNativeLib("macosx", "bulletjme", true, false);
+ }
+ break;
+ }
+ }
+}
diff --git a/engine/src/desktop/com/jme3/system/awt/AwtPanel.java b/engine/src/desktop/com/jme3/system/awt/AwtPanel.java
new file mode 100644
index 0000000..b84565b
--- /dev/null
+++ b/engine/src/desktop/com/jme3/system/awt/AwtPanel.java
@@ -0,0 +1,293 @@
+package com.jme3.system.awt;
+
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.Screenshots;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferStrategy;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AwtPanel extends Canvas implements SceneProcessor {
+
+ private boolean attachAsMain = false;
+
+ private BufferedImage img;
+ private FrameBuffer fb;
+ private ByteBuffer byteBuf;
+ private IntBuffer intBuf;
+ private RenderManager rm;
+ private PaintMode paintMode;
+ private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
+
+ // Visibility/drawing vars
+ private BufferStrategy strategy;
+ private AffineTransformOp transformOp;
+ private AtomicBoolean hasNativePeer = new AtomicBoolean(false);
+ private AtomicBoolean showing = new AtomicBoolean(false);
+ private AtomicBoolean repaintRequest = new AtomicBoolean(false);
+
+ // Reshape vars
+ private int newWidth = 1;
+ private int newHeight = 1;
+ private AtomicBoolean reshapeNeeded = new AtomicBoolean(false);
+ private final Object lock = new Object();
+
+ public AwtPanel(PaintMode paintMode){
+ this.paintMode = paintMode;
+
+ if (paintMode == PaintMode.Accelerated){
+ setIgnoreRepaint(true);
+ }
+
+ addComponentListener(new ComponentAdapter(){
+ @Override
+ public void componentResized(ComponentEvent e) {
+ synchronized (lock){
+ int newWidth2 = Math.max(getWidth(), 1);
+ int newHeight2 = Math.max(getHeight(), 1);
+ if (newWidth != newWidth2 || newHeight != newHeight2){
+ newWidth = newWidth2;
+ newHeight = newHeight2;
+ reshapeNeeded.set(true);
+ System.out.println("EDT: componentResized " + newWidth + ", " + newHeight);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void addNotify(){
+ super.addNotify();
+
+ synchronized (lock){
+ hasNativePeer.set(true);
+ System.out.println("EDT: addNotify");
+ }
+
+ requestFocusInWindow();
+ }
+
+ @Override
+ public void removeNotify(){
+ synchronized (lock){
+ hasNativePeer.set(false);
+ System.out.println("EDT: removeNotify");
+ }
+
+ super.removeNotify();
+ }
+
+ @Override
+ public void paint(Graphics g){
+ Graphics2D g2d = (Graphics2D) g;
+ synchronized (lock){
+ g2d.drawImage(img, transformOp, 0, 0);
+ }
+ }
+
+ public boolean checkVisibilityState(){
+ if (!hasNativePeer.get()){
+ if (strategy != null){
+// strategy.dispose();
+ strategy = null;
+ System.out.println("OGL: Not visible. Destroy strategy.");
+ }
+ return false;
+ }
+
+ boolean currentShowing = isShowing();
+ if (showing.getAndSet(currentShowing) != currentShowing){
+ if (currentShowing){
+ System.out.println("OGL: Enter showing state.");
+ }else{
+ System.out.println("OGL: Exit showing state.");
+ }
+ }
+ return currentShowing;
+ }
+
+ public void repaintInThread(){
+ // Convert screenshot.
+ byteBuf.clear();
+ rm.getRenderer().readFrameBuffer(fb, byteBuf);
+
+ synchronized (lock){
+ // All operations on img must be synchronized
+ // as it is accessed from EDT.
+ Screenshots.convertScreenShot2(intBuf, img);
+ repaint();
+ }
+ }
+
+ public void drawFrameInThread(){
+ // Convert screenshot.
+ byteBuf.clear();
+ rm.getRenderer().readFrameBuffer(fb, byteBuf);
+ Screenshots.convertScreenShot2(intBuf, img);
+
+ synchronized (lock){
+ // All operations on strategy should be synchronized (?)
+ if (strategy == null){
+ try {
+ createBufferStrategy(1,
+ new BufferCapabilities(
+ new ImageCapabilities(true),
+ new ImageCapabilities(true),
+ BufferCapabilities.FlipContents.UNDEFINED)
+ );
+ } catch (AWTException ex) {
+ ex.printStackTrace();
+ }
+ strategy = getBufferStrategy();
+ System.out.println("OGL: Visible. Create strategy.");
+ }
+
+ // Draw screenshot.
+ do {
+ do {
+ Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
+ if (g2d == null){
+ System.out.println("OGL: DrawGraphics was null.");
+ return;
+ }
+
+ g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_SPEED);
+
+ g2d.drawImage(img, transformOp, 0, 0);
+ g2d.dispose();
+ strategy.show();
+ } while (strategy.contentsRestored());
+ } while (strategy.contentsLost());
+ }
+ }
+
+ public boolean isActiveDrawing(){
+ return paintMode != PaintMode.OnRequest && showing.get();
+ }
+
+ public void attachTo(boolean overrideMainFramebuffer, ViewPort ... vps){
+ if (viewPorts.size() > 0){
+ for (ViewPort vp : viewPorts){
+ vp.setOutputFrameBuffer(null);
+ }
+ viewPorts.get(viewPorts.size()-1).removeProcessor(this);
+ }
+
+ viewPorts.addAll(Arrays.asList(vps));
+ viewPorts.get(viewPorts.size()-1).addProcessor(this);
+
+ this.attachAsMain = overrideMainFramebuffer;
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ if (this.rm == null){
+ // First time called in OGL thread
+ this.rm = rm;
+ reshapeInThread(1, 1);
+ }
+ }
+
+ private void reshapeInThread(int width, int height) {
+ byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4);
+ intBuf = byteBuf.asIntBuffer();
+
+ fb = new FrameBuffer(width, height, 1);
+ fb.setDepthBuffer(Format.Depth);
+ fb.setColorBuffer(Format.RGB8);
+
+ if (attachAsMain){
+ rm.getRenderer().setMainFrameBufferOverride(fb);
+ }
+
+ synchronized (lock){
+ img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ }
+
+// synchronized (lock){
+// img = (BufferedImage) getGraphicsConfiguration().createCompatibleImage(width, height);
+// }
+
+ AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
+ tx.translate(0, -img.getHeight());
+ transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+
+ for (ViewPort vp : viewPorts){
+ if (!attachAsMain){
+ vp.setOutputFrameBuffer(fb);
+ }
+ vp.getCamera().resize(width, height, true);
+
+ // NOTE: Hack alert. This is done ONLY for custom framebuffers.
+ // Main framebuffer should use RenderManager.notifyReshape().
+ for (SceneProcessor sp : vp.getProcessors()){
+ sp.reshape(vp, width, height);
+ }
+ }
+ }
+
+ public boolean isInitialized() {
+ return fb != null;
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ @Override
+ public void invalidate(){
+ // For "PaintMode.OnDemand" only.
+ repaintRequest.set(true);
+ }
+
+ public void postFrame(FrameBuffer out) {
+ if (!attachAsMain && out != fb){
+ throw new IllegalStateException("Why did you change the output framebuffer?");
+ }
+
+ if (reshapeNeeded.getAndSet(false)){
+ reshapeInThread(newWidth, newHeight);
+ }else{
+ if (!checkVisibilityState()){
+ return;
+ }
+
+ switch (paintMode){
+ case Accelerated:
+ drawFrameInThread();
+ break;
+ case Repaint:
+ repaintInThread();
+ break;
+ case OnRequest:
+ if (repaintRequest.getAndSet(false)){
+ repaintInThread();
+ }
+ break;
+ }
+ }
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public void cleanup() {
+ }
+}
diff --git a/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java b/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java
new file mode 100644
index 0000000..c980c40
--- /dev/null
+++ b/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java
@@ -0,0 +1,202 @@
+package com.jme3.system.awt;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.awt.AwtKeyInput;
+import com.jme3.input.awt.AwtMouseInput;
+import com.jme3.renderer.Renderer;
+import com.jme3.system.*;
+import java.util.ArrayList;
+
+public class AwtPanelsContext implements JmeContext {
+
+ protected JmeContext actualContext;
+ protected AppSettings settings = new AppSettings(true);
+ protected SystemListener listener;
+ protected ArrayList<AwtPanel> panels = new ArrayList<AwtPanel>();
+ protected AwtPanel inputSource;
+
+ protected AwtMouseInput mouseInput = new AwtMouseInput();
+ protected AwtKeyInput keyInput = new AwtKeyInput();
+
+ protected boolean lastThrottleState = false;
+
+ private class AwtPanelsListener implements SystemListener {
+
+ public void initialize() {
+ initInThread();
+ }
+
+ public void reshape(int width, int height) {
+ throw new IllegalStateException();
+ }
+
+ public void update() {
+ updateInThread();
+ }
+
+ public void requestClose(boolean esc) {
+ // shouldn't happen
+ throw new IllegalStateException();
+ }
+
+ public void gainFocus() {
+ // shouldn't happen
+ throw new IllegalStateException();
+ }
+
+ public void loseFocus() {
+ // shouldn't happen
+ throw new IllegalStateException();
+ }
+
+ public void handleError(String errorMsg, Throwable t) {
+ listener.handleError(errorMsg, t);
+ }
+
+ public void destroy() {
+ destroyInThread();
+ }
+ }
+
+ public void setInputSource(AwtPanel panel){
+ if (!panels.contains(panel))
+ throw new IllegalArgumentException();
+
+ inputSource = panel;
+ mouseInput.setInputSource(panel);
+ keyInput.setInputSource(panel);
+ }
+
+ public Type getType() {
+ return Type.OffscreenSurface;
+ }
+
+ public void setSystemListener(SystemListener listener) {
+ this.listener = listener;
+ }
+
+ public AppSettings getSettings() {
+ return settings;
+ }
+
+ public Renderer getRenderer() {
+ return actualContext.getRenderer();
+ }
+
+ public MouseInput getMouseInput() {
+ return mouseInput;
+ }
+
+ public KeyInput getKeyInput() {
+ return keyInput;
+ }
+
+ public JoyInput getJoyInput() {
+ return null;
+ }
+
+ public TouchInput getTouchInput() {
+ return null;
+ }
+
+ public Timer getTimer() {
+ return actualContext.getTimer();
+ }
+
+ public boolean isCreated() {
+ return actualContext != null && actualContext.isCreated();
+ }
+
+ public boolean isRenderable() {
+ return actualContext != null && actualContext.isRenderable();
+ }
+
+ public AwtPanelsContext(){
+ }
+
+ public AwtPanel createPanel(PaintMode paintMode){
+ AwtPanel panel = new AwtPanel(paintMode);
+ panels.add(panel);
+ return panel;
+ }
+
+ private void initInThread(){
+ listener.initialize();
+ }
+
+ private void updateInThread(){
+ // Check if throttle required
+ boolean needThrottle = true;
+
+ for (AwtPanel panel : panels){
+ if (panel.isActiveDrawing()){
+ needThrottle = false;
+ break;
+ }
+ }
+
+ if (lastThrottleState != needThrottle){
+ lastThrottleState = needThrottle;
+ if (lastThrottleState){
+ System.out.println("OGL: Throttling update loop.");
+ }else{
+ System.out.println("OGL: Ceased throttling update loop.");
+ }
+ }
+
+ if (needThrottle){
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ }
+ }
+
+ listener.update();
+ }
+
+ private void destroyInThread(){
+ listener.destroy();
+ }
+
+ public void setSettings(AppSettings settings) {
+ this.settings.copyFrom(settings);
+ this.settings.setRenderer(AppSettings.LWJGL_OPENGL2);
+ if (actualContext != null){
+ actualContext.setSettings(settings);
+ }
+ }
+
+ public void create(boolean waitFor) {
+ if (actualContext != null){
+ throw new IllegalStateException("Already created");
+ }
+
+ actualContext = JmeSystem.newContext(settings, Type.OffscreenSurface);
+ actualContext.setSystemListener(new AwtPanelsListener());
+ actualContext.create(waitFor);
+ }
+
+ public void destroy(boolean waitFor) {
+ if (actualContext == null)
+ throw new IllegalStateException("Not created");
+
+ // destroy parent context
+ actualContext.destroy(waitFor);
+ }
+
+ public void setTitle(String title) {
+ // not relevant, ignore
+ }
+
+ public void setAutoFlushFrames(boolean enabled) {
+ // not relevant, ignore
+ }
+
+ public void restart() {
+ // only relevant if changing pixel format.
+ }
+
+}
diff --git a/engine/src/desktop/com/jme3/system/awt/PaintMode.java b/engine/src/desktop/com/jme3/system/awt/PaintMode.java
new file mode 100644
index 0000000..66938dd
--- /dev/null
+++ b/engine/src/desktop/com/jme3/system/awt/PaintMode.java
@@ -0,0 +1,7 @@
+package com.jme3.system.awt;
+
+public enum PaintMode {
+ Accelerated,
+ Repaint,
+ OnRequest;
+}
diff --git a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java
new file mode 100644
index 0000000..3f471f7
--- /dev/null
+++ b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import javax.imageio.ImageIO;
+
+public class AWTLoader implements AssetLoader {
+
+ public static final ColorModel AWT_RGBA4444 = new DirectColorModel(16,
+ 0xf000,
+ 0x0f00,
+ 0x00f0,
+ 0x000f);
+
+ public static final ColorModel AWT_RGBA5551
+ = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ new int[]{5, 5, 5, 1},
+ true,
+ false,
+ Transparency.BITMASK,
+ DataBuffer.TYPE_BYTE);
+
+ private Object extractImageData(BufferedImage img){
+ DataBuffer buf = img.getRaster().getDataBuffer();
+ switch (buf.getDataType()){
+ case DataBuffer.TYPE_BYTE:
+ DataBufferByte byteBuf = (DataBufferByte) buf;
+ return byteBuf.getData();
+ case DataBuffer.TYPE_USHORT:
+ DataBufferUShort shortBuf = (DataBufferUShort) buf;
+ return shortBuf.getData();
+ }
+ return null;
+ }
+
+ private void flipImage(byte[] img, int width, int height, int bpp){
+ int scSz = (width * bpp) / 8;
+ byte[] sln = new byte[scSz];
+ int y2 = 0;
+ for (int y1 = 0; y1 < height / 2; y1++){
+ y2 = height - y1 - 1;
+ System.arraycopy(img, y1 * scSz, sln, 0, scSz);
+ System.arraycopy(img, y2 * scSz, img, y1 * scSz, scSz);
+ System.arraycopy(sln, 0, img, y2 * scSz, scSz);
+ }
+ }
+
+ private void flipImage(short[] img, int width, int height, int bpp){
+ int scSz = (width * bpp) / 8;
+ scSz /= 2; // Because shorts are 2 bytes
+ short[] sln = new short[scSz];
+ int y2 = 0;
+ for (int y1 = 0; y1 < height / 2; y1++){
+ y2 = height - y1 - 1;
+ System.arraycopy(img, y1 * scSz, sln, 0, scSz);
+ System.arraycopy(img, y2 * scSz, img, y1 * scSz, scSz);
+ System.arraycopy(sln, 0, img, y2 * scSz, scSz);
+ }
+ }
+
+ public Image load(BufferedImage img, boolean flipY){
+ int width = img.getWidth();
+ int height = img.getHeight();
+
+ switch (img.getType()){
+ case BufferedImage.TYPE_4BYTE_ABGR: // most common in PNG images w/ alpha
+ byte[] dataBuf1 = (byte[]) extractImageData(img);
+ if (flipY)
+ flipImage(dataBuf1, width, height, 32);
+
+ ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4);
+ data1.put(dataBuf1);
+ return new Image(Format.ABGR8, width, height, data1);
+ case BufferedImage.TYPE_3BYTE_BGR: // most common in JPEG images
+ byte[] dataBuf2 = (byte[]) extractImageData(img);
+ if (flipY)
+ flipImage(dataBuf2, width, height, 24);
+
+ ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3);
+ data2.put(dataBuf2);
+ return new Image(Format.BGR8, width, height, data2);
+ case BufferedImage.TYPE_BYTE_GRAY: // grayscale fonts
+ byte[] dataBuf3 = (byte[]) extractImageData(img);
+ if (flipY)
+ flipImage(dataBuf3, width, height, 8);
+ ByteBuffer data3 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight());
+ data3.put(dataBuf3);
+ return new Image(Format.Luminance8, width, height, data3);
+ case BufferedImage.TYPE_USHORT_GRAY: // grayscale heightmap
+ short[] dataBuf4 = (short[]) extractImageData(img);
+ if (flipY)
+ flipImage(dataBuf4, width, height, 16);
+
+ ByteBuffer data4 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*2);
+ data4.asShortBuffer().put(dataBuf4);
+ return new Image(Format.Luminance16, width, height, data4);
+ default:
+ break;
+ }
+
+ if (img.getTransparency() == Transparency.OPAQUE){
+ ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3);
+ // no alpha
+ for (int y = 0; y < height; y++){
+ for (int x = 0; x < width; x++){
+ int ny = y;
+ if (flipY){
+ ny = height - y - 1;
+ }
+
+ int rgb = img.getRGB(x,ny);
+ byte r = (byte) ((rgb & 0x00FF0000) >> 16);
+ byte g = (byte) ((rgb & 0x0000FF00) >> 8);
+ byte b = (byte) ((rgb & 0x000000FF));
+ data.put(r).put(g).put(b);
+ }
+ }
+ data.flip();
+ return new Image(Format.RGB8, width, height, data);
+ }else{
+ ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4);
+ // no alpha
+ for (int y = 0; y < height; y++){
+ for (int x = 0; x < width; x++){
+ int ny = y;
+ if (flipY){
+ ny = height - y - 1;
+ }
+
+ int rgb = img.getRGB(x,ny);
+ byte a = (byte) ((rgb & 0xFF000000) >> 24);
+ byte r = (byte) ((rgb & 0x00FF0000) >> 16);
+ byte g = (byte) ((rgb & 0x0000FF00) >> 8);
+ byte b = (byte) ((rgb & 0x000000FF));
+ data.put(r).put(g).put(b).put(a);
+ }
+ }
+ data.flip();
+ return new Image(Format.RGBA8, width, height, data);
+ }
+ }
+
+ public Image load(InputStream in, boolean flipY) throws IOException{
+ ImageIO.setUseCache(false);
+ BufferedImage img = ImageIO.read(in);
+ if (img == null)
+ return null;
+
+ return load(img, flipY);
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (ImageIO.getImageWritersBySuffix(info.getKey().getExtension()) != null){
+
+ boolean flip = ((TextureKey) info.getKey()).isFlipY();
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ Image img = load(in, flip);
+ return img;
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/engine/src/desktop/com/jme3/util/Screenshots.java b/engine/src/desktop/com/jme3/util/Screenshots.java
new file mode 100644
index 0000000..bbb6966
--- /dev/null
+++ b/engine/src/desktop/com/jme3/util/Screenshots.java
@@ -0,0 +1,78 @@
+package com.jme3.util;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.WritableRaster;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+public final class Screenshots {
+
+ public static void convertScreenShot2(IntBuffer bgraBuf, BufferedImage out){
+ WritableRaster wr = out.getRaster();
+ DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
+
+ int[] cpuArray = db.getData();
+
+ bgraBuf.clear();
+ bgraBuf.get(cpuArray);
+
+// int width = wr.getWidth();
+// int height = wr.getHeight();
+//
+// // flip the components the way AWT likes them
+// for (int y = 0; y < height / 2; y++){
+// for (int x = 0; x < width; x++){
+// int inPtr = (y * width + x);
+// int outPtr = ((height-y-1) * width + x);
+// int pixel = cpuArray[inPtr];
+// cpuArray[inPtr] = cpuArray[outPtr];
+// cpuArray[outPtr] = pixel;
+// }
+// }
+ }
+
+ public static void convertScreenShot(ByteBuffer bgraBuf, BufferedImage out){
+ WritableRaster wr = out.getRaster();
+ DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
+
+ byte[] cpuArray = db.getData();
+
+ // copy native memory to java memory
+ bgraBuf.clear();
+ bgraBuf.get(cpuArray);
+ bgraBuf.clear();
+
+ int width = wr.getWidth();
+ int height = wr.getHeight();
+
+ // flip the components the way AWT likes them
+ for (int y = 0; y < height / 2; y++){
+ for (int x = 0; x < width; x++){
+ int inPtr = (y * width + x) * 4;
+ int outPtr = ((height-y-1) * width + x) * 4;
+
+ byte b1 = cpuArray[inPtr+0];
+ byte g1 = cpuArray[inPtr+1];
+ byte r1 = cpuArray[inPtr+2];
+ byte a1 = cpuArray[inPtr+3];
+
+ byte b2 = cpuArray[outPtr+0];
+ byte g2 = cpuArray[outPtr+1];
+ byte r2 = cpuArray[outPtr+2];
+ byte a2 = cpuArray[outPtr+3];
+
+ cpuArray[outPtr+0] = a1;
+ cpuArray[outPtr+1] = b1;
+ cpuArray[outPtr+2] = g1;
+ cpuArray[outPtr+3] = r1;
+
+ cpuArray[inPtr+0] = a2;
+ cpuArray[inPtr+1] = b2;
+ cpuArray[inPtr+2] = g2;
+ cpuArray[inPtr+3] = r2;
+ }
+ }
+ }
+}
diff --git a/engine/src/desktop/jme3tools/converters/ImageToAwt.java b/engine/src/desktop/jme3tools/converters/ImageToAwt.java
new file mode 100644
index 0000000..836cc7a
--- /dev/null
+++ b/engine/src/desktop/jme3tools/converters/ImageToAwt.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters;
+
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.plugins.AWTLoader;
+import com.jme3.util.BufferUtils;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.EnumMap;
+
+public class ImageToAwt {
+
+ private static final EnumMap<Format, DecodeParams> params
+ = new EnumMap<Format, DecodeParams>(Format.class);
+
+ private static class DecodeParams {
+
+ final int bpp, am, rm, gm, bm, as, rs, gs, bs, im, is;
+
+ public DecodeParams(int bpp, int am, int rm, int gm, int bm, int as, int rs, int gs, int bs, int im, int is) {
+ this.bpp = bpp;
+ this.am = am;
+ this.rm = rm;
+ this.gm = gm;
+ this.bm = bm;
+ this.as = as;
+ this.rs = rs;
+ this.gs = gs;
+ this.bs = bs;
+ this.im = im;
+ this.is = is;
+ }
+
+ public DecodeParams(int bpp, int rm, int rs, int im, int is, boolean alpha){
+ this.bpp = bpp;
+ if (alpha){
+ this.am = rm;
+ this.as = rs;
+ this.rm = 0;
+ this.rs = 0;
+ }else{
+ this.rm = rm;
+ this.rs = rs;
+ this.am = 0;
+ this.as = 0;
+ }
+
+ this.gm = 0;
+ this.bm = 0;
+ this.gs = 0;
+ this.bs = 0;
+ this.im = im;
+ this.is = is;
+ }
+
+ public DecodeParams(int bpp, int rm, int rs, int im, int is){
+ this(bpp, rm, rs, im, is, false);
+ }
+ }
+
+ static {
+ final int mx___ = 0xff000000;
+ final int m_x__ = 0x00ff0000;
+ final int m__x_ = 0x0000ff00;
+ final int m___x = 0x000000ff;
+ final int sx___ = 24;
+ final int s_x__ = 16;
+ final int s__x_ = 8;
+ final int s___x = 0;
+ final int mxxxx = 0xffffffff;
+ final int sxxxx = 0;
+
+ final int m4x___ = 0xf000;
+ final int m4_x__ = 0x0f00;
+ final int m4__x_ = 0x00f0;
+ final int m4___x = 0x000f;
+ final int s4x___ = 12;
+ final int s4_x__ = 8;
+ final int s4__x_ = 4;
+ final int s4___x = 0;
+
+ final int m5___ = 0xf800;
+ final int m_5__ = 0x07c0;
+ final int m__5_ = 0x003e;
+ final int m___1 = 0x0001;
+
+ final int s5___ = 11;
+ final int s_5__ = 6;
+ final int s__5_ = 1;
+ final int s___1 = 0;
+
+ final int m5__ = 0xf800;
+ final int m_6_ = 0x07e0;
+ final int m__5 = 0x001f;
+
+ final int s5__ = 11;
+ final int s_6_ = 5;
+ final int s__5 = 0;
+
+ final int mxx__ = 0xffff0000;
+ final int sxx__ = 32;
+ final int m__xx = 0x0000ffff;
+ final int s__xx = 0;
+
+ // note: compressed, depth, or floating point formats not included here..
+
+ params.put(Format.ABGR8, new DecodeParams(4, mx___, m___x, m__x_, m_x__,
+ sx___, s___x, s__x_, s_x__,
+ mxxxx, sxxxx));
+ params.put(Format.ARGB4444, new DecodeParams(2, m4x___, m4_x__, m4__x_, m4___x,
+ s4x___, s4_x__, s4__x_, s4___x,
+ mxxxx, sxxxx));
+ params.put(Format.Alpha16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, true));
+ params.put(Format.Alpha8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, true));
+ params.put(Format.BGR8, new DecodeParams(3, 0, m___x, m__x_, m_x__,
+ 0, s___x, s__x_, s_x__,
+ mxxxx, sxxxx));
+ params.put(Format.Luminance16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));
+ params.put(Format.Luminance8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));
+ params.put(Format.Luminance16Alpha16, new DecodeParams(4, m__xx, mxx__, 0, 0,
+ s__xx, sxx__, 0, 0,
+ mxxxx, sxxxx));
+ params.put(Format.Luminance16F, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));
+ params.put(Format.Luminance16FAlpha16F, new DecodeParams(4, m__xx, mxx__, 0, 0,
+ s__xx, sxx__, 0, 0,
+ mxxxx, sxxxx));
+ params.put(Format.Luminance32F, new DecodeParams(4, mxxxx, sxxxx, mxxxx, sxxxx, false));
+ params.put(Format.Luminance8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));
+ params.put(Format.RGB5A1, new DecodeParams(2, m___1, m5___, m_5__, m__5_,
+ s___1, s5___, s_5__, s__5_,
+ mxxxx, sxxxx));
+ params.put(Format.RGB565, new DecodeParams(2, 0, m5__ , m_6_ , m__5,
+ 0, s5__ , s_6_ , s__5,
+ mxxxx, sxxxx));
+ params.put(Format.RGB8, new DecodeParams(3, 0, m_x__, m__x_, m___x,
+ 0, s_x__, s__x_, s___x,
+ mxxxx, sxxxx));
+ params.put(Format.RGBA8, new DecodeParams(4, m___x, mx___, m_x__, m__x_,
+ s___x, sx___, s_x__, s__x_,
+ mxxxx, sxxxx));
+ }
+
+ private static int Ix(int x, int y, int w){
+ return y * w + x;
+ }
+
+ private static int readPixel(ByteBuffer buf, int idx, int bpp){
+ buf.position(idx);
+ int original = buf.get() & 0xff;
+ while ((--bpp) > 0){
+ original = (original << 8) | (buf.get() & 0xff);
+ }
+ return original;
+ }
+
+ private static void writePixel(ByteBuffer buf, int idx, int pixel, int bpp){
+ buf.position(idx);
+ while ((--bpp) >= 0){
+// pixel = pixel >> 8;
+ byte bt = (byte) ((pixel >> (bpp * 8)) & 0xff);
+// buf.put( (byte) (pixel & 0xff) );
+ buf.put(bt);
+ }
+ }
+
+
+ /**
+ * Convert an AWT image to jME image.
+ */
+ public static void convert(BufferedImage image, Format format, ByteBuffer buf){
+ DecodeParams p = params.get(format);
+ if (p == null)
+ throw new UnsupportedOperationException("Image format " + format + " is not supported");
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ boolean alpha = true;
+ boolean luminance = false;
+
+ int reductionA = 8 - Integer.bitCount(p.am);
+ int reductionR = 8 - Integer.bitCount(p.rm);
+ int reductionG = 8 - Integer.bitCount(p.gm);
+ int reductionB = 8 - Integer.bitCount(p.bm);
+
+ int initialPos = buf.position();
+ for (int y = 0; y < height; y++){
+ for (int x = 0; x < width; x++){
+ // Get ARGB
+ int argb = image.getRGB(x, y);
+
+ // Extract color components
+ int a = (argb & 0xff000000) >> 24;
+ int r = (argb & 0x00ff0000) >> 16;
+ int g = (argb & 0x0000ff00) >> 8;
+ int b = (argb & 0x000000ff);
+
+ // Remove anything after 8 bits
+ a = a & 0xff;
+ r = r & 0xff;
+ g = g & 0xff;
+ b = b & 0xff;
+
+ // Set full alpha if target image has no alpha
+ if (!alpha)
+ a = 0xff;
+
+ // Convert color to luminance if target
+ // image is in luminance format
+ if (luminance){
+ // convert RGB to luminance
+ }
+
+ // Do bit reduction, assumes proper rounding has already been
+ // done.
+ a = a >> reductionA;
+ r = r >> reductionR;
+ g = g >> reductionG;
+ b = b >> reductionB;
+
+ // Put components into appropriate positions
+ a = (a << p.as) & p.am;
+ r = (r << p.rs) & p.rm;
+ g = (g << p.gs) & p.gm;
+ b = (b << p.bs) & p.bm;
+
+ int outputPixel = ((a | r | g | b) << p.is) & p.im;
+ int i = initialPos + (Ix(x,y,width) * p.bpp);
+ writePixel(buf, i, outputPixel, p.bpp);
+ }
+ }
+ }
+
+ private static final double LOG2 = Math.log(2);
+
+ public static void createData(Image image, boolean mipmaps){
+ int bpp = image.getFormat().getBitsPerPixel();
+ int w = image.getWidth();
+ int h = image.getHeight();
+ if (!mipmaps){
+ image.setData(BufferUtils.createByteBuffer(w*h*bpp/8));
+ return;
+ }
+ int expectedMipmaps = 1 + (int) Math.ceil(Math.log(Math.max(h, w)) / LOG2);
+ int[] mipMapSizes = new int[expectedMipmaps];
+ int total = 0;
+ for (int i = 0; i < mipMapSizes.length; i++){
+ int size = (w * h * bpp) / 8;
+ total += size;
+ mipMapSizes[i] = size;
+ w /= 2;
+ h /= 2;
+ }
+ image.setMipMapSizes(mipMapSizes);
+ image.setData(BufferUtils.createByteBuffer(total));
+ }
+
+ /**
+ * Convert the image from the given format to the output format.
+ * It is assumed that both images have buffers with the appropriate
+ * number of elements and that both have the same dimensions.
+ *
+ * @param input
+ * @param output
+ */
+ public static void convert(Image input, Image output){
+ DecodeParams inParams = params.get(input.getFormat());
+ DecodeParams outParams = params.get(output.getFormat());
+
+ if (inParams == null || outParams == null)
+ throw new UnsupportedOperationException();
+
+ int width = input.getWidth();
+ int height = input.getHeight();
+
+ if (width != output.getWidth() || height != output.getHeight())
+ throw new IllegalArgumentException();
+
+ ByteBuffer inData = input.getData(0);
+
+ boolean inAlpha = false;
+ boolean inLum = false;
+ boolean inRGB = false;
+ if (inParams.am != 0) {
+ inAlpha = true;
+ }
+
+ if (inParams.rm != 0 && inParams.gm == 0 && inParams.bm == 0) {
+ inLum = true;
+ } else if (inParams.rm != 0 && inParams.gm != 0 && inParams.bm != 0) {
+ inRGB = true;
+ }
+
+ int expansionA = 8 - Integer.bitCount(inParams.am);
+ int expansionR = 8 - Integer.bitCount(inParams.rm);
+ int expansionG = 8 - Integer.bitCount(inParams.gm);
+ int expansionB = 8 - Integer.bitCount(inParams.bm);
+
+ int inputPixel;
+ for (int y = 0; y < height; y++){
+ for (int x = 0; x < width; x++){
+ int i = Ix(x, y, width) * inParams.bpp;
+ inputPixel = (readPixel(inData, i, inParams.bpp) & inParams.im) >> inParams.is;
+
+ int a = (inputPixel & inParams.am) >> inParams.as;
+ int r = (inputPixel & inParams.rm) >> inParams.rs;
+ int g = (inputPixel & inParams.gm) >> inParams.gs;
+ int b = (inputPixel & inParams.bm) >> inParams.bs;
+
+ r = r & 0xff;
+ g = g & 0xff;
+ b = b & 0xff;
+ a = a & 0xff;
+
+ a = a << expansionA;
+ r = r << expansionR;
+ g = g << expansionG;
+ b = b << expansionB;
+
+ if (inLum)
+ b = g = r;
+
+ if (!inAlpha)
+ a = 0xff;
+
+// int argb = (a << 24) | (r << 16) | (g << 8) | b;
+// out.setRGB(x, y, argb);
+ }
+ }
+ }
+
+ public static BufferedImage convert(Image image, boolean do16bit, boolean fullalpha, int mipLevel){
+ Format format = image.getFormat();
+ DecodeParams p = params.get(image.getFormat());
+ if (p == null)
+ throw new UnsupportedOperationException();
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ int level = mipLevel;
+ while (--level >= 0){
+ width /= 2;
+ height /= 2;
+ }
+
+ ByteBuffer buf = image.getData(0);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ BufferedImage out;
+
+ boolean alpha = false;
+ boolean luminance = false;
+ boolean rgb = false;
+ if (p.am != 0)
+ alpha = true;
+
+ if (p.rm != 0 && p.gm == 0 && p.bm == 0)
+ luminance = true;
+ else if (p.rm != 0 && p.gm != 0 && p.bm != 0)
+ rgb = true;
+
+ // alpha OR luminance but not both
+ if ( (alpha && !rgb && !luminance) || (luminance && !alpha && !rgb) ){
+ out = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ }else if ( (rgb && alpha) || (luminance && alpha) ){
+ if (do16bit){
+ if (fullalpha){
+ ColorModel model = AWTLoader.AWT_RGBA4444;
+ WritableRaster raster = model.createCompatibleWritableRaster(width, width);
+ out = new BufferedImage(model, raster, false, null);
+ }else{
+ // RGB5_A1
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ int[] nBits = {5, 5, 5, 1};
+ int[] bOffs = {0, 1, 2, 3};
+ ColorModel colorModel = new ComponentColorModel(cs, nBits, true, false,
+ Transparency.BITMASK,
+ DataBuffer.TYPE_BYTE);
+ WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
+ width, height,
+ width*2, 2,
+ bOffs, null);
+ out = new BufferedImage(colorModel, raster, false, null);
+ }
+ }else{
+ out = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ }
+ }else{
+ if (do16bit){
+ out = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB);
+ }else{
+ out = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ }
+ }
+
+ int expansionA = 8 - Integer.bitCount(p.am);
+ int expansionR = 8 - Integer.bitCount(p.rm);
+ int expansionG = 8 - Integer.bitCount(p.gm);
+ int expansionB = 8 - Integer.bitCount(p.bm);
+
+ if (expansionR < 0){
+ expansionR = 0;
+ }
+
+ int mipPos = 0;
+ for (int i = 0; i < mipLevel; i++){
+ mipPos += image.getMipMapSizes()[i];
+ }
+ int inputPixel;
+ for (int y = 0; y < height; y++){
+ for (int x = 0; x < width; x++){
+ int i = mipPos + (Ix(x,y,width) * p.bpp);
+ inputPixel = (readPixel(buf,i,p.bpp) & p.im) >> p.is;
+ int a = (inputPixel & p.am) >> p.as;
+ int r = (inputPixel & p.rm) >> p.rs;
+ int g = (inputPixel & p.gm) >> p.gs;
+ int b = (inputPixel & p.bm) >> p.bs;
+
+ r = r & 0xff;
+ g = g & 0xff;
+ b = b & 0xff;
+ a = a & 0xff;
+
+ a = a << expansionA;
+ r = r << expansionR;
+ g = g << expansionG;
+ b = b << expansionB;
+
+ if (luminance)
+ b = g = r;
+
+ if (!alpha)
+ a = 0xff;
+
+ int argb = (a << 24) | (r << 16) | (g << 8) | b;
+ out.setRGB(x, y, argb);
+ }
+ }
+
+ return out;
+ }
+
+}
diff --git a/engine/src/desktop/jme3tools/converters/MipMapGenerator.java b/engine/src/desktop/jme3tools/converters/MipMapGenerator.java
new file mode 100644
index 0000000..a4541e6
--- /dev/null
+++ b/engine/src/desktop/jme3tools/converters/MipMapGenerator.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters;
+
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.plugins.AWTLoader;
+import com.jme3.util.BufferUtils;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+public class MipMapGenerator {
+
+ private static BufferedImage scaleDown(BufferedImage sourceImage, int targetWidth, int targetHeight) {
+ int sourceWidth = sourceImage.getWidth();
+ int sourceHeight = sourceImage.getHeight();
+
+ BufferedImage targetImage = new BufferedImage(targetWidth, targetHeight, sourceImage.getType());
+
+ Graphics2D g = targetImage.createGraphics();
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ g.drawImage(sourceImage, 0, 0, targetWidth, targetHeight, 0, 0, sourceWidth, sourceHeight, null);
+ g.dispose();
+
+ return targetImage;
+ }
+
+ public static void resizeToPowerOf2(Image image){
+ BufferedImage original = ImageToAwt.convert(image, false, true, 0);
+ int potWidth = FastMath.nearestPowerOfTwo(image.getWidth());
+ int potHeight = FastMath.nearestPowerOfTwo(image.getHeight());
+ int potSize = Math.max(potWidth, potHeight);
+
+ BufferedImage scaled = scaleDown(original, potSize, potSize);
+
+ AWTLoader loader = new AWTLoader();
+ Image output = loader.load(scaled, false);
+
+ image.setWidth(potSize);
+ image.setHeight(potSize);
+ image.setDepth(0);
+ image.setData(output.getData(0));
+ image.setFormat(output.getFormat());
+ image.setMipMapSizes(null);
+ }
+
+ public static void generateMipMaps(Image image){
+ BufferedImage original = ImageToAwt.convert(image, false, true, 0);
+ int width = original.getWidth();
+ int height = original.getHeight();
+ int level = 0;
+
+ BufferedImage current = original;
+ AWTLoader loader = new AWTLoader();
+ ArrayList<ByteBuffer> output = new ArrayList<ByteBuffer>();
+ int totalSize = 0;
+ Format format = null;
+
+ while (height >= 1 || width >= 1){
+ Image converted = loader.load(current, false);
+ format = converted.getFormat();
+ output.add(converted.getData(0));
+ totalSize += converted.getData(0).capacity();
+
+ if(height == 1 || width == 1) {
+ break;
+ }
+
+ level++;
+
+ height /= 2;
+ width /= 2;
+
+ current = scaleDown(current, width, height);
+ }
+
+ ByteBuffer combinedData = BufferUtils.createByteBuffer(totalSize);
+ int[] mipSizes = new int[output.size()];
+ for (int i = 0; i < output.size(); i++){
+ ByteBuffer data = output.get(i);
+ data.clear();
+ combinedData.put(data);
+ mipSizes[i] = data.capacity();
+ }
+ combinedData.flip();
+
+ // insert mip data into image
+ image.setData(0, combinedData);
+ image.setMipMapSizes(mipSizes);
+ image.setFormat(format);
+ }
+
+}
diff --git a/engine/src/desktop/jme3tools/navigation/Coordinate.java b/engine/src/desktop/jme3tools/navigation/Coordinate.java
new file mode 100644
index 0000000..6e2c2d2
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/Coordinate.java
@@ -0,0 +1,247 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+import java.text.DecimalFormat;
+
+/**
+ * Coordinate class. Used to store a coordinate in [DD]D MM.M format.
+ *
+ * @author Benjamin Jakobus (based on JMarine by Benjamin Jakobus and Cormac Gebruers)
+ * @version 1.0
+ * @since 1.0
+ */
+public class Coordinate {
+
+ /* the degree part of the position (+ N/E, -W/S) */
+ private int deg;
+
+ /* the decimals of a minute */
+ private double minsDecMins;
+
+ /* the coordinate as a decimal*/
+ private double decCoordinate;
+
+ /* whether this coordinate is a latitude or a longitude: : LAT==0, LONG==1 */
+ private int coOrdinate;
+
+ /* The minutes trailing decimal precision to use for positions */
+ public static final int MINPRECISION = 4;
+ /* The degrees trailing decimal precision to use for positions */
+ public static final int DEGPRECISION = 7;
+
+ /* typeDefs for coOrdinates */
+ public static final int LAT = 0;
+ public static final int LNG = 1;
+
+ /* typeDefs for quadrant */
+ public static final int E = 0;
+ public static final int S = 1;
+ public static final int W = 2;
+ public static final int N = 3;
+
+ /**
+ * Constructor
+ *
+ * @param deg
+ * @param minsDecMins
+ * @param coOrdinate
+ * @param quad
+ * @throws InvalidPositionException
+ * @since 1.0
+ */
+ public Coordinate(int deg, float minsDecMins, int coOrdinate,
+ int quad) throws InvalidPositionException {
+ buildCoOrdinate(deg, minsDecMins, coOrdinate, quad);
+ if (verify()) {
+ } else {
+ throw new InvalidPositionException();
+ }
+ }
+
+ /**
+ * Constructor
+ * @param decCoordinate
+ * @param coOrdinate
+ * @throws InvalidPositionException
+ * @since 1.0
+ */
+ public Coordinate(double decCoordinate, int coOrdinate) throws InvalidPositionException {
+ DecimalFormat form = new DecimalFormat("#.#######");
+
+ this.decCoordinate = decCoordinate;
+ this.coOrdinate = coOrdinate;
+ if (verify()) {
+ deg = new Float(decCoordinate).intValue();
+ if (deg < 0) {
+ minsDecMins = Double.parseDouble(form.format((Math.abs(decCoordinate) - Math.abs(deg)) * 60));
+ } else {
+ minsDecMins = Double.parseDouble(form.format((decCoordinate - deg) * 60));
+ }
+ } else {
+ throw new InvalidPositionException();
+ }
+ }
+
+ /**
+ * This constructor takes a coordinate in the ALRS formats i.e
+ * 38∞31.64'N for lat, and 28∞19.12'W for long
+ * Note: ALRS positions are occasionally written with the decimal minutes
+ * apostrophe in the 'wrong' place and with an non CP1252 compliant decimal character.
+ * This issue has to be corrected in the source database
+ * @param coOrdinate
+ * @throws InvalidPositionException
+ * @since 1.0
+ */
+ public Coordinate(String coOrdinate) throws InvalidPositionException {
+ //firstly split it into its component parts and dispose of the unneeded characters
+ String[] items = coOrdinate.split("°");
+ int deg = Integer.valueOf(items[0]);
+
+ items = items[1].split("'");
+ float minsDecMins = Float.valueOf(items[0]);
+ char quad = items[1].charAt(0);
+
+ switch (quad) {
+ case 'N':
+ buildCoOrdinate(deg, minsDecMins, Coordinate.LAT, Coordinate.N);
+ break;
+ case 'S':
+ buildCoOrdinate(deg, minsDecMins, Coordinate.LAT, Coordinate.S);
+ break;
+ case 'E':
+ buildCoOrdinate(deg, minsDecMins, Coordinate.LNG, Coordinate.E);
+ break;
+ case 'W':
+ buildCoOrdinate(deg, minsDecMins, Coordinate.LNG, Coordinate.W);
+ }
+ if (verify()) {
+ } else {
+ throw new InvalidPositionException();
+ }
+ }
+
+ /**
+ * Prints out a coordinate as a string
+ * @return the coordinate in decimal format
+ * @since 1.0
+ */
+ public String toStringDegMin() {
+ String str = "";
+ String quad = "";
+ StringUtil su = new StringUtil();
+ switch (coOrdinate) {
+ case LAT:
+ if (decCoordinate >= 0) {
+ quad = "N";
+ } else {
+ quad = "S";
+ }
+ str = su.padNumZero(Math.abs(deg), 2);
+ str += "\u00b0" + su.padNumZero(Math.abs(minsDecMins), 2, MINPRECISION) + "'" + quad;
+ break;
+ case LNG:
+ if (decCoordinate >= 0) {
+ quad = "E";
+ } else {
+ quad = "W";
+ }
+ str = su.padNumZero(Math.abs(deg), 3);
+ str += "\u00b0" + su.padNumZero(Math.abs(minsDecMins), 2, MINPRECISION) + "'" + quad;
+ break;
+ }
+ return str;
+ }
+
+ /**
+ * Prints out a coordinate as a string
+ * @return the coordinate in decimal format
+ * @since 1.0
+ */
+ public String toStringDec() {
+ StringUtil u = new StringUtil();
+ switch (coOrdinate) {
+ case LAT:
+ return u.padNumZero(decCoordinate, 2, DEGPRECISION);
+ case LNG:
+ return u.padNumZero(decCoordinate, 3, DEGPRECISION);
+ }
+ return "error";
+ }
+
+ /**
+ * Returns the coordinate's decimal value
+ * @return float the decimal value of the coordinate
+ * @since 1.0
+ */
+ public double decVal() {
+ return decCoordinate;
+ }
+
+ /**
+ * Determines whether a decimal position is valid
+ * @return result of validity test
+ * @since 1.0
+ */
+ private boolean verify() {
+ switch (coOrdinate) {
+ case LAT:
+ if (Math.abs(decCoordinate) > 90.0) {
+ return false;
+ }
+ break;
+
+ case LNG:
+ if (Math.abs(decCoordinate) > 180) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Populate this object by parsing the arguments to the function
+ * Placed here to allow multiple constructors to use it
+ * @since 1.0
+ */
+ private void buildCoOrdinate(int deg, float minsDecMins, int coOrdinate,
+ int quad) {
+ NumUtil nu = new NumUtil();
+
+ switch (coOrdinate) {
+ case LAT:
+ switch (quad) {
+ case N:
+ this.deg = deg;
+ this.minsDecMins = minsDecMins;
+ this.coOrdinate = coOrdinate;
+ decCoordinate = nu.Round(this.deg + (float) this.minsDecMins / 60, Coordinate.MINPRECISION);
+ break;
+
+ case S:
+ this.deg = -deg;
+ this.minsDecMins = minsDecMins;
+ this.coOrdinate = coOrdinate;
+ decCoordinate = nu.Round(this.deg - ((float) this.minsDecMins / 60), Coordinate.MINPRECISION);
+ }
+
+ case LNG:
+ switch (quad) {
+ case E:
+ this.deg = deg;
+ this.minsDecMins = minsDecMins;
+ this.coOrdinate = coOrdinate;
+ decCoordinate = nu.Round(this.deg + ((float) this.minsDecMins / 60), Coordinate.MINPRECISION);
+ break;
+
+ case W:
+ this.deg = -deg;
+ this.minsDecMins = minsDecMins;
+ this.coOrdinate = coOrdinate;
+ decCoordinate = nu.Round(this.deg - ((float) this.minsDecMins / 60), Coordinate.MINPRECISION);
+ }
+ }
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/GCSailing.java b/engine/src/desktop/jme3tools/navigation/GCSailing.java
new file mode 100644
index 0000000..f5699b5
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/GCSailing.java
@@ -0,0 +1,33 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+/**
+ * A utility class to package up a great circle sailing.
+ *
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+public class GCSailing {
+
+ private int[] courses;
+ private float[] distancesNM;
+
+ public GCSailing(int[] pCourses, float[] pDistancesNM) {
+ courses = pCourses;
+ distancesNM = pDistancesNM;
+ }
+
+ public int[] getCourses() {
+ return courses;
+ }
+
+ public float[] getDistancesNM() {
+ return distancesNM;
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/InvalidPositionException.java b/engine/src/desktop/jme3tools/navigation/InvalidPositionException.java
new file mode 100644
index 0000000..32b3c9b
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/InvalidPositionException.java
@@ -0,0 +1,14 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jme3tools.navigation;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class InvalidPositionException extends Exception{
+
+}
diff --git a/engine/src/desktop/jme3tools/navigation/MapModel2D.java b/engine/src/desktop/jme3tools/navigation/MapModel2D.java
new file mode 100644
index 0000000..a1a08c1
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/MapModel2D.java
@@ -0,0 +1,368 @@
+package jme3tools.navigation;
+import java.awt.Point;
+import java.text.DecimalFormat;
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+
+/**
+ * A representation of the actual map in terms of lat/long and x,y co-ordinates.
+ * The Map class contains various helper methods such as methods for determining
+ * the pixel positions for lat/long co-ordinates and vice versa.
+ *
+ * @author Cormac Gebruers
+ * @author Benjamin Jakobus
+ * @version 1.0
+ * @since 1.0
+ */
+public class MapModel2D {
+
+ /* The number of radians per degree */
+ private final static double RADIANS_PER_DEGREE = 57.2957;
+
+ /* The number of degrees per radian */
+ private final static double DEGREES_PER_RADIAN = 0.0174532925;
+
+ /* The map's width in longitude */
+ public final static int DEFAULT_MAP_WIDTH_LONGITUDE = 360;
+
+ /* The top right hand corner of the map */
+ private Position centre;
+
+ /* The x and y co-ordinates for the viewport's centre */
+ private int xCentre;
+ private int yCentre;
+
+ /* The width (in pixels) of the viewport holding the map */
+ private int viewportWidth;
+
+ /* The viewport height in pixels */
+ private int viewportHeight;
+
+ /* The number of minutes that one pixel represents */
+ private double minutesPerPixel;
+
+ /**
+ * Constructor
+ * @param viewportWidth the pixel width of the viewport (component) in which
+ * the map is displayed
+ * @since 1.0
+ */
+ public MapModel2D(int viewportWidth) {
+ try {
+ this.centre = new Position(0, 0);
+ } catch (InvalidPositionException e) {
+ e.printStackTrace();
+ }
+
+ this.viewportWidth = viewportWidth;
+
+ // Calculate the number of minutes that one pixel represents along the longitude
+ calculateMinutesPerPixel(DEFAULT_MAP_WIDTH_LONGITUDE);
+
+ // Calculate the viewport height based on its width and the number of degrees (85)
+ // in our map
+ viewportHeight = ((int) NavCalculator.computeDMPClarkeSpheroid(0, 85) / (int) minutesPerPixel) * 2;
+// viewportHeight = viewportWidth; // REMOVE!!!
+ // Determine the map's x,y centre
+ xCentre = viewportWidth / 2;
+ yCentre = viewportHeight / 2;
+ }
+
+ /**
+ * Returns the height of the viewport in pixels
+ * @return the height of the viewport in pixels
+ * @since 0.1
+ */
+ public int getViewportPixelHeight() {
+ return viewportHeight;
+ }
+
+ /**
+ * Calculates the number of minutes per pixels using a given
+ * map width in longitude
+ * @param mapWidthInLongitude
+ * @since 1.0
+ */
+ public void calculateMinutesPerPixel(double mapWidthInLongitude) {
+ minutesPerPixel = (mapWidthInLongitude * 60) / (double) viewportWidth;
+ }
+
+ /**
+ * Returns the width of the viewport in pixels
+ * @return the width of the viewport in pixels
+ * @since 0.1
+ */
+ public int getViewportPixelWidth() {
+ return viewportWidth;
+ }
+
+ public void setViewportWidth(int viewportWidth) {
+ this.viewportWidth = viewportWidth;
+ }
+
+ public void setViewportHeight(int viewportHeight) {
+ this.viewportHeight = viewportHeight;
+ }
+
+ public void setCentre(Position centre) {
+ this.centre = centre;
+ }
+
+ /**
+ * Returns the number of minutes there are per pixel
+ * @return the number of minutes per pixel
+ * @since 1.0
+ */
+ public double getMinutesPerPixel() {
+ return minutesPerPixel;
+ }
+
+ public double getMetersPerPixel() {
+ return 1853 * minutesPerPixel;
+ }
+
+ public void setMinutesPerPixel(double minutesPerPixel) {
+ this.minutesPerPixel = minutesPerPixel;
+ }
+
+ /**
+ * Converts a latitude/longitude position into a pixel co-ordinate
+ * @param position the position to convert
+ * @return {@code Point} a pixel co-ordinate
+ * @since 1.0
+ */
+ public Point toPixel(Position position) {
+ // Get the distance between position and the centre for calculating
+ // the position's longitude translation
+ double distance = NavCalculator.computeLongDiff(centre.getLongitude(),
+ position.getLongitude());
+
+ // Use the distance from the centre to calculate the pixel x co-ordinate
+ double distanceInPixels = (distance / minutesPerPixel);
+
+ // Use the difference in meridional parts to calculate the pixel y co-ordinate
+ double dmp = NavCalculator.computeDMPClarkeSpheroid(centre.getLatitude(),
+ position.getLatitude());
+
+ int x = 0;
+ int y = 0;
+
+ if (centre.getLatitude() == position.getLatitude()) {
+ y = yCentre;
+ }
+ if (centre.getLongitude() == position.getLongitude()) {
+ x = xCentre;
+ }
+
+ // Distinguish between northern and southern hemisphere for latitude calculations
+ if (centre.getLatitude() > 0 && position.getLatitude() > centre.getLatitude()) {
+ // Centre is north. Position is north of centre
+ y = yCentre + (int) ((dmp) / minutesPerPixel);
+ } else if (centre.getLatitude() > 0 && position.getLatitude() < centre.getLatitude()) {
+ // Centre is north. Position is south of centre
+ y = yCentre - (int) ((dmp) / minutesPerPixel);
+ } else if (centre.getLatitude() < 0 && position.getLatitude() > centre.getLatitude()) {
+ // Centre is south. Position is north of centre
+ y = yCentre + (int) ((dmp) / minutesPerPixel);
+ } else if (centre.getLatitude() < 0 && position.getLatitude() < centre.getLatitude()) {
+ // Centre is south. Position is south of centre
+ y = yCentre - (int) ((dmp) / minutesPerPixel);
+ } else if (centre.getLatitude() == 0 && position.getLatitude() > centre.getLatitude()) {
+ // Centre is at the equator. Position is north of the equator
+ y = yCentre + (int) ((dmp) / minutesPerPixel);
+ } else if (centre.getLatitude() == 0 && position.getLatitude() < centre.getLatitude()) {
+ // Centre is at the equator. Position is south of the equator
+ y = yCentre - (int) ((dmp) / minutesPerPixel);
+ }
+
+ // Distinguish between western and eastern hemisphere for longitude calculations
+ if (centre.getLongitude() < 0 && position.getLongitude() < centre.getLongitude()) {
+ // Centre is west. Position is west of centre
+ x = xCentre - (int) distanceInPixels;
+ } else if (centre.getLongitude() < 0 && position.getLongitude() > centre.getLongitude()) {
+ // Centre is west. Position is south of centre
+ x = xCentre + (int) distanceInPixels;
+ } else if (centre.getLongitude() > 0 && position.getLongitude() < centre.getLongitude()) {
+ // Centre is east. Position is west of centre
+ x = xCentre - (int) distanceInPixels;
+ } else if (centre.getLongitude() > 0 && position.getLongitude() > centre.getLongitude()) {
+ // Centre is east. Position is east of centre
+ x = xCentre + (int) distanceInPixels;
+ } else if (centre.getLongitude() == 0 && position.getLongitude() > centre.getLongitude()) {
+ // Centre is at the equator. Position is east of centre
+ x = xCentre + (int) distanceInPixels;
+ } else if (centre.getLongitude() == 0 && position.getLongitude() < centre.getLongitude()) {
+ // Centre is at the equator. Position is west of centre
+ x = xCentre - (int) distanceInPixels;
+ }
+
+ // Distinguish between northern and souterhn hemisphere for longitude calculations
+ return new Point(x, y);
+ }
+
+ /**
+ * Converts a pixel position into a mercator position
+ * @param p {@link Point} object that you wish to convert into
+ * longitude / latiude
+ * @return the converted {@code Position} object
+ * @since 1.0
+ */
+ public Position toPosition(Point p) {
+ double lat, lon;
+ Position pos = null;
+ try {
+ Point pixelCentre = toPixel(new Position(0, 0));
+
+ // Get the distance between position and the centre
+ double xDistance = distance(xCentre, p.getX());
+ double yDistance = distance(pixelCentre.getY(), p.getY());
+ double lonDistanceInDegrees = (xDistance * minutesPerPixel) / 60;
+ double mp = (yDistance * minutesPerPixel);
+ // If we are zoomed in past a certain point, then use linear search.
+ // Otherwise use binary search
+ if (getMinutesPerPixel() < 0.05) {
+ lat = findLat(mp, getCentre().getLatitude());
+ if (lat == -1000) {
+ System.out.println("lat: " + lat);
+ }
+ } else {
+ lat = findLat(mp, 0.0, 85.0);
+ }
+ lon = (p.getX() < xCentre ? centre.getLongitude() - lonDistanceInDegrees
+ : centre.getLongitude() + lonDistanceInDegrees);
+
+ if (p.getY() > pixelCentre.getY()) {
+ lat = -1 * lat;
+ }
+ if (lat == -1000 || lon == -1000) {
+ return pos;
+ }
+ pos = new Position(lat, lon);
+ } catch (InvalidPositionException ipe) {
+ ipe.printStackTrace();
+ }
+ return pos;
+ }
+
+ /**
+ * Calculates distance between two points on the map in pixels
+ * @param a
+ * @param b
+ * @return distance the distance between a and b in pixels
+ * @since 1.0
+ */
+ private double distance(double a, double b) {
+ return Math.abs(a - b);
+ }
+
+ /**
+ * Defines the centre of the map in pixels
+ * @param p <code>Point</code> object denoting the map's new centre
+ * @since 1.0
+ */
+ public void setCentre(Point p) {
+ try {
+ Position newCentre = toPosition(p);
+ if (newCentre != null) {
+ centre = newCentre;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Sets the map's xCentre
+ * @param xCentre
+ * @since 1.0
+ */
+ public void setXCentre(int xCentre) {
+ this.xCentre = xCentre;
+ }
+
+ /**
+ * Sets the map's yCentre
+ * @param yCentre
+ * @since 1.0
+ */
+ public void setYCentre(int yCentre) {
+ this.yCentre = yCentre;
+ }
+
+ /**
+ * Returns the pixel (x,y) centre of the map
+ * @return {@link Point) object marking the map's (x,y) centre
+ * @since 1.0
+ */
+ public Point getPixelCentre() {
+ return new Point(xCentre, yCentre);
+ }
+
+ /**
+ * Returns the {@code Position} centre of the map
+ * @return {@code Position} object marking the map's (lat, long) centre
+ * @since 1.0
+ */
+ public Position getCentre() {
+ return centre;
+ }
+
+ /**
+ * Uses binary search to find the latitude of a given MP.
+ *
+ * @param mp maridian part
+ * @param low
+ * @param high
+ * @return the latitude of the MP value
+ * @since 1.0
+ */
+ private double findLat(double mp, double low, double high) {
+ DecimalFormat form = new DecimalFormat("#.####");
+ mp = Math.round(mp);
+ double midLat = (low + high) / 2.0;
+ // ctr is used to make sure that with some
+ // numbers which can't be represented exactly don't inifitely repeat
+ double guessMP = NavCalculator.computeDMPClarkeSpheroid(0, (float) midLat);
+
+ while (low <= high) {
+ if (guessMP == mp) {
+ return midLat;
+ } else {
+ if (guessMP > mp) {
+ high = midLat - 0.0001;
+ } else {
+ low = midLat + 0.0001;
+ }
+ }
+
+ midLat = Double.valueOf(form.format(((low + high) / 2.0)));
+ guessMP = NavCalculator.computeDMPClarkeSpheroid(0, (float) midLat);
+ guessMP = Math.round(guessMP);
+ }
+ return -1000;
+ }
+
+ /**
+ * Uses linear search to find the latitude of a given MP
+ * @param mp the meridian part for which to find the latitude
+ * @param previousLat the previous latitude. Used as a upper / lower bound
+ * @return the latitude of the MP value
+ */
+ private double findLat(double mp, double previousLat) {
+ DecimalFormat form = new DecimalFormat("#.#####");
+ mp = Double.parseDouble(form.format(mp));
+ double guessMP;
+ for (double lat = previousLat - 0.25; lat < previousLat + 1; lat += 0.00001) {
+ guessMP = NavCalculator.computeDMPClarkeSpheroid(0, lat);
+ guessMP = Double.parseDouble(form.format(guessMP));
+ if (guessMP == mp || Math.abs(guessMP - mp) < 0.001) {
+ return lat;
+ }
+ }
+ return -1000;
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/MapModel3D.java b/engine/src/desktop/jme3tools/navigation/MapModel3D.java
new file mode 100644
index 0000000..f3d296c
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/MapModel3D.java
@@ -0,0 +1,389 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+import com.jme3.math.Vector3f;
+import java.text.DecimalFormat;
+
+
+/**
+ * A representation of the actual map in terms of lat/long and x,y,z co-ordinates.
+ * The Map class contains various helper methods such as methods for determining
+ * the world unit positions for lat/long coordinates and vice versa. This map projection
+ * does not handle screen/pixel coordinates.
+ *
+ * @author Benjamin Jakobus (thanks to Cormac Gebruers)
+ * @version 1.0
+ * @since 1.0
+ */
+public class MapModel3D {
+
+ /* The number of radians per degree */
+ private final static double RADIANS_PER_DEGREE = 57.2957;
+
+ /* The number of degrees per radian */
+ private final static double DEGREES_PER_RADIAN = 0.0174532925;
+
+ /* The map's width in longitude */
+ public final static int DEFAULT_MAP_WIDTH_LONGITUDE = 360;
+
+ /* The top right hand corner of the map */
+ private Position centre;
+
+ /* The x and y co-ordinates for the viewport's centre */
+ private int xCentre;
+ private int zCentre;
+
+ /* The width (in world units (wu)) of the viewport holding the map */
+ private int worldWidth;
+
+ /* The viewport height in pixels */
+ private int worldHeight;
+
+ /* The number of minutes that one pixel represents */
+ private double minutesPerWorldUnit;
+
+ /**
+ * Constructor.
+ *
+ * @param worldWidth The world unit width the map's area
+ * @since 1.0
+ */
+ public MapModel3D(int worldWidth) {
+ try {
+ this.centre = new Position(0, 0);
+ } catch (InvalidPositionException e) {
+ e.printStackTrace();
+ }
+
+ this.worldWidth = worldWidth;
+
+ // Calculate the number of minutes that one pixel represents along the longitude
+ calculateMinutesPerWorldUnit(DEFAULT_MAP_WIDTH_LONGITUDE);
+
+ // Calculate the viewport height based on its width and the number of degrees (85)
+ // in our map
+ worldHeight = ((int) NavCalculator.computeDMPClarkeSpheroid(0, 85) / (int) minutesPerWorldUnit) * 2;
+
+ // Determine the map's x,y centre
+ xCentre = 0;
+ zCentre = 0;
+// xCentre = worldWidth / 2;
+// zCentre = worldHeight / 2;
+ }
+
+ /**
+ * Returns the height of the viewport in pixels.
+ *
+ * @return The height of the viewport in pixels.
+ * @since 1.0
+ */
+ public int getWorldHeight() {
+ return worldHeight;
+ }
+
+ /**
+ * Calculates the number of minutes per pixels using a given
+ * map width in longitude.
+ *
+ * @param mapWidthInLongitude The map's with in degrees of longitude.
+ * @since 1.0
+ */
+ public void calculateMinutesPerWorldUnit(double mapWidthInLongitude) {
+ // Multiply mapWidthInLongitude by 60 to convert it to minutes.
+ minutesPerWorldUnit = (mapWidthInLongitude * 60) / (double) worldWidth;
+ }
+
+ /**
+ * Returns the width of the viewport in pixels.
+ *
+ * @return The width of the viewport in pixels.
+ * @since 1.0
+ */
+ public int getWorldWidth() {
+ return worldWidth;
+ }
+
+ /**
+ * Sets the world's desired width.
+ *
+ * @param viewportWidth The world's desired width in WU.
+ * @since 1.0
+ */
+ public void setWorldWidth(int viewportWidth) {
+ this.worldWidth = viewportWidth;
+ }
+
+ /**
+ * Sets the world's desired height.
+ *
+ * @param viewportHeight The world's desired height in WU.
+ * @since 1.0
+ */
+ public void setWorldHeight(int viewportHeight) {
+ this.worldHeight = viewportHeight;
+ }
+
+ /**
+ * Sets the map's centre.
+ *
+ * @param centre The <code>Position</code> denoting the map's
+ * desired centre.
+ * @since 1.0
+ */
+ public void setCentre(Position centre) {
+ this.centre = centre;
+ }
+
+ /**
+ * Returns the number of minutes there are per WU.
+ *
+ * @return The number of minutes per WU.
+ * @since 1.0
+ */
+ public double getMinutesPerWu() {
+ return minutesPerWorldUnit;
+ }
+
+ /**
+ * Returns the meters per WU.
+ *
+ * @return The meters per WU.
+ * @since 1.0
+ */
+ public double getMetersPerWu() {
+ return 1853 * minutesPerWorldUnit;
+ }
+
+ /**
+ * Converts a latitude/longitude position into a WU coordinate.
+ *
+ * @param position The <code>Position</code> to convert.
+ * @return The <code>Point</code> a pixel coordinate.
+ * @since 1.0
+ */
+ public Vector3f toWorldUnit(Position position) {
+ // Get the difference between position and the centre for calculating
+ // the position's longitude translation
+ double distance = NavCalculator.computeLongDiff(centre.getLongitude(),
+ position.getLongitude());
+
+ // Use the difference from the centre to calculate the pixel x co-ordinate
+ double distanceInPixels = (distance / minutesPerWorldUnit);
+
+ // Use the difference in meridional parts to calculate the pixel y co-ordinate
+ double dmp = NavCalculator.computeDMPClarkeSpheroid(centre.getLatitude(),
+ position.getLatitude());
+
+ int x = 0;
+ int z = 0;
+
+ if (centre.getLatitude() == position.getLatitude()) {
+ z = zCentre;
+ }
+ if (centre.getLongitude() == position.getLongitude()) {
+ x = xCentre;
+ }
+
+ // Distinguish between northern and southern hemisphere for latitude calculations
+ if (centre.getLatitude() > 0 && position.getLatitude() > centre.getLatitude()) {
+ // Centre is north. Position is north of centre
+ z = zCentre - (int) ((dmp) / minutesPerWorldUnit);
+ } else if (centre.getLatitude() > 0 && position.getLatitude() < centre.getLatitude()) {
+ // Centre is north. Position is south of centre
+ z = zCentre + (int) ((dmp) / minutesPerWorldUnit);
+ } else if (centre.getLatitude() < 0 && position.getLatitude() > centre.getLatitude()) {
+ // Centre is south. Position is north of centre
+ z = zCentre - (int) ((dmp) / minutesPerWorldUnit);
+ } else if (centre.getLatitude() < 0 && position.getLatitude() < centre.getLatitude()) {
+ // Centre is south. Position is south of centre
+ z = zCentre + (int) ((dmp) / minutesPerWorldUnit);
+ } else if (centre.getLatitude() == 0 && position.getLatitude() > centre.getLatitude()) {
+ // Centre is at the equator. Position is north of the equator
+ z = zCentre - (int) ((dmp) / minutesPerWorldUnit);
+ } else if (centre.getLatitude() == 0 && position.getLatitude() < centre.getLatitude()) {
+ // Centre is at the equator. Position is south of the equator
+ z = zCentre + (int) ((dmp) / minutesPerWorldUnit);
+ }
+
+ // Distinguish between western and eastern hemisphere for longitude calculations
+ if (centre.getLongitude() < 0 && position.getLongitude() < centre.getLongitude()) {
+ // Centre is west. Position is west of centre
+ x = xCentre - (int) distanceInPixels;
+ } else if (centre.getLongitude() < 0 && position.getLongitude() > centre.getLongitude()) {
+ // Centre is west. Position is south of centre
+ x = xCentre + (int) distanceInPixels;
+ } else if (centre.getLongitude() > 0 && position.getLongitude() < centre.getLongitude()) {
+ // Centre is east. Position is west of centre
+ x = xCentre - (int) distanceInPixels;
+ } else if (centre.getLongitude() > 0 && position.getLongitude() > centre.getLongitude()) {
+ // Centre is east. Position is east of centre
+ x = xCentre + (int) distanceInPixels;
+ } else if (centre.getLongitude() == 0 && position.getLongitude() > centre.getLongitude()) {
+ // Centre is at the equator. Position is east of centre
+ x = xCentre + (int) distanceInPixels;
+ } else if (centre.getLongitude() == 0 && position.getLongitude() < centre.getLongitude()) {
+ // Centre is at the equator. Position is west of centre
+ x = xCentre - (int) distanceInPixels;
+ }
+
+ // Distinguish between northern and southern hemisphere for longitude calculations
+ return new Vector3f(x, 0, z);
+ }
+
+ /**
+ * Converts a world position into a Mercator position.
+ *
+ * @param posVec <code>Vector</code> containing the world unit
+ * coordinates that are to be converted into
+ * longitude / latitude coordinates.
+ * @return The resulting <code>Position</code> in degrees of
+ * latitude and longitude.
+ * @since 1.0
+ */
+ public Position toPosition(Vector3f posVec) {
+ double lat, lon;
+ Position pos = null;
+ try {
+ Vector3f worldCentre = toWorldUnit(new Position(0, 0));
+
+ // Get the difference between position and the centre
+ double xDistance = difference(xCentre, posVec.getX());
+ double yDistance = difference(worldCentre.getZ(), posVec.getZ());
+ double lonDistanceInDegrees = (xDistance * minutesPerWorldUnit) / 60;
+ double mp = (yDistance * minutesPerWorldUnit);
+ // If we are zoomed in past a certain point, then use linear search.
+ // Otherwise use binary search
+ if (getMinutesPerWu() < 0.05) {
+ lat = findLat(mp, getCentre().getLatitude());
+ if (lat == -1000) {
+ System.out.println("lat: " + lat);
+ }
+ } else {
+ lat = findLat(mp, 0.0, 85.0);
+ }
+ lon = (posVec.getX() < xCentre ? centre.getLongitude() - lonDistanceInDegrees
+ : centre.getLongitude() + lonDistanceInDegrees);
+
+ if (posVec.getZ() > worldCentre.getZ()) {
+ lat = -1 * lat;
+ }
+ if (lat == -1000 || lon == -1000) {
+ return pos;
+ }
+ pos = new Position(lat, lon);
+ } catch (InvalidPositionException ipe) {
+ ipe.printStackTrace();
+ }
+ return pos;
+ }
+
+ /**
+ * Calculates difference between two points on the map in WU.
+ *
+ * @param a
+ * @param b
+ * @return difference The difference between a and b in WU.
+ * @since 1.0
+ */
+ private double difference(double a, double b) {
+ return Math.abs(a - b);
+ }
+
+ /**
+ * Defines the centre of the map in pixels.
+ *
+ * @param posVec <code>Vector3f</code> object denoting the map's new centre.
+ * @since 1.0
+ */
+ public void setCentre(Vector3f posVec) {
+ try {
+ Position newCentre = toPosition(posVec);
+ if (newCentre != null) {
+ centre = newCentre;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns the WU (x,y,z) centre of the map.
+ *
+ * @return <code>Vector3f</code> object marking the map's (x,y) centre.
+ * @since 1.0
+ */
+ public Vector3f getCentreWu() {
+ return new Vector3f(xCentre, 0, zCentre);
+ }
+
+ /**
+ * Returns the <code>Position</code> centre of the map.
+ *
+ * @return <code>Position</code> object marking the map's (lat, long)
+ * centre.
+ * @since 1.0
+ */
+ public Position getCentre() {
+ return centre;
+ }
+
+ /**
+ * Uses binary search to find the latitude of a given MP.
+ *
+ * @param mp Maridian part whose latitude to determine.
+ * @param low Minimum latitude bounds.
+ * @param high Maximum latitude bounds.
+ * @return The latitude of the MP value
+ * @since 1.0
+ */
+ private double findLat(double mp, double low, double high) {
+ DecimalFormat form = new DecimalFormat("#.####");
+ mp = Math.round(mp);
+ double midLat = (low + high) / 2.0;
+ // ctr is used to make sure that with some
+ // numbers which can't be represented exactly don't inifitely repeat
+ double guessMP = NavCalculator.computeDMPClarkeSpheroid(0, (float) midLat);
+
+ while (low <= high) {
+ if (guessMP == mp) {
+ return midLat;
+ } else {
+ if (guessMP > mp) {
+ high = midLat - 0.0001;
+ } else {
+ low = midLat + 0.0001;
+ }
+ }
+
+ midLat = Double.valueOf(form.format(((low + high) / 2.0)));
+ guessMP = NavCalculator.computeDMPClarkeSpheroid(0, (float) midLat);
+ guessMP = Math.round(guessMP);
+ }
+ return -1000;
+ }
+
+ /**
+ * Uses linear search to find the latitude of a given MP.
+ *
+ * @param mp The meridian part for which to find the latitude.
+ * @param previousLat The previous latitude. Used as a upper / lower bound.
+ * @return The latitude of the MP value.
+ * @since 1.0
+ */
+ private double findLat(double mp, double previousLat) {
+ DecimalFormat form = new DecimalFormat("#.#####");
+ mp = Double.parseDouble(form.format(mp));
+ double guessMP;
+ for (double lat = previousLat - 0.25; lat < previousLat + 1; lat += 0.00001) {
+ guessMP = NavCalculator.computeDMPClarkeSpheroid(0, lat);
+ guessMP = Double.parseDouble(form.format(guessMP));
+ if (guessMP == mp || Math.abs(guessMP - mp) < 0.05) {
+ return lat;
+ }
+ }
+ return -1000;
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/NavCalculator.java b/engine/src/desktop/jme3tools/navigation/NavCalculator.java
new file mode 100644
index 0000000..7145636
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/NavCalculator.java
@@ -0,0 +1,591 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+
+
+/**
+ * A utlity class for performing position calculations
+ *
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+public class NavCalculator {
+
+ private double distance;
+ private double trueCourse;
+
+ /* The earth's radius in meters */
+ public static final int WGS84_EARTH_RADIUS = 6378137;
+ private String strCourse;
+
+ /* The sailing calculation type */
+ public static final int MERCATOR = 0;
+ public static final int GC = 1;
+
+ /* The degree precision to use for courses */
+ public static final int RL_CRS_PRECISION = 1;
+
+ /* The distance precision to use for distances */
+ public static final int RL_DIST_PRECISION = 1;
+ public static final int METERS_PER_MINUTE = 1852;
+
+ /**
+ * Constructor
+ * @param P1
+ * @param P2
+ * @param calcType
+ * @since 1.0
+ */
+ public NavCalculator(Position P1, Position P2, int calcType) {
+ switch (calcType) {
+ case MERCATOR:
+ mercatorSailing(P1, P2);
+ break;
+ case GC:
+ greatCircleSailing(P1, P2);
+ break;
+ }
+ }
+
+ /**
+ * Constructor
+ * @since 1.0
+ */
+ public NavCalculator() {
+ }
+
+ /**
+ * Determines a great circle track between two positions
+ * @param p1 origin position
+ * @param p2 destination position
+ */
+ public GCSailing greatCircleSailing(Position p1, Position p2) {
+ return new GCSailing(new int[0], new float[0]);
+ }
+
+ /**
+ * Determines a Rhumb Line course and distance between two points
+ * @param p1 origin position
+ * @param p2 destination position
+ */
+ public RLSailing rhumbLineSailing(Position p1, Position p2) {
+ RLSailing rl = mercatorSailing(p1, p2);
+ return rl;
+ }
+
+ /**
+ * Determines the rhumb line course and distance between two positions
+ * @param p1 origin position
+ * @param p2 destination position
+ */
+ public RLSailing mercatorSailing(Position p1, Position p2) {
+
+ double dLat = computeDLat(p1.getLatitude(), p2.getLatitude());
+ //plane sailing...
+ if (dLat == 0) {
+ RLSailing rl = planeSailing(p1, p2);
+ return rl;
+ }
+
+ double dLong = computeDLong(p1.getLongitude(), p2.getLongitude());
+ double dmp = (float) computeDMPClarkeSpheroid(p1.getLatitude(), p2.getLatitude());
+
+ trueCourse = (float) Math.toDegrees(Math.atan(dLong / dmp));
+ double degCrs = convertCourse((float) trueCourse, p1, p2);
+ distance = (float) Math.abs(dLat / Math.cos(Math.toRadians(trueCourse)));
+
+ RLSailing rl = new RLSailing(degCrs, (float) distance);
+ trueCourse = rl.getCourse();
+ strCourse = (dLat < 0 ? "S" : "N");
+ strCourse += " " + trueCourse;
+ strCourse += " " + (dLong < 0 ? "W" : "E");
+ return rl;
+
+ }
+
+ /**
+ * Calculate a plane sailing situation - i.e. where Lats are the same
+ * @param p1
+ * @param p2
+ * @return
+ * @since 1.0
+ */
+ public RLSailing planeSailing(Position p1, Position p2) {
+ double dLong = computeDLong(p1.getLongitude(), p2.getLongitude());
+
+ double sgnDLong = 0 - (dLong / Math.abs(dLong));
+ if (Math.abs(dLong) > 180 * 60) {
+ dLong = (360 * 60 - Math.abs(dLong)) * sgnDLong;
+ }
+
+ double redist = 0;
+ double recourse = 0;
+ if (p1.getLatitude() == 0) {
+ redist = Math.abs(dLong);
+ } else {
+ redist = Math.abs(dLong * (float) Math.cos(p1.getLatitude() * 2 * Math.PI / 360));
+ }
+ recourse = (float) Math.asin(0 - sgnDLong);
+ recourse = recourse * 360 / 2 / (float) Math.PI;
+
+ if (recourse < 0) {
+ recourse = recourse + 360;
+ }
+ return new RLSailing(recourse, redist);
+ }
+
+ /**
+ * Converts a course from cardinal XddY to ddd notation
+ * @param tc
+ * @param p1 position one
+ * @param p2 position two
+ * @return
+ * @since 1.0
+ */
+ public static double convertCourse(float tc, Position p1, Position p2) {
+
+ double dLat = p1.getLatitude() - p2.getLatitude();
+ double dLong = p1.getLongitude() - p2.getLongitude();
+ //NE
+ if (dLong >= 0 & dLat >= 0) {
+ return Math.abs(tc);
+ }
+
+ //SE
+ if (dLong >= 0 & dLat < 0) {
+ return 180 - Math.abs(tc);
+ }
+
+ //SW
+ if (dLong < 0 & dLat < 0) {
+ return 180 + Math.abs(tc);
+ }
+
+ //NW
+ if (dLong < 0 & dLat >= 0) {
+ return 360 - Math.abs(tc);
+ }
+ return -1;
+ }
+
+ /**
+ * Getter method for the distance between two points
+ * @return distance
+ * @since 1.0
+ */
+ public double getDistance() {
+ return distance;
+ }
+
+ /**
+ * Getter method for the true course
+ * @return true course
+ * @since 1.0
+ */
+ public double getTrueCourse() {
+ return trueCourse;
+ }
+
+ /**
+ * Getter method for the true course
+ * @return true course
+ * @since 1.0
+ */
+ public String getStrCourse() {
+ return strCourse;
+ }
+
+ /**
+ * Computes the difference in meridional parts for two latitudes in minutes
+ * (based on Clark 1880 spheroid)
+ * @param lat1
+ * @param lat2
+ * @return difference in minutes
+ * @since 1.0
+ */
+ public static double computeDMPClarkeSpheroid(double lat1, double lat2) {
+ double absLat1 = Math.abs(lat1);
+ double absLat2 = Math.abs(lat2);
+
+ double m1 = (7915.704468 * (Math.log(Math.tan(Math.toRadians(45
+ + (absLat1 / 2)))) / Math.log(10))
+ - 23.268932 * Math.sin(Math.toRadians(absLat1))
+ - 0.052500 * Math.pow(Math.sin(Math.toRadians(absLat1)), 3)
+ - 0.000213 * Math.pow(Math.sin(Math.toRadians(absLat1)), 5));
+
+ double m2 = (7915.704468 * (Math.log(Math.tan(Math.toRadians(45
+ + (absLat2 / 2)))) / Math.log(10))
+ - 23.268932 * Math.sin(Math.toRadians(absLat2))
+ - 0.052500 * Math.pow(Math.sin(Math.toRadians(absLat2)), 3)
+ - 0.000213 * Math.pow(Math.sin(Math.toRadians(absLat2)), 5));
+ if ((lat1 <= 0 && lat2 <= 0) || (lat1 > 0 && lat2 > 0)) {
+ return Math.abs(m1 - m2);
+ } else {
+ return m1 + m2;
+ }
+ }
+
+ /**
+ * Computes the difference in meridional parts for a perfect sphere between
+ * two degrees of latitude
+ * @param lat1
+ * @param lat2
+ * @return difference in meridional parts between lat1 and lat2 in minutes
+ * @since 1.0
+ */
+ public static float computeDMPWGS84Spheroid(float lat1, float lat2) {
+ float absLat1 = Math.abs(lat1);
+ float absLat2 = Math.abs(lat2);
+
+ float m1 = (float) (7915.7045 * Math.log10(Math.tan(Math.toRadians(45 + (absLat1 / 2))))
+ - 23.01358 * Math.sin(absLat1 - 0.05135) * Math.pow(Math.sin(absLat1), 3));
+
+ float m2 = (float) (7915.7045 * Math.log10(Math.tan(Math.toRadians(45 + (absLat2 / 2))))
+ - 23.01358 * Math.sin(absLat2 - 0.05135) * Math.pow(Math.sin(absLat2), 3));
+
+ if (lat1 <= 0 & lat2 <= 0 || lat1 > 0 & lat2 > 0) {
+ return Math.abs(m1 - m2);
+ } else {
+ return m1 + m2;
+ }
+ }
+
+ /**
+ * Predicts the position of a target for a given time in the future
+ * @param time the number of seconds from now for which to predict the future
+ * position
+ * @param speed the miles per minute that the target is traveling
+ * @param currentLat the target's current latitude
+ * @param currentLong the target's current longitude
+ * @param course the target's current course in degrees
+ * @return the predicted future position
+ * @since 1.0
+ */
+ public static Position predictPosition(int time, double speed,
+ double currentLat, double currentLong, double course) {
+ Position futurePosition = null;
+ course = Math.toRadians(course);
+ double futureLong = currentLong + speed * time * Math.sin(course);
+ double futureLat = currentLat + speed * time * Math.cos(course);
+ try {
+ futurePosition = new Position(futureLat, futureLong);
+ } catch (InvalidPositionException ipe) {
+ ipe.printStackTrace();
+ }
+ return futurePosition;
+
+ }
+
+ /**
+ * Computes the coordinate of position B relative to an offset given
+ * a distance and an angle.
+ *
+ * @param offset The offset position.
+ * @param bearing The bearing between the offset and the coordinate
+ * that you want to calculate.
+ * @param distance The distance, in meters, between the offset
+ * and point B.
+ * @return The position of point B that is located from
+ * given offset at given distance and angle.
+ * @since 1.0
+ */
+ public static Position computePosition(Position initialPos, double heading,
+ double distance) {
+ if (initialPos == null) {
+ return null;
+ }
+ double angle;
+ if (heading < 90) {
+ angle = heading;
+ } else if (heading > 90 && heading < 180) {
+ angle = 180 - heading;
+ } else if (heading > 180 && heading < 270) {
+ angle = heading - 180;
+ } else {
+ angle = 360 - heading;
+ }
+
+ Position newPosition = null;
+
+ // Convert meters into nautical miles
+ distance = distance * 0.000539956803;
+ angle = Math.toRadians(angle);
+ double initialLat = initialPos.getLatitude();
+ double initialLong = initialPos.getLongitude();
+ double dlat = distance * Math.cos(angle);
+ dlat = dlat / 60;
+ dlat = Math.abs(dlat);
+ double newLat = 0;
+ if ((heading > 270 && heading < 360) || (heading > 0 && heading < 90)) {
+ newLat = initialLat + dlat;
+ } else if (heading < 270 && heading > 90) {
+ newLat = initialLat - dlat;
+ }
+ double meanLat = (Math.abs(dlat) / 2.0) + newLat;
+ double dep = (Math.abs(dlat * 60)) * Math.tan(angle);
+ double dlong = dep * (1.0 / Math.cos(Math.toRadians(meanLat)));
+ dlong = dlong / 60;
+ dlong = Math.abs(dlong);
+ double newLong;
+ if (heading > 180 && heading < 360) {
+ newLong = initialLong - dlong;
+ } else {
+ newLong = initialLong + dlong;
+ }
+
+ if (newLong < -180) {
+ double diff = Math.abs(newLong + 180);
+ newLong = 180 - diff;
+ }
+
+ if (newLong > 180) {
+ double diff = Math.abs(newLong + 180);
+ newLong = (180 - diff) * -1;
+ }
+
+ if (heading == 0 || heading == 360 || heading == 180) {
+ newLong = initialLong;
+ newLat = initialLat + dlat;
+ } else if (heading == 90 || heading == 270) {
+ newLat = initialLat;
+// newLong = initialLong + dlong; THIS WAS THE ORIGINAL (IT WORKED)
+ newLong = initialLong - dlong;
+ }
+ try {
+ newPosition = new Position(newLat,
+ newLong);
+ } catch (InvalidPositionException ipe) {
+ ipe.printStackTrace();
+ System.out.println(newLat + "," + newLong);
+ }
+ return newPosition;
+ }
+
+ /**
+ * Computes the difference in Longitude between two positions and assigns the
+ * correct sign -westwards travel, + eastwards travel
+ * @param lng1
+ * @param lng2
+ * @return difference in longitude
+ * @since 1.0
+ */
+ public static double computeDLong(double lng1, double lng2) {
+ if (lng1 - lng2 == 0) {
+ return 0;
+ }
+
+ // both easterly
+ if (lng1 >= 0 & lng2 >= 0) {
+ return -(lng1 - lng2) * 60;
+ }
+ //both westerly
+ if (lng1 < 0 & lng2 < 0) {
+ return -(lng1 - lng2) * 60;
+ }
+
+ //opposite sides of Date line meridian
+
+ //sum less than 180
+ if (Math.abs(lng1) + Math.abs(lng2) < 180) {
+ if (lng1 < 0 & lng2 > 0) {
+ return -(Math.abs(lng1) + Math.abs(lng2)) * 60;
+ } else {
+ return Math.abs(lng1) + Math.abs(lng2) * 60;
+ }
+ } else {
+ //sum greater than 180
+ if (lng1 < 0 & lng2 > 0) {
+ return -(360 - (Math.abs(lng1) + Math.abs(lng2))) * 60;
+ } else {
+ return (360 - (Math.abs(lng1) + Math.abs(lng2))) * 60;
+ }
+ }
+ }
+
+ /**
+ * Computes the difference in Longitude between two positions and assigns the
+ * correct sign -westwards travel, + eastwards travel
+ * @param lng1
+ * @param lng2
+ * @return difference in longitude
+ * @since 1.0
+ */
+ public static double computeLongDiff(double lng1, double lng2) {
+ if (lng1 - lng2 == 0) {
+ return 0;
+ }
+
+ // both easterly
+ if (lng1 >= 0 & lng2 >= 0) {
+ return Math.abs(-(lng1 - lng2) * 60);
+ }
+ //both westerly
+ if (lng1 < 0 & lng2 < 0) {
+ return Math.abs(-(lng1 - lng2) * 60);
+ }
+
+ if (lng1 == 0) {
+ return Math.abs(lng2 * 60);
+ }
+
+ if (lng2 == 0) {
+ return Math.abs(lng1 * 60);
+ }
+
+ return (Math.abs(lng1) + Math.abs(lng2)) * 60;
+ }
+
+ /**
+ * Compute the difference in latitude between two positions
+ * @param lat1
+ * @param lat2
+ * @return difference in latitude
+ * @since 1.0
+ */
+ public static double computeDLat(double lat1, double lat2) {
+ //same side of equator
+
+ //plane sailing
+ if (lat1 - lat2 == 0) {
+ return 0;
+ }
+
+ //both northerly
+ if (lat1 >= 0 & lat2 >= 0) {
+ return -(lat1 - lat2) * 60;
+ }
+ //both southerly
+ if (lat1 < 0 & lat2 < 0) {
+ return -(lat1 - lat2) * 60;
+ }
+
+ //opposite sides of equator
+ if (lat1 >= 0) {
+ //heading south
+ return -(Math.abs(lat1) + Math.abs(lat2));
+ } else {
+ //heading north
+ return (Math.abs(lat1) + Math.abs(lat2));
+ }
+ }
+
+ public static class Quadrant {
+
+ private static final Quadrant FIRST = new Quadrant(1, 1);
+ private static final Quadrant SECOND = new Quadrant(-1, 1);
+ private static final Quadrant THIRD = new Quadrant(-1, -1);
+ private static final Quadrant FOURTH = new Quadrant(1, -1);
+ private final int lonMultiplier;
+ private final int latMultiplier;
+
+ public Quadrant(final int xMultiplier, final int yMultiplier) {
+ this.lonMultiplier = xMultiplier;
+ this.latMultiplier = yMultiplier;
+ }
+
+ static Quadrant getQuadrant(double degrees, boolean invert) {
+ if (invert) {
+ if (degrees >= 0 && degrees <= 90) {
+ return FOURTH;
+ } else if (degrees > 90 && degrees <= 180) {
+ return THIRD;
+ } else if (degrees > 180 && degrees <= 270) {
+ return SECOND;
+ }
+ return FIRST;
+ } else {
+ if (degrees >= 0 && degrees <= 90) {
+ return FIRST;
+ } else if (degrees > 90 && degrees <= 180) {
+ return SECOND;
+ } else if (degrees > 180 && degrees <= 270) {
+ return THIRD;
+ }
+ return FOURTH;
+ }
+ }
+ }
+
+ /**
+ * Converts meters to degrees.
+ *
+ * @param meters The meters that you want to convert into degrees.
+ * @return The degree equivalent of the given meters.
+ * @since 1.0
+ */
+ public static double toDegrees(double meters) {
+ return (meters / METERS_PER_MINUTE) / 60;
+ }
+
+ /**
+ * Computes the bearing between two points.
+ *
+ * @param p1
+ * @param p2
+ * @return
+ * @since 1.0
+ */
+ public static int computeBearing(Position p1, Position p2) {
+ int bearing;
+ double dLon = computeDLong(p1.getLongitude(), p2.getLongitude());
+ double y = Math.sin(dLon) * Math.cos(p2.getLatitude());
+ double x = Math.cos(p1.getLatitude()) * Math.sin(p2.getLatitude())
+ - Math.sin(p1.getLatitude()) * Math.cos(p2.getLatitude()) * Math.cos(dLon);
+ bearing = (int) Math.toDegrees(Math.atan2(y, x));
+ return bearing;
+ }
+
+ /**
+ * Computes the angle between two points.
+ *
+ * @param p1
+ * @param p2
+ * @return
+ */
+ public static int computeAngle(Position p1, Position p2) {
+ // cos (adj / hyp)
+ double adj = Math.abs(p1.getLongitude() - p2.getLongitude());
+ double opp = Math.abs(p1.getLatitude() - p2.getLatitude());
+ return (int) Math.toDegrees(Math.atan(opp / adj));
+
+// int angle = (int)Math.atan2(p2.getLatitude() - p1.getLatitude(),
+// p2.getLongitude() - p1.getLongitude());
+ //Actually it's ATan2(dy , dx) where dy = y2 - y1 and dx = x2 - x1, or ATan(dy / dx)
+ }
+
+ public static int computeHeading(Position p1, Position p2) {
+ int angle = computeAngle(p1, p2);
+ // NE
+ if (p2.getLongitude() >= p1.getLongitude() && p2.getLatitude() >= p1.getLatitude()) {
+ return angle;
+ } else if (p2.getLongitude() >= p1.getLongitude() && p2.getLatitude() <= p1.getLatitude()) {
+ // SE
+ return 90 + angle;
+ } else if (p2.getLongitude() <= p1.getLongitude() && p2.getLatitude() <= p1.getLatitude()) {
+ // SW
+ return 270 - angle;
+ } else {
+ // NW
+ return 270 + angle;
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ int pos = NavCalculator.computeHeading(new Position(0, 0), new Position(10, -10));
+// System.out.println(pos.getLatitude() + "," + pos.getLongitude());
+ System.out.println(pos);
+ } catch (Exception e) {
+ }
+
+
+
+
+
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/NumUtil.java b/engine/src/desktop/jme3tools/navigation/NumUtil.java
new file mode 100644
index 0000000..086fff2
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/NumUtil.java
@@ -0,0 +1,30 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+/**
+ * Provides various helper methods for number conversions (such as degree to radian
+ * conversion, decimal degree to radians etc)
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+public class NumUtil {
+
+ /**
+ * Rounds a number
+ * @param Rval number to be rounded
+ * @param Rpl number of decimal places
+ * @return rounded number
+ * @since 0.1
+ */
+ public float Round(float Rval, int Rpl) {
+ float p = (float) Math.pow(10, Rpl);
+ Rval = Rval * p;
+ float tmp = Math.round(Rval);
+ return (float) tmp / p;
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/Position.java b/engine/src/desktop/jme3tools/navigation/Position.java
new file mode 100644
index 0000000..b55e852
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/Position.java
@@ -0,0 +1,228 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+/**
+ * This class represents the position of an entity in the world.
+ *
+ * @author Benjamin Jakobus (based on JMarine by Cormac Gebruers and Benjamin Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+public class Position {
+
+ /* the latitude (+ N/E) */
+ private Coordinate lat;
+
+ /* the longitude (-W/S) */
+ private Coordinate lng;
+
+ /* An optional time to associate with this position - for historical tracking */
+ private String utcTimeStamp;
+
+ /* Degree position */
+ private double degree;
+
+ /**
+ * A new position expressed in decimal format
+ * @param dblLat
+ * @param dblLng
+ * @since 1.0
+ */
+ public Position(double dblLat, double dblLng) throws InvalidPositionException {
+ lat = new Coordinate(dblLat, Coordinate.LAT);
+ lng = new Coordinate(dblLng, Coordinate.LNG);
+ }
+
+ /**
+ * A new position expressed in decimal format and degrees
+ * @param dblLat
+ * @param dblLng
+ * @param degree
+ * @since 1.0
+ */
+// public Position(double dblLat, double dblLng, double degree) throws InvalidPositionException {
+// lat = new Coordinate(dblLat, Coordinate.LAT);
+// lng = new Coordinate(dblLng, Coordinate.LNG);
+// this.degree = degree;
+// }
+ /**
+ * A new position expressed in DegMin format
+ * @param latDeg
+ * @param latMin
+ * @param lngDeg
+ * @param lngMin
+ * @since 1.0
+ */
+ public Position(int latDeg, float latMin, int latQuad, int lngDeg,
+ float lngMin, int lngQuad) throws InvalidPositionException {
+ lat = new Coordinate(latDeg, latMin, Coordinate.LAT, latQuad);
+ lng = new Coordinate(lngDeg, lngMin, Coordinate.LNG, lngQuad);
+ }
+
+ /**
+ * A new position expressed in ALRS format
+ * @param lat
+ * @param lng
+ * @since 1.0
+ */
+ public Position(String lat, String lng) throws InvalidPositionException {
+ this.lat = new Coordinate(lat);
+ this.lng = new Coordinate(lng);
+ }
+
+ /**
+ * A new position expressed in NMEA GPS message format:
+ * 4807.038,N,01131.000,E
+ * @param
+ * @param
+ * @param
+ * @param
+ * @since 12.0
+ */
+ public Position(String latNMEAGPS, String latQuad, String lngNMEAGPS, String lngQuad, String utcTimeStamp) {
+ int quad;
+
+ //LAT
+ if (latQuad.compareTo("N") == 0) {
+ quad = Coordinate.N;
+ } else {
+ quad = Coordinate.S;
+ }
+ try {
+ this.lat = new Coordinate(Integer.valueOf(latNMEAGPS.substring(0, 2)), Float.valueOf(latNMEAGPS.substring(2)), Coordinate.LAT, quad);
+ } catch (InvalidPositionException e) {
+ e.printStackTrace();
+ }
+
+ //LNG
+ if (lngQuad.compareTo("E") == 0) {
+ quad = Coordinate.E;
+ } else {
+ quad = Coordinate.W;
+ }
+ try {
+ this.lng = new Coordinate(Integer.valueOf(lngNMEAGPS.substring(0, 3)), Float.valueOf(lngNMEAGPS.substring(3)), Coordinate.LNG, quad);
+ } catch (InvalidPositionException e) {
+ e.printStackTrace();
+ }
+
+ //TIMESTAMP
+ this.associateUTCTime(utcTimeStamp);
+ }
+
+ /**
+ * Add a reference time for this position - useful for historical tracking
+ * @param data
+ * @since 1.0
+ */
+ public void associateUTCTime(String data) {
+ utcTimeStamp = data;
+ }
+
+ /**
+ * Returns the UTC time stamp
+ * @return str the UTC timestamp
+ * @since 1.0
+ */
+ public String utcTimeStamp() {
+ return utcTimeStamp;
+ }
+
+ /**
+ * Prints out position using decimal format
+ * @return the position in decimal format
+ */
+ public String toStringDec() {
+ return lat.toStringDec() + " " + lng.toStringDec();
+ }
+
+ /**
+ * Return the position latitude in decimal format
+ * @return the latitude in decimal format
+ * @since 1.0
+ */
+ public double getLatitude() {
+ return lat.decVal();
+ }
+
+ /**
+ * Returns the degree of the entity
+ * @return degree
+ * @since 1.0
+ */
+// public double getDegree() {
+// return degree;
+// }
+ /**
+ * Return the position longitude in decimal format
+ * @return the longitude in decimal format
+ * @since 1.0
+ */
+ public double getLongitude() {
+ return lng.decVal();
+ }
+
+ /**
+ * Prints out position using DegMin format
+ * @return the position in DegMin Format
+ * @since 1.0
+ */
+ public String toStringDegMin() {
+ String output = "";
+ output += lat.toStringDegMin();
+ output += " " + lng.toStringDegMin();
+ return output;
+ }
+
+ /**
+ * Prints out the position latitude
+ * @return the latitude as a string for display purposes
+ * @since 1.0
+ */
+ public String toStringDegMinLat() {
+ return lat.toStringDegMin();
+ }
+
+ /**
+ * Prints out the position longitude
+ * @return the longitude as a string for display purposes
+ * @since 1.0
+ */
+ public String toStringDegMinLng() {
+ return lng.toStringDegMin();
+ }
+
+ /**
+ * Prints out the position latitude
+ * @return the latitude as a string for display purposes
+ * @since 1.0
+ */
+ public String toStringDecLat() {
+ return lat.toStringDec();
+ }
+
+ /**
+ * Prints out the position longitude
+ * @return the longitude as a string for display purposes
+ * @since 1.0
+ */
+ public String toStringDecLng() {
+ return lng.toStringDec();
+ }
+
+ //TEST HARNESS - DO NOT DELETE!
+ public static void main(String[] argsc) {
+
+ //NMEA GPS Position format:
+ Position p = new Position("4807.038", "N", "01131.000", "W", "123519");
+ System.out.println(p.toStringDegMinLat());
+ System.out.println(p.getLatitude());
+ System.out.println(p.getLongitude());
+ System.out.println(p.toStringDegMinLng());
+ System.out.println(p.utcTimeStamp());
+
+ }//main
+}
diff --git a/engine/src/desktop/jme3tools/navigation/RLSailing.java b/engine/src/desktop/jme3tools/navigation/RLSailing.java
new file mode 100644
index 0000000..1a9a91c
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/RLSailing.java
@@ -0,0 +1,32 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+/**
+ * A utility class to package up a rhumb line sailing
+ *
+ * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin
+ * Jakobus)
+ * @version 1.0
+ * @since 1.0
+ */
+public class RLSailing {
+
+ private double course;
+ private double distNM;
+
+ public RLSailing(double pCourse, double pDistNM) {
+ course = pCourse;
+ distNM = pDistNM;
+ }
+
+ public double getCourse() {
+ return course;
+ }
+
+ public double getDistNM() {
+ return distNM;
+ }
+}
diff --git a/engine/src/desktop/jme3tools/navigation/StringUtil.java b/engine/src/desktop/jme3tools/navigation/StringUtil.java
new file mode 100644
index 0000000..b59f68f
--- /dev/null
+++ b/engine/src/desktop/jme3tools/navigation/StringUtil.java
@@ -0,0 +1,260 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.navigation;
+
+import java.util.regex.Pattern;
+
+/**
+ * A collection of String utilities.
+ *
+ * @author Benjamin Jakobus
+ * @version 1.0
+ */
+public class StringUtil {
+
+ /**
+ * Splits a newline (\n) delimited string into an array of strings
+ *
+ * @param str the string to split up
+ * @param delimiter the delimiter to use in splitting
+ * @returns an array of String objects equivalent to str
+ */
+ public String[] splitDelimitedStr(String str, String delimiter) {
+ Pattern pttn = Pattern.compile(delimiter);
+ return pttn.split(str);
+ }
+
+ /**
+ * Right aligns a long number with spaces for printing
+ *
+ * @param num the number to be aligned
+ * @param totalLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNum(long num, int totalLen) {
+ String numStr = Long.toString(num);
+ int len = totalLen - numStr.length();
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += " ";
+ }
+ return pads + numStr;
+ }
+
+ /**
+ * Right aligns a long number with zeros for printing
+ *
+ * @param num the number to be aligned
+ * @param totalLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNumZero(long num, int totalLen) {
+ String numStr = Long.toString(num);
+ int len = totalLen - numStr.length();
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += "0";
+ }
+ return pads + numStr;
+ }
+
+ /**
+ * Right aligns an integer number with spaces for printing
+ *
+ * @param num the number to be aligned
+ * @param totalLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNum(int num, int totalLen) {
+ String numStr = Integer.toString(num);
+ int len = totalLen - numStr.length();
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += " ";
+ }
+ return pads + numStr;
+ }
+
+ /**
+ * Right aligns an integer number with zeros for printing
+ *
+ * @param num the number to be aligned
+ * @param totalLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNumZero(int num, int totalLen) {
+ String numStr = Integer.toString(num);
+ int len = totalLen - numStr.length();
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += "0";
+ }
+ return pads + numStr;
+ }
+
+ /**
+ * Right aligns a double number with spaces for printing
+ *
+ * @param num the number to be aligned
+ * @param wholeLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNum(double num, int wholeLen, int decimalPlaces) {
+ String numStr = Double.toString(num);
+ int dpLoc = numStr.indexOf(".");
+
+ int len = wholeLen - dpLoc;
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += " ";
+ }
+
+ numStr = pads + numStr;
+
+ dpLoc = numStr.indexOf(".");
+
+ if (dpLoc + 1 + decimalPlaces > numStr.substring(dpLoc).length()) {
+ return numStr;
+ }
+ return numStr.substring(0, dpLoc + 1 + decimalPlaces);
+ }
+
+ /**
+ * Right aligns a double number with zeros for printing
+ *
+ * @param num the number to be aligned
+ * @param wholeLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNumZero(double num, int wholeLen, int decimalPlaces) {
+ String numStr = Double.toString(num);
+ int dpLoc = numStr.indexOf(".");
+
+ int len = wholeLen - dpLoc;
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += "0";
+ }
+
+ numStr = pads + numStr;
+
+ dpLoc = numStr.indexOf(".");
+
+ if (dpLoc + 1 + decimalPlaces > numStr.substring(dpLoc).length()) {
+ return numStr;
+ }
+ return numStr.substring(0, dpLoc + 1 + decimalPlaces);
+ }
+
+ /**
+ * Right aligns a float number with spaces for printing
+ *
+ * @param num the number to be aligned
+ * @param wholeLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNum(float num, int wholeLen, int decimalPlaces) {
+ String numStr = Float.toString(num);
+ int dpLoc = numStr.indexOf(".");
+
+ int len = wholeLen - dpLoc;
+ String pads = "";
+ for (int i = 0; i < len; i++) {
+ pads += " ";
+ }
+
+ numStr = pads + numStr;
+
+ dpLoc = numStr.indexOf(".");
+
+ if (dpLoc + 1 + decimalPlaces > numStr.substring(dpLoc).length()) {
+ return numStr;
+ }
+ return numStr.substring(0, dpLoc + 1 + decimalPlaces);
+ }
+
+ /**
+ * Right aligns a float number with zeros for printing
+ *
+ * @param num the number to be aligned
+ * @param wholeLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padNumZero(float num, int wholeLen, int decimalPlaces) {
+ String numStr = Float.toString(num);
+ int dpLoc = numStr.indexOf(".");
+
+ int len = wholeLen - dpLoc;
+ String pads = "";
+
+ if (numStr.charAt(0) == '-') {
+ len += 1;
+ for (int i = 0; i < len; i++) {
+ pads += "0";
+ }
+ pads = "-" + pads;
+ numStr = pads + numStr.substring(1);
+ } else {
+ for (int i = 0; i < len; i++) {
+ pads += "0";
+ }
+ numStr = pads + numStr;
+ }
+
+ dpLoc = numStr.indexOf(".");
+ int length = numStr.substring(dpLoc).length();
+ while (length < decimalPlaces) {
+ numStr += "0";
+ }
+ return numStr;
+
+ }
+
+ /**
+ * Right aligns a float number with zeros for printing
+ *
+ * @param num the number to be aligned
+ * @param wholeLen the total length of the padded string
+ * @return the padded number
+ */
+ public String padStringRight(String input, int wholeLen) {
+ for (int i = input.length(); i < wholeLen; i++) {
+ input += " ";
+ }
+ return input;
+ }
+
+ /**
+ * @param arr a boolean array to be represented as a string
+ * @return the array as a string
+ */
+ public String boolArrToStr(boolean[] arr) {
+ String output = "";
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i]) {
+ output += "1";
+ } else {
+ output += "0";
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Formats a double nicely for printing: THIS DOES NOT ROUND!!!!
+ * @param num the double to be turned into a pretty string
+ * @return the pretty string
+ */
+ public String prettyNum(double num) {
+ String numStr = (new Double(num)).toString();
+
+ while (numStr.length() < 4) {
+ numStr += "0";
+ }
+
+ numStr = numStr.substring(0, numStr.indexOf(".") + 3);
+ return numStr;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java b/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java
new file mode 100644
index 0000000..e4dba9c
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet;
+
+import com.bulletphysics.BulletGlobals;
+import com.bulletphysics.ContactAddedCallback;
+import com.bulletphysics.ContactDestroyedCallback;
+import com.bulletphysics.ContactProcessedCallback;
+import com.bulletphysics.collision.broadphase.*;
+import com.bulletphysics.collision.dispatch.CollisionWorld.LocalConvexResult;
+import com.bulletphysics.collision.dispatch.CollisionWorld.LocalRayResult;
+import com.bulletphysics.collision.dispatch.*;
+import com.bulletphysics.collision.narrowphase.ManifoldPoint;
+import com.bulletphysics.collision.shapes.ConvexShape;
+import com.bulletphysics.dynamics.DiscreteDynamicsWorld;
+import com.bulletphysics.dynamics.DynamicsWorld;
+import com.bulletphysics.dynamics.InternalTickCallback;
+import com.bulletphysics.dynamics.RigidBody;
+import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver;
+import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
+import com.bulletphysics.extras.gimpact.GImpactCollisionAlgorithm;
+import com.jme3.app.AppTask;
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.collision.*;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.PhysicsControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.joints.PhysicsJoint;
+import com.jme3.bullet.objects.PhysicsCharacter;
+import com.jme3.bullet.objects.PhysicsGhostObject;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.bullet.util.Converter;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>PhysicsSpace - The central jbullet-jme physics space</p>
+ * @author normenhansen
+ */
+public class PhysicsSpace {
+
+ public static final int AXIS_X = 0;
+ public static final int AXIS_Y = 1;
+ public static final int AXIS_Z = 2;
+ private static ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>> pQueueTL =
+ new ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>>() {
+
+ @Override
+ protected ConcurrentLinkedQueue<AppTask<?>> initialValue() {
+ return new ConcurrentLinkedQueue<AppTask<?>>();
+ }
+ };
+ private ConcurrentLinkedQueue<AppTask<?>> pQueue = new ConcurrentLinkedQueue<AppTask<?>>();
+ private static ThreadLocal<PhysicsSpace> physicsSpaceTL = new ThreadLocal<PhysicsSpace>();
+ private DiscreteDynamicsWorld dynamicsWorld = null;
+ private BroadphaseInterface broadphase;
+ private BroadphaseType broadphaseType = BroadphaseType.DBVT;
+ private CollisionDispatcher dispatcher;
+ private ConstraintSolver solver;
+ private DefaultCollisionConfiguration collisionConfiguration;
+// private Map<GhostObject, PhysicsGhostObject> physicsGhostNodes = new ConcurrentHashMap<GhostObject, PhysicsGhostObject>();
+ private Map<RigidBody, PhysicsRigidBody> physicsNodes = new ConcurrentHashMap<RigidBody, PhysicsRigidBody>();
+ private List<PhysicsJoint> physicsJoints = new LinkedList<PhysicsJoint>();
+ private List<PhysicsCollisionListener> collisionListeners = new LinkedList<PhysicsCollisionListener>();
+ private List<PhysicsCollisionEvent> collisionEvents = new LinkedList<PhysicsCollisionEvent>();
+ private Map<Integer, PhysicsCollisionGroupListener> collisionGroupListeners = new ConcurrentHashMap<Integer, PhysicsCollisionGroupListener>();
+ private ConcurrentLinkedQueue<PhysicsTickListener> tickListeners = new ConcurrentLinkedQueue<PhysicsTickListener>();
+ private PhysicsCollisionEventFactory eventFactory = new PhysicsCollisionEventFactory();
+ private Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f);
+ private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
+ private float accuracy = 1f / 60f;
+ private int maxSubSteps = 4;
+ private javax.vecmath.Vector3f rayVec1 = new javax.vecmath.Vector3f();
+ private javax.vecmath.Vector3f rayVec2 = new javax.vecmath.Vector3f();
+ private com.bulletphysics.linearmath.Transform sweepTrans1 = new com.bulletphysics.linearmath.Transform(new javax.vecmath.Matrix3f());
+ private com.bulletphysics.linearmath.Transform sweepTrans2 = new com.bulletphysics.linearmath.Transform(new javax.vecmath.Matrix3f());
+ private AssetManager debugManager;
+
+ /**
+ * Get the current PhysicsSpace <b>running on this thread</b><br/>
+ * For parallel physics, this can also be called from the OpenGL thread to receive the PhysicsSpace
+ * @return the PhysicsSpace running on this thread
+ */
+ public static PhysicsSpace getPhysicsSpace() {
+ return physicsSpaceTL.get();
+ }
+
+ /**
+ * Used internally
+ * @param space
+ */
+ public static void setLocalThreadPhysicsSpace(PhysicsSpace space) {
+ physicsSpaceTL.set(space);
+ }
+
+ public PhysicsSpace() {
+ this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT);
+ }
+
+ public PhysicsSpace(BroadphaseType broadphaseType) {
+ this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
+ }
+
+ public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) {
+ this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
+ }
+
+ public PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) {
+ this.worldMin.set(worldMin);
+ this.worldMax.set(worldMax);
+ this.broadphaseType = broadphaseType;
+ create();
+ }
+
+ /**
+ * Has to be called from the (designated) physics thread
+ */
+ public void create() {
+ pQueueTL.set(pQueue);
+
+ collisionConfiguration = new DefaultCollisionConfiguration();
+ dispatcher = new CollisionDispatcher(collisionConfiguration);
+ switch (broadphaseType) {
+ case SIMPLE:
+ broadphase = new SimpleBroadphase();
+ break;
+ case AXIS_SWEEP_3:
+ broadphase = new AxisSweep3(Converter.convert(worldMin), Converter.convert(worldMax));
+ break;
+ case AXIS_SWEEP_3_32:
+ broadphase = new AxisSweep3_32(Converter.convert(worldMin), Converter.convert(worldMax));
+ break;
+ case DBVT:
+ broadphase = new DbvtBroadphase();
+ break;
+ }
+
+ solver = new SequentialImpulseConstraintSolver();
+
+ dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
+ dynamicsWorld.setGravity(new javax.vecmath.Vector3f(0, -9.81f, 0));
+
+ broadphase.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback());
+ GImpactCollisionAlgorithm.registerAlgorithm(dispatcher);
+
+ physicsSpaceTL.set(this);
+ //register filter callback for tick / collision
+ setTickCallback();
+ setContactCallbacks();
+ //register filter callback for collision groups
+ setOverlapFilterCallback();
+ }
+
+ private void setOverlapFilterCallback() {
+ OverlapFilterCallback callback = new OverlapFilterCallback() {
+
+ public boolean needBroadphaseCollision(BroadphaseProxy bp, BroadphaseProxy bp1) {
+ boolean collides = (bp.collisionFilterGroup & bp1.collisionFilterMask) != 0;
+ if (collides) {
+ collides = (bp1.collisionFilterGroup & bp.collisionFilterMask) != 0;
+ }
+ if (collides) {
+ assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject);
+ com.bulletphysics.collision.dispatch.CollisionObject colOb = (com.bulletphysics.collision.dispatch.CollisionObject) bp.clientObject;
+ com.bulletphysics.collision.dispatch.CollisionObject colOb1 = (com.bulletphysics.collision.dispatch.CollisionObject) bp1.clientObject;
+ assert (colOb.getUserPointer() != null && colOb1.getUserPointer() != null);
+ PhysicsCollisionObject collisionObject = (PhysicsCollisionObject) colOb.getUserPointer();
+ PhysicsCollisionObject collisionObject1 = (PhysicsCollisionObject) colOb1.getUserPointer();
+ if ((collisionObject.getCollideWithGroups() & collisionObject1.getCollisionGroup()) > 0
+ || (collisionObject1.getCollideWithGroups() & collisionObject.getCollisionGroup()) > 0) {
+ PhysicsCollisionGroupListener listener = collisionGroupListeners.get(collisionObject.getCollisionGroup());
+ PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(collisionObject1.getCollisionGroup());
+ if (listener != null) {
+ return listener.collide(collisionObject, collisionObject1);
+ } else if (listener1 != null) {
+ return listener1.collide(collisionObject, collisionObject1);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return collides;
+ }
+ };
+ dynamicsWorld.getPairCache().setOverlapFilterCallback(callback);
+ }
+
+ private void setTickCallback() {
+ final PhysicsSpace space = this;
+ InternalTickCallback callback2 = new InternalTickCallback() {
+
+ @Override
+ public void internalTick(DynamicsWorld dw, float f) {
+ //execute task list
+ AppTask task = pQueue.poll();
+ task = pQueue.poll();
+ while (task != null) {
+ while (task.isCancelled()) {
+ task = pQueue.poll();
+ }
+ try {
+ task.invoke();
+ } catch (Exception ex) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ task = pQueue.poll();
+ }
+ for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
+ PhysicsTickListener physicsTickCallback = it.next();
+ physicsTickCallback.prePhysicsTick(space, f);
+ }
+ }
+ };
+ dynamicsWorld.setPreTickCallback(callback2);
+ InternalTickCallback callback = new InternalTickCallback() {
+
+ @Override
+ public void internalTick(DynamicsWorld dw, float f) {
+ for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
+ PhysicsTickListener physicsTickCallback = it.next();
+ physicsTickCallback.physicsTick(space, f);
+ }
+ }
+ };
+ dynamicsWorld.setInternalTickCallback(callback, this);
+ }
+
+ private void setContactCallbacks() {
+ BulletGlobals.setContactAddedCallback(new ContactAddedCallback() {
+
+ public boolean contactAdded(ManifoldPoint cp, com.bulletphysics.collision.dispatch.CollisionObject colObj0,
+ int partId0, int index0, com.bulletphysics.collision.dispatch.CollisionObject colObj1, int partId1,
+ int index1) {
+ System.out.println("contact added");
+ return true;
+ }
+ });
+
+ BulletGlobals.setContactProcessedCallback(new ContactProcessedCallback() {
+
+ public boolean contactProcessed(ManifoldPoint cp, Object body0, Object body1) {
+ if (body0 instanceof CollisionObject && body1 instanceof CollisionObject) {
+ PhysicsCollisionObject node = null, node1 = null;
+ CollisionObject rBody0 = (CollisionObject) body0;
+ CollisionObject rBody1 = (CollisionObject) body1;
+ node = (PhysicsCollisionObject) rBody0.getUserPointer();
+ node1 = (PhysicsCollisionObject) rBody1.getUserPointer();
+ collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, cp));
+ }
+ return true;
+ }
+ });
+
+ BulletGlobals.setContactDestroyedCallback(new ContactDestroyedCallback() {
+
+ public boolean contactDestroyed(Object userPersistentData) {
+ System.out.println("contact destroyed");
+ return true;
+ }
+ });
+ }
+
+ /**
+ * updates the physics space
+ * @param time the current time value
+ */
+ public void update(float time) {
+ update(time, maxSubSteps);
+ }
+
+ /**
+ * updates the physics space, uses maxSteps<br>
+ * @param time the current time value
+ * @param maxSteps
+ */
+ public void update(float time, int maxSteps) {
+ if (getDynamicsWorld() == null) {
+ return;
+ }
+ //step simulation
+ dynamicsWorld.stepSimulation(time, maxSteps, accuracy);
+ }
+
+ public void distributeEvents() {
+ //add collision callbacks
+ synchronized (collisionEvents) {
+ for (Iterator<PhysicsCollisionEvent> it = collisionEvents.iterator(); it.hasNext();) {
+ PhysicsCollisionEvent physicsCollisionEvent = it.next();
+ for (PhysicsCollisionListener listener : collisionListeners) {
+ listener.collision(physicsCollisionEvent);
+ }
+ //recycle events
+ eventFactory.recycle(physicsCollisionEvent);
+ it.remove();
+ }
+ }
+ }
+
+ public static <V> Future<V> enqueueOnThisThread(Callable<V> callable) {
+ AppTask<V> task = new AppTask<V>(callable);
+ System.out.println("created apptask");
+ pQueueTL.get().add(task);
+ return task;
+ }
+
+ /**
+ * calls the callable on the next physics tick (ensuring e.g. force applying)
+ * @param <V>
+ * @param callable
+ * @return
+ */
+ public <V> Future<V> enqueue(Callable<V> callable) {
+ AppTask<V> task = new AppTask<V>(callable);
+ pQueue.add(task);
+ return task;
+ }
+
+ /**
+ * adds an object to the physics space
+ * @param obj the PhysicsControl or Spatial with PhysicsControl to add
+ */
+ public void add(Object obj) {
+ if (obj instanceof PhysicsControl) {
+ ((PhysicsControl) obj).setPhysicsSpace(this);
+ } else if (obj instanceof Spatial) {
+ Spatial node = (Spatial) obj;
+ PhysicsControl control = node.getControl(PhysicsControl.class);
+ control.setPhysicsSpace(this);
+ } else if (obj instanceof PhysicsCollisionObject) {
+ addCollisionObject((PhysicsCollisionObject) obj);
+ } else if (obj instanceof PhysicsJoint) {
+ addJoint((PhysicsJoint) obj);
+ } else {
+ throw (new UnsupportedOperationException("Cannot add this kind of object to the physics space."));
+ }
+ }
+
+ public void addCollisionObject(PhysicsCollisionObject obj) {
+ if (obj instanceof PhysicsGhostObject) {
+ addGhostObject((PhysicsGhostObject) obj);
+ } else if (obj instanceof PhysicsRigidBody) {
+ addRigidBody((PhysicsRigidBody) obj);
+ } else if (obj instanceof PhysicsVehicle) {
+ addRigidBody((PhysicsVehicle) obj);
+ } else if (obj instanceof PhysicsCharacter) {
+ addCharacter((PhysicsCharacter) obj);
+ }
+ }
+
+ /**
+ * removes an object from the physics space
+ * @param obj the PhysicsControl or Spatial with PhysicsControl to remove
+ */
+ public void remove(Object obj) {
+ if (obj instanceof PhysicsControl) {
+ ((PhysicsControl) obj).setPhysicsSpace(null);
+ } else if (obj instanceof Spatial) {
+ Spatial node = (Spatial) obj;
+ PhysicsControl control = node.getControl(PhysicsControl.class);
+ control.setPhysicsSpace(null);
+ } else if (obj instanceof PhysicsCollisionObject) {
+ removeCollisionObject((PhysicsCollisionObject) obj);
+ } else if (obj instanceof PhysicsJoint) {
+ removeJoint((PhysicsJoint) obj);
+ } else {
+ throw (new UnsupportedOperationException("Cannot remove this kind of object from the physics space."));
+ }
+ }
+
+ public void removeCollisionObject(PhysicsCollisionObject obj) {
+ if (obj instanceof PhysicsGhostObject) {
+ removeGhostObject((PhysicsGhostObject) obj);
+ } else if (obj instanceof PhysicsRigidBody) {
+ removeRigidBody((PhysicsRigidBody) obj);
+ } else if (obj instanceof PhysicsCharacter) {
+ removeCharacter((PhysicsCharacter) obj);
+ }
+ }
+
+ /**
+ * adds all physics controls and joints in the given spatial node to the physics space
+ * (e.g. after loading from disk) - recursive if node
+ * @param spatial the rootnode containing the physics objects
+ */
+ public void addAll(Spatial spatial) {
+ if (spatial.getControl(RigidBodyControl.class) != null) {
+ RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class);
+ if (!physicsNodes.containsValue(physicsNode)) {
+ physicsNode.setPhysicsSpace(this);
+ }
+ //add joints
+ List<PhysicsJoint> joints = physicsNode.getJoints();
+ for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) {
+ PhysicsJoint physicsJoint = it1.next();
+ //add connected physicsnodes if they are not already added
+ if (!physicsNodes.containsValue(physicsJoint.getBodyA())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ add(physicsJoint.getBodyA());
+ } else {
+ addRigidBody(physicsJoint.getBodyA());
+ }
+ }
+ if (!physicsNodes.containsValue(physicsJoint.getBodyB())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ add(physicsJoint.getBodyB());
+ } else {
+ addRigidBody(physicsJoint.getBodyB());
+ }
+ }
+ if (!physicsJoints.contains(physicsJoint)) {
+ addJoint(physicsJoint);
+ }
+ }
+ } else if (spatial.getControl(PhysicsControl.class) != null) {
+ spatial.getControl(PhysicsControl.class).setPhysicsSpace(this);
+ }
+ //recursion
+ if (spatial instanceof Node) {
+ List<Spatial> children = ((Node) spatial).getChildren();
+ for (Iterator<Spatial> it = children.iterator(); it.hasNext();) {
+ Spatial spat = it.next();
+ addAll(spat);
+ }
+ }
+ }
+
+ /**
+ * Removes all physics controls and joints in the given spatial from the physics space
+ * (e.g. before saving to disk) - recursive if node
+ * @param spatial the rootnode containing the physics objects
+ */
+ public void removeAll(Spatial spatial) {
+ if (spatial.getControl(RigidBodyControl.class) != null) {
+ RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class);
+ if (physicsNodes.containsValue(physicsNode)) {
+ physicsNode.setPhysicsSpace(null);
+ }
+ //remove joints
+ List<PhysicsJoint> joints = physicsNode.getJoints();
+ for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) {
+ PhysicsJoint physicsJoint = it1.next();
+ //add connected physicsnodes if they are not already added
+ if (physicsNodes.containsValue(physicsJoint.getBodyA())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ remove(physicsJoint.getBodyA());
+ } else {
+ removeRigidBody(physicsJoint.getBodyA());
+ }
+ }
+ if (physicsNodes.containsValue(physicsJoint.getBodyB())) {
+ if (physicsJoint.getBodyA() instanceof PhysicsControl) {
+ remove(physicsJoint.getBodyB());
+ } else {
+ removeRigidBody(physicsJoint.getBodyB());
+ }
+ }
+ if (physicsJoints.contains(physicsJoint)) {
+ removeJoint(physicsJoint);
+ }
+ }
+ } else if (spatial.getControl(PhysicsControl.class) != null) {
+ spatial.getControl(PhysicsControl.class).setPhysicsSpace(null);
+ }
+ //recursion
+ if (spatial instanceof Node) {
+ List<Spatial> children = ((Node) spatial).getChildren();
+ for (Iterator<Spatial> it = children.iterator(); it.hasNext();) {
+ Spatial spat = it.next();
+ removeAll(spat);
+ }
+ }
+ }
+
+ private void addGhostObject(PhysicsGhostObject node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding ghost object {0} to physics space.", node.getObjectId());
+ dynamicsWorld.addCollisionObject(node.getObjectId());
+ }
+
+ private void removeGhostObject(PhysicsGhostObject node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing ghost object {0} from physics space.", node.getObjectId());
+ dynamicsWorld.removeCollisionObject(node.getObjectId());
+ }
+
+ private void addCharacter(PhysicsCharacter node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding character {0} to physics space.", node.getObjectId());
+// dynamicsWorld.addCollisionObject(node.getObjectId());
+ dynamicsWorld.addCollisionObject(node.getObjectId(), CollisionFilterGroups.CHARACTER_FILTER, (short) (CollisionFilterGroups.STATIC_FILTER | CollisionFilterGroups.DEFAULT_FILTER));
+ dynamicsWorld.addAction(node.getControllerId());
+ }
+
+ private void removeCharacter(PhysicsCharacter node) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing character {0} from physics space.", node.getObjectId());
+ dynamicsWorld.removeAction(node.getControllerId());
+ dynamicsWorld.removeCollisionObject(node.getObjectId());
+ }
+
+ private void addRigidBody(PhysicsRigidBody node) {
+ physicsNodes.put(node.getObjectId(), node);
+
+ //Workaround
+ //It seems that adding a Kinematic RigidBody to the dynamicWorld prevent it from being non kinematic again afterward.
+ //so we add it non kinematic, then set it kinematic again.
+ boolean kinematic = false;
+ if (node.isKinematic()) {
+ kinematic = true;
+ node.setKinematic(false);
+ }
+ dynamicsWorld.addRigidBody(node.getObjectId());
+ if (kinematic) {
+ node.setKinematic(true);
+ }
+
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding RigidBody {0} to physics space.", node.getObjectId());
+ if (node instanceof PhysicsVehicle) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding vehicle constraint {0} to physics space.", ((PhysicsVehicle) node).getVehicleId());
+ ((PhysicsVehicle) node).createVehicle(this);
+ dynamicsWorld.addVehicle(((PhysicsVehicle) node).getVehicleId());
+ }
+ }
+
+ private void removeRigidBody(PhysicsRigidBody node) {
+ if (node instanceof PhysicsVehicle) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing vehicle constraint {0} from physics space.", ((PhysicsVehicle) node).getVehicleId());
+ dynamicsWorld.removeVehicle(((PhysicsVehicle) node).getVehicleId());
+ }
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing RigidBody {0} from physics space.", node.getObjectId());
+ physicsNodes.remove(node.getObjectId());
+ dynamicsWorld.removeRigidBody(node.getObjectId());
+ }
+
+ private void addJoint(PhysicsJoint joint) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding Joint {0} to physics space.", joint.getObjectId());
+ physicsJoints.add(joint);
+ dynamicsWorld.addConstraint(joint.getObjectId(), !joint.isCollisionBetweenLinkedBodys());
+ }
+
+ private void removeJoint(PhysicsJoint joint) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing Joint {0} from physics space.", joint.getObjectId());
+ physicsJoints.remove(joint);
+ dynamicsWorld.removeConstraint(joint.getObjectId());
+ }
+
+ /**
+ * Sets the gravity of the PhysicsSpace, set before adding physics objects!
+ * @param gravity
+ */
+ public void setGravity(Vector3f gravity) {
+ dynamicsWorld.setGravity(Converter.convert(gravity));
+ }
+
+ /**
+ * applies gravity value to all objects
+ */
+ public void applyGravity() {
+ dynamicsWorld.applyGravity();
+ }
+
+ /**
+ * clears forces of all objects
+ */
+ public void clearForces() {
+ dynamicsWorld.clearForces();
+ }
+
+ /**
+ * Adds the specified listener to the physics tick listeners.
+ * The listeners are called on each physics step, which is not necessarily
+ * each frame but is determined by the accuracy of the physics space.
+ * @param listener
+ */
+ public void addTickListener(PhysicsTickListener listener) {
+ tickListeners.add(listener);
+ }
+
+ public void removeTickListener(PhysicsTickListener listener) {
+ tickListeners.remove(listener);
+ }
+
+ /**
+ * Adds a CollisionListener that will be informed about collision events
+ * @param listener the CollisionListener to add
+ */
+ public void addCollisionListener(PhysicsCollisionListener listener) {
+ collisionListeners.add(listener);
+ }
+
+ /**
+ * Removes a CollisionListener from the list
+ * @param listener the CollisionListener to remove
+ */
+ public void removeCollisionListener(PhysicsCollisionListener listener) {
+ collisionListeners.remove(listener);
+ }
+
+ /**
+ * Adds a listener for a specific collision group, such a listener can disable collisions when they happen.<br>
+ * There can be only one listener per collision group.
+ * @param listener
+ * @param collisionGroup
+ */
+ public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) {
+ collisionGroupListeners.put(collisionGroup, listener);
+ }
+
+ public void removeCollisionGroupListener(int collisionGroup) {
+ collisionGroupListeners.remove(collisionGroup);
+ }
+
+ /**
+ * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults
+ */
+ public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to) {
+ List<PhysicsRayTestResult> results = new LinkedList<PhysicsRayTestResult>();
+ dynamicsWorld.rayTest(Converter.convert(from, rayVec1), Converter.convert(to, rayVec2), new InternalRayListener(results));
+ return results;
+ }
+
+ /**
+ * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults
+ */
+ public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results) {
+ results.clear();
+ dynamicsWorld.rayTest(Converter.convert(from, rayVec1), Converter.convert(to, rayVec2), new InternalRayListener(results));
+ return results;
+ }
+
+ private class InternalRayListener extends CollisionWorld.RayResultCallback {
+
+ private List<PhysicsRayTestResult> results;
+
+ public InternalRayListener(List<PhysicsRayTestResult> results) {
+ this.results = results;
+ }
+
+ @Override
+ public float addSingleResult(LocalRayResult lrr, boolean bln) {
+ PhysicsCollisionObject obj = (PhysicsCollisionObject) lrr.collisionObject.getUserPointer();
+ results.add(new PhysicsRayTestResult(obj, Converter.convert(lrr.hitNormalLocal), lrr.hitFraction, bln));
+ return lrr.hitFraction;
+ }
+ }
+
+ /**
+ * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/>
+ * You have to use different Transforms for start and end (at least distance > 0.4f).
+ * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center.
+ */
+ public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) {
+ List<PhysicsSweepTestResult> results = new LinkedList<PhysicsSweepTestResult>();
+ if (!(shape.getCShape() instanceof ConvexShape)) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!");
+ return results;
+ }
+ dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
+ return results;
+
+ }
+
+ /**
+ * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/>
+ * You have to use different Transforms for start and end (at least distance > 0.4f).
+ * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center.
+ */
+ public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) {
+ results.clear();
+ if (!(shape.getCShape() instanceof ConvexShape)) {
+ Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!");
+ return results;
+ }
+ dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
+ return results;
+ }
+
+ private class InternalSweepListener extends CollisionWorld.ConvexResultCallback {
+
+ private List<PhysicsSweepTestResult> results;
+
+ public InternalSweepListener(List<PhysicsSweepTestResult> results) {
+ this.results = results;
+ }
+
+ @Override
+ public float addSingleResult(LocalConvexResult lcr, boolean bln) {
+ PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer();
+ results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln));
+ return lcr.hitFraction;
+ }
+ }
+
+ /**
+ * destroys the current PhysicsSpace so that a new one can be created
+ */
+ public void destroy() {
+ physicsNodes.clear();
+ physicsJoints.clear();
+
+ dynamicsWorld.destroy();
+ dynamicsWorld = null;
+ }
+
+ /**
+ * used internally
+ * @return the dynamicsWorld
+ */
+ public DynamicsWorld getDynamicsWorld() {
+ return dynamicsWorld;
+ }
+
+ public BroadphaseType getBroadphaseType() {
+ return broadphaseType;
+ }
+
+ public void setBroadphaseType(BroadphaseType broadphaseType) {
+ this.broadphaseType = broadphaseType;
+ }
+
+ /**
+ * Sets the maximum amount of extra steps that will be used to step the physics
+ * when the fps is below the physics fps. Doing this maintains determinism in physics.
+ * For example a maximum number of 2 can compensate for framerates as low as 30fps
+ * when the physics has the default accuracy of 60 fps. Note that setting this
+ * value too high can make the physics drive down its own fps in case its overloaded.
+ * @param steps The maximum number of extra steps, default is 4.
+ */
+ public void setMaxSubSteps(int steps) {
+ maxSubSteps = steps;
+ }
+
+ /**
+ * get the current accuracy of the physics computation
+ * @return the current accuracy
+ */
+ public float getAccuracy() {
+ return accuracy;
+ }
+
+ /**
+ * sets the accuracy of the physics computation, default=1/60s<br>
+ * @param accuracy
+ */
+ public void setAccuracy(float accuracy) {
+ this.accuracy = accuracy;
+ }
+
+ public Vector3f getWorldMin() {
+ return worldMin;
+ }
+
+ /**
+ * only applies for AXIS_SWEEP broadphase
+ * @param worldMin
+ */
+ public void setWorldMin(Vector3f worldMin) {
+ this.worldMin.set(worldMin);
+ }
+
+ public Vector3f getWorldMax() {
+ return worldMax;
+ }
+
+ /**
+ * only applies for AXIS_SWEEP broadphase
+ * @param worldMax
+ */
+ public void setWorldMax(Vector3f worldMax) {
+ this.worldMax.set(worldMax);
+ }
+
+ /**
+ * Enable debug display for physics
+ * @param manager AssetManager to use to create debug materials
+ */
+ public void enableDebug(AssetManager manager) {
+ debugManager = manager;
+ }
+
+ /**
+ * Disable debug display
+ */
+ public void disableDebug() {
+ debugManager = null;
+ }
+
+ public AssetManager getDebugManager() {
+ return debugManager;
+ }
+
+ /**
+ * interface with Broadphase types
+ */
+ public enum BroadphaseType {
+
+ /**
+ * basic Broadphase
+ */
+ SIMPLE,
+ /**
+ * better Broadphase, needs worldBounds , max Object number = 16384
+ */
+ AXIS_SWEEP_3,
+ /**
+ * better Broadphase, needs worldBounds , max Object number = 65536
+ */
+ AXIS_SWEEP_3_32,
+ /**
+ * Broadphase allowing quicker adding/removing of physics objects
+ */
+ DBVT;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java
new file mode 100644
index 0000000..4dc768a
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.bulletphysics.collision.narrowphase.ManifoldPoint;
+import com.jme3.bullet.util.Converter;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.util.EventObject;
+
+/**
+ * A CollisionEvent stores all information about a collision in the PhysicsWorld.
+ * Do not store this Object, as it will be reused after the collision() method has been called.
+ * Get/reference all data you need in the collide method.
+ * @author normenhansen
+ */
+public class PhysicsCollisionEvent extends EventObject {
+
+ public static final int TYPE_ADDED = 0;
+ public static final int TYPE_PROCESSED = 1;
+ public static final int TYPE_DESTROYED = 2;
+ private int type;
+ private PhysicsCollisionObject nodeA;
+ private PhysicsCollisionObject nodeB;
+ private ManifoldPoint cp;
+
+ public PhysicsCollisionEvent(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, ManifoldPoint cp) {
+ super(source);
+ this.type = type;
+ this.nodeA = source;
+ this.nodeB = nodeB;
+ this.cp = cp;
+ }
+
+ /**
+ * used by event factory, called when event is destroyed
+ */
+ public void clean() {
+ source = null;
+ type = 0;
+ nodeA = null;
+ nodeB = null;
+ cp = null;
+ }
+
+ /**
+ * used by event factory, called when event reused
+ */
+ public void refactor(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, ManifoldPoint cp) {
+ this.source = source;
+ this.type = type;
+ this.nodeA = source;
+ this.nodeB = nodeB;
+ this.cp = cp;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial
+ */
+ public Spatial getNodeA() {
+ if (nodeA.getUserObject() instanceof Spatial) {
+ return (Spatial) nodeA.getUserObject();
+ }
+ return null;
+ }
+
+ /**
+ * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial
+ */
+ public Spatial getNodeB() {
+ if (nodeB.getUserObject() instanceof Spatial) {
+ return (Spatial) nodeB.getUserObject();
+ }
+ return null;
+ }
+
+ public PhysicsCollisionObject getObjectA() {
+ return nodeA;
+ }
+
+ public PhysicsCollisionObject getObjectB() {
+ return nodeB;
+ }
+
+ public float getAppliedImpulse() {
+ return cp.appliedImpulse;
+ }
+
+ public float getAppliedImpulseLateral1() {
+ return cp.appliedImpulseLateral1;
+ }
+
+ public float getAppliedImpulseLateral2() {
+ return cp.appliedImpulseLateral2;
+ }
+
+ public float getCombinedFriction() {
+ return cp.combinedFriction;
+ }
+
+ public float getCombinedRestitution() {
+ return cp.combinedRestitution;
+ }
+
+ public float getDistance1() {
+ return cp.distance1;
+ }
+
+ public int getIndex0() {
+ return cp.index0;
+ }
+
+ public int getIndex1() {
+ return cp.index1;
+ }
+
+ public Vector3f getLateralFrictionDir1() {
+ return Converter.convert(cp.lateralFrictionDir1);
+ }
+
+ public Vector3f getLateralFrictionDir2() {
+ return Converter.convert(cp.lateralFrictionDir2);
+ }
+
+ public boolean isLateralFrictionInitialized() {
+ return cp.lateralFrictionInitialized;
+ }
+
+ public int getLifeTime() {
+ return cp.lifeTime;
+ }
+
+ public Vector3f getLocalPointA() {
+ return Converter.convert(cp.localPointA);
+ }
+
+ public Vector3f getLocalPointB() {
+ return Converter.convert(cp.localPointB);
+ }
+
+ public Vector3f getNormalWorldOnB() {
+ return Converter.convert(cp.normalWorldOnB);
+ }
+
+ public int getPartId0() {
+ return cp.partId0;
+ }
+
+ public int getPartId1() {
+ return cp.partId1;
+ }
+
+ public Vector3f getPositionWorldOnA() {
+ return Converter.convert(cp.positionWorldOnA);
+ }
+
+ public Vector3f getPositionWorldOnB() {
+ return Converter.convert(cp.positionWorldOnB);
+ }
+
+ public Object getUserPersistentData() {
+ return cp.userPersistentData;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java
new file mode 100644
index 0000000..cf8d8ae
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.bulletphysics.collision.narrowphase.ManifoldPoint;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class PhysicsCollisionEventFactory {
+
+ private ConcurrentLinkedQueue<PhysicsCollisionEvent> eventBuffer = new ConcurrentLinkedQueue<PhysicsCollisionEvent>();
+
+ public PhysicsCollisionEvent getEvent(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, ManifoldPoint cp) {
+ PhysicsCollisionEvent event = eventBuffer.poll();
+ if (event == null) {
+ event = new PhysicsCollisionEvent(type, source, nodeB, cp);
+ }else{
+ event.refactor(type, source, nodeB, cp);
+ }
+ return event;
+ }
+
+ public void recycle(PhysicsCollisionEvent event) {
+ event.clean();
+ eventBuffer.add(event);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java
new file mode 100644
index 0000000..0054cf0
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.util.DebugShapeFactory;
+import com.jme3.export.*;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Base class for collision objects (PhysicsRigidBody, PhysicsGhostObject)
+ * @author normenhansen
+ */
+public abstract class PhysicsCollisionObject implements Savable {
+
+ protected Spatial debugShape;
+ protected Arrow debugArrow;
+ protected Geometry debugArrowGeom;
+ protected Material debugMaterialBlue;
+ protected Material debugMaterialRed;
+ protected Material debugMaterialGreen;
+ protected Material debugMaterialYellow;
+ protected CollisionShape collisionShape;
+ public static final int COLLISION_GROUP_NONE = 0x00000000;
+ public static final int COLLISION_GROUP_01 = 0x00000001;
+ public static final int COLLISION_GROUP_02 = 0x00000002;
+ public static final int COLLISION_GROUP_03 = 0x00000004;
+ public static final int COLLISION_GROUP_04 = 0x00000008;
+ public static final int COLLISION_GROUP_05 = 0x00000010;
+ public static final int COLLISION_GROUP_06 = 0x00000020;
+ public static final int COLLISION_GROUP_07 = 0x00000040;
+ public static final int COLLISION_GROUP_08 = 0x00000080;
+ public static final int COLLISION_GROUP_09 = 0x00000100;
+ public static final int COLLISION_GROUP_10 = 0x00000200;
+ public static final int COLLISION_GROUP_11 = 0x00000400;
+ public static final int COLLISION_GROUP_12 = 0x00000800;
+ public static final int COLLISION_GROUP_13 = 0x00001000;
+ public static final int COLLISION_GROUP_14 = 0x00002000;
+ public static final int COLLISION_GROUP_15 = 0x00004000;
+ public static final int COLLISION_GROUP_16 = 0x00008000;
+ protected int collisionGroup = 0x00000001;
+ protected int collisionGroupsMask = 0x00000001;
+ private Object userObject;
+
+ /**
+ * Sets a CollisionShape to this physics object, note that the object should
+ * not be in the physics space when adding a new collision shape as it is rebuilt
+ * on the physics side.
+ * @param collisionShape the CollisionShape to set
+ */
+ public void setCollisionShape(CollisionShape collisionShape) {
+ this.collisionShape = collisionShape;
+ updateDebugShape();
+ }
+
+ /**
+ * @return the CollisionShape of this PhysicsNode, to be able to reuse it with
+ * other physics nodes (increases performance)
+ */
+ public CollisionShape getCollisionShape() {
+ return collisionShape;
+ }
+
+ /**
+ * Returns the collision group for this collision shape
+ * @return
+ */
+ public int getCollisionGroup() {
+ return collisionGroup;
+ }
+
+ /**
+ * Sets the collision group number for this physics object. <br>
+ * The groups are integer bit masks and some pre-made variables are available in CollisionObject.
+ * All physics objects are by default in COLLISION_GROUP_01.<br>
+ * Two object will collide when <b>one</b> of the partys has the
+ * collisionGroup of the other in its collideWithGroups set.
+ * @param collisionGroup the collisionGroup to set
+ */
+ public void setCollisionGroup(int collisionGroup) {
+ this.collisionGroup = collisionGroup;
+ }
+
+ /**
+ * Add a group that this object will collide with.<br>
+ * Two object will collide when <b>one</b> of the partys has the
+ * collisionGroup of the other in its collideWithGroups set.<br>
+ * @param collisionGroup
+ */
+ public void addCollideWithGroup(int collisionGroup) {
+ this.collisionGroupsMask = this.collisionGroupsMask | collisionGroup;
+ }
+
+ /**
+ * Remove a group from the list this object collides with.
+ * @param collisionGroup
+ */
+ public void removeCollideWithGroup(int collisionGroup) {
+ this.collisionGroupsMask = this.collisionGroupsMask & ~collisionGroup;
+ }
+
+ /**
+ * Directly set the bitmask for collision groups that this object collides with.
+ * @param collisionGroups
+ */
+ public void setCollideWithGroups(int collisionGroups) {
+ this.collisionGroupsMask = collisionGroups;
+ }
+
+ /**
+ * Gets the bitmask of collision groups that this object collides with.
+ * @return
+ */
+ public int getCollideWithGroups() {
+ return collisionGroupsMask;
+ }
+
+ /**
+ * Creates a visual debug shape of the current collision shape of this physics object<br/>
+ * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b>
+ * @param manager AssetManager to load the default wireframe material for the debug shape
+ */
+ protected Spatial attachDebugShape(AssetManager manager) {
+ debugMaterialBlue = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialBlue.getAdditionalRenderState().setWireframe(true);
+ debugMaterialBlue.setColor("Color", ColorRGBA.Blue);
+ debugMaterialGreen = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialGreen.getAdditionalRenderState().setWireframe(true);
+ debugMaterialGreen.setColor("Color", ColorRGBA.Green);
+ debugMaterialRed = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialRed.getAdditionalRenderState().setWireframe(true);
+ debugMaterialRed.setColor("Color", ColorRGBA.Red);
+ debugMaterialYellow = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ debugMaterialYellow.getAdditionalRenderState().setWireframe(true);
+ debugMaterialYellow.setColor("Color", ColorRGBA.Yellow);
+ debugArrow = new Arrow(Vector3f.UNIT_XYZ);
+ debugArrowGeom = new Geometry("DebugArrow", debugArrow);
+ debugArrowGeom.setMaterial(debugMaterialGreen);
+ return attachDebugShape();
+ }
+
+ /**
+ * creates a debug shape for this CollisionObject
+ * @param manager
+ * @return
+ */
+ public Spatial createDebugShape(AssetManager manager){
+ return attachDebugShape(manager);
+ }
+
+ protected Spatial attachDebugShape(Material material) {
+ debugMaterialBlue = material;
+ debugMaterialGreen = material;
+ debugMaterialRed = material;
+ debugMaterialYellow = material;
+ debugArrow = new Arrow(Vector3f.UNIT_XYZ);
+ debugArrowGeom = new Geometry("DebugArrow", debugArrow);
+ debugArrowGeom.setMaterial(debugMaterialGreen);
+ return attachDebugShape();
+ }
+
+ public Spatial debugShape() {
+ return debugShape;
+ }
+
+ /**
+ * Creates a visual debug shape of the current collision shape of this physics object<br/>
+ * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b>
+ * @param material Material to use for the debug shape
+ */
+ protected Spatial attachDebugShape() {
+ if (debugShape != null) {
+ detachDebugShape();
+ }
+ Spatial spatial = getDebugShape();
+ this.debugShape = spatial;
+ return debugShape;
+ }
+
+ protected void updateDebugShape() {
+ if (debugShape != null) {
+ detachDebugShape();
+ attachDebugShape();
+ }
+ }
+
+ protected Spatial getDebugShape() {
+ Spatial spatial = DebugShapeFactory.getDebugShape(collisionShape);
+ if (spatial == null) {
+ return new Node("nullnode");
+ }
+ if (spatial instanceof Node) {
+ List<Spatial> children = ((Node) spatial).getChildren();
+ for (Iterator<Spatial> it1 = children.iterator(); it1.hasNext();) {
+ Spatial spatial1 = it1.next();
+ Geometry geom = ((Geometry) spatial1);
+ geom.setMaterial(debugMaterialBlue);
+ geom.setCullHint(Spatial.CullHint.Never);
+ }
+ } else {
+ Geometry geom = ((Geometry) spatial);
+ geom.setMaterial(debugMaterialBlue);
+ geom.setCullHint(Spatial.CullHint.Never);
+ }
+ spatial.setCullHint(Spatial.CullHint.Never);
+ return spatial;
+ }
+
+ /**
+ * Removes the debug shape
+ */
+ public void detachDebugShape() {
+ debugShape = null;
+ }
+
+ /**
+ * @return the userObject
+ */
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ /**
+ * @param userObject the userObject to set
+ */
+ public void setUserObject(Object userObject) {
+ this.userObject = userObject;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(collisionGroup, "collisionGroup", 0x00000001);
+ capsule.write(collisionGroupsMask, "collisionGroupsMask", 0x00000001);
+ capsule.write(debugShape, "debugShape", null);
+ capsule.write(collisionShape, "collisionShape", null);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ collisionGroup = capsule.readInt("collisionGroup", 0x00000001);
+ collisionGroupsMask = capsule.readInt("collisionGroupsMask", 0x00000001);
+ debugShape = (Spatial) capsule.readSavable("debugShape", null);
+ CollisionShape shape = (CollisionShape) capsule.readSavable("collisionShape", null);
+ collisionShape = shape;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java
new file mode 100644
index 0000000..1941344
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.math.Vector3f;
+
+/**
+ * Contains the results of a PhysicsSpace rayTest
+ * @author normenhansen
+ */
+public class PhysicsRayTestResult {
+
+ private PhysicsCollisionObject collisionObject;
+ private Vector3f hitNormalLocal;
+ private float hitFraction;
+ private boolean normalInWorldSpace;
+
+ public PhysicsRayTestResult() {
+ }
+
+ public PhysicsRayTestResult(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) {
+ this.collisionObject = collisionObject;
+ this.hitNormalLocal = hitNormalLocal;
+ this.hitFraction = hitFraction;
+ this.normalInWorldSpace = normalInWorldSpace;
+ }
+
+ /**
+ * @return the collisionObject
+ */
+ public PhysicsCollisionObject getCollisionObject() {
+ return collisionObject;
+ }
+
+ /**
+ * @return the hitNormalLocal
+ */
+ public Vector3f getHitNormalLocal() {
+ return hitNormalLocal;
+ }
+
+ /**
+ * @return the hitFraction
+ */
+ public float getHitFraction() {
+ return hitFraction;
+ }
+
+ /**
+ * @return the normalInWorldSpace
+ */
+ public boolean isNormalInWorldSpace() {
+ return normalInWorldSpace;
+ }
+
+ public void fill(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) {
+ this.collisionObject = collisionObject;
+ this.hitNormalLocal = hitNormalLocal;
+ this.hitFraction = hitFraction;
+ this.normalInWorldSpace = normalInWorldSpace;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java
new file mode 100644
index 0000000..d513204
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision;
+
+import com.jme3.math.Vector3f;
+
+/**
+ * Contains the results of a PhysicsSpace rayTest
+ * @author normenhansen
+ */
+public class PhysicsSweepTestResult {
+
+ private PhysicsCollisionObject collisionObject;
+ private Vector3f hitNormalLocal;
+ private float hitFraction;
+ private boolean normalInWorldSpace;
+
+ public PhysicsSweepTestResult() {
+ }
+
+ public PhysicsSweepTestResult(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) {
+ this.collisionObject = collisionObject;
+ this.hitNormalLocal = hitNormalLocal;
+ this.hitFraction = hitFraction;
+ this.normalInWorldSpace = normalInWorldSpace;
+ }
+
+ /**
+ * @return the collisionObject
+ */
+ public PhysicsCollisionObject getCollisionObject() {
+ return collisionObject;
+ }
+
+ /**
+ * @return the hitNormalLocal
+ */
+ public Vector3f getHitNormalLocal() {
+ return hitNormalLocal;
+ }
+
+ /**
+ * @return the hitFraction
+ */
+ public float getHitFraction() {
+ return hitFraction;
+ }
+
+ /**
+ * @return the normalInWorldSpace
+ */
+ public boolean isNormalInWorldSpace() {
+ return normalInWorldSpace;
+ }
+
+ public void fill(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) {
+ this.collisionObject = collisionObject;
+ this.hitNormalLocal = hitNormalLocal;
+ this.hitFraction = hitFraction;
+ this.normalInWorldSpace = normalInWorldSpace;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java
new file mode 100644
index 0000000..ea3f605
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.BoxShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * Basic box collision shape
+ * @author normenhansen
+ */
+public class BoxCollisionShape extends CollisionShape {
+
+ private Vector3f halfExtents;
+
+ public BoxCollisionShape() {
+ }
+
+ /**
+ * creates a collision box from the given halfExtents
+ * @param halfExtents the halfExtents of the CollisionBox
+ */
+ public BoxCollisionShape(Vector3f halfExtents) {
+ this.halfExtents = halfExtents;
+ createShape();
+ }
+
+ public final Vector3f getHalfExtents() {
+ return halfExtents;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(halfExtents, "halfExtents", new Vector3f(1, 1, 1));
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ Vector3f halfExtents = (Vector3f) capsule.readSavable("halfExtents", new Vector3f(1, 1, 1));
+ this.halfExtents = halfExtents;
+ createShape();
+ }
+
+ protected void createShape() {
+ cShape = new BoxShape(Converter.convert(halfExtents));
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java
new file mode 100644
index 0000000..61024eb
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.CapsuleShape;
+import com.bulletphysics.collision.shapes.CapsuleShapeX;
+import com.bulletphysics.collision.shapes.CapsuleShapeZ;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * Basic capsule collision shape
+ * @author normenhansen
+ */
+public class CapsuleCollisionShape extends CollisionShape{
+ protected float radius,height;
+ protected int axis;
+
+ public CapsuleCollisionShape() {
+ }
+
+ /**
+ * creates a new CapsuleCollisionShape with the given radius and height
+ * @param radius the radius of the capsule
+ * @param height the height of the capsule
+ */
+ public CapsuleCollisionShape(float radius, float height) {
+ this.radius=radius;
+ this.height=height;
+ this.axis=1;
+ CapsuleShape capShape=new CapsuleShape(radius,height);
+ cShape=capShape;
+ }
+
+ /**
+ * creates a capsule shape around the given axis (0=X,1=Y,2=Z)
+ * @param radius
+ * @param height
+ * @param axis
+ */
+ public CapsuleCollisionShape(float radius, float height, int axis) {
+ this.radius=radius;
+ this.height=height;
+ this.axis=axis;
+ createShape();
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public int getAxis() {
+ return axis;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(radius, "radius", 0.5f);
+ capsule.write(height, "height", 1);
+ capsule.write(axis, "axis", 1);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ radius = capsule.readFloat("radius", 0.5f);
+ height = capsule.readFloat("height", 0.5f);
+ axis = capsule.readInt("axis", 1);
+ createShape();
+ }
+
+ protected void createShape(){
+ switch(axis){
+ case 0:
+ cShape=new CapsuleShapeX(radius,height);
+ break;
+ case 1:
+ cShape=new CapsuleShape(radius,height);
+ break;
+ case 2:
+ cShape=new CapsuleShapeZ(radius,height);
+ break;
+ }
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java
new file mode 100644
index 0000000..3a36a75
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.*;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * This Object holds information about a jbullet CollisionShape to be able to reuse
+ * CollisionShapes (as suggested in bullet manuals)
+ * TODO: add static methods to create shapes from nodes (like jbullet-jme constructor)
+ * @author normenhansen
+ */
+public abstract class CollisionShape implements Savable {
+
+ protected com.bulletphysics.collision.shapes.CollisionShape cShape;
+ protected Vector3f scale = new Vector3f(1, 1, 1);
+ protected float margin = 0.0f;
+
+ public CollisionShape() {
+ }
+
+ /**
+ * used internally, not safe
+ */
+ public void calculateLocalInertia(float mass, javax.vecmath.Vector3f vector) {
+ if (cShape == null) {
+ return;
+ }
+ if (this instanceof MeshCollisionShape) {
+ vector.set(0, 0, 0);
+ } else {
+ cShape.calculateLocalInertia(mass, vector);
+ }
+ }
+
+ /**
+ * used internally
+ */
+ public com.bulletphysics.collision.shapes.CollisionShape getCShape() {
+ return cShape;
+ }
+
+ /**
+ * used internally
+ */
+ public void setCShape(com.bulletphysics.collision.shapes.CollisionShape cShape) {
+ this.cShape = cShape;
+ }
+
+ public void setScale(Vector3f scale) {
+ this.scale.set(scale);
+ cShape.setLocalScaling(Converter.convert(scale));
+ }
+
+ public float getMargin() {
+ return cShape.getMargin();
+ }
+
+ public void setMargin(float margin) {
+ cShape.setMargin(margin);
+ this.margin = margin;
+ }
+
+ public Vector3f getScale() {
+ return scale;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(scale, "scale", new Vector3f(1, 1, 1));
+ capsule.write(getMargin(), "margin", 0.0f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ this.scale = (Vector3f) capsule.readSavable("scale", new Vector3f(1, 1, 1));
+ this.margin = capsule.readFloat("margin", 0.0f);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java
new file mode 100644
index 0000000..1beb066
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.CompoundShape;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A CompoundCollisionShape allows combining multiple base shapes
+ * to generate a more sophisticated shape.
+ * @author normenhansen
+ */
+public class CompoundCollisionShape extends CollisionShape {
+
+ protected ArrayList<ChildCollisionShape> children = new ArrayList<ChildCollisionShape>();
+
+ public CompoundCollisionShape() {
+ cShape = new CompoundShape();
+ }
+
+ /**
+ * adds a child shape at the given local translation
+ * @param shape the child shape to add
+ * @param location the local location of the child shape
+ */
+ public void addChildShape(CollisionShape shape, Vector3f location) {
+ Transform transA = new Transform(Converter.convert(new Matrix3f()));
+ Converter.convert(location, transA.origin);
+ children.add(new ChildCollisionShape(location.clone(), new Matrix3f(), shape));
+ ((CompoundShape) cShape).addChildShape(transA, shape.getCShape());
+ }
+
+ /**
+ * adds a child shape at the given local translation
+ * @param shape the child shape to add
+ * @param location the local location of the child shape
+ */
+ public void addChildShape(CollisionShape shape, Vector3f location, Matrix3f rotation) {
+ if(shape instanceof CompoundCollisionShape){
+ throw new IllegalStateException("CompoundCollisionShapes cannot have CompoundCollisionShapes as children!");
+ }
+ Transform transA = new Transform(Converter.convert(rotation));
+ Converter.convert(location, transA.origin);
+ Converter.convert(rotation, transA.basis);
+ children.add(new ChildCollisionShape(location.clone(), rotation.clone(), shape));
+ ((CompoundShape) cShape).addChildShape(transA, shape.getCShape());
+ }
+
+ private void addChildShapeDirect(CollisionShape shape, Vector3f location, Matrix3f rotation) {
+ if(shape instanceof CompoundCollisionShape){
+ throw new IllegalStateException("CompoundCollisionShapes cannot have CompoundCollisionShapes as children!");
+ }
+ Transform transA = new Transform(Converter.convert(rotation));
+ Converter.convert(location, transA.origin);
+ Converter.convert(rotation, transA.basis);
+ ((CompoundShape) cShape).addChildShape(transA, shape.getCShape());
+ }
+
+ /**
+ * removes a child shape
+ * @param shape the child shape to remove
+ */
+ public void removeChildShape(CollisionShape shape) {
+ ((CompoundShape) cShape).removeChildShape(shape.getCShape());
+ for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
+ ChildCollisionShape childCollisionShape = it.next();
+ if (childCollisionShape.shape == shape) {
+ it.remove();
+ }
+ }
+ }
+
+ public List<ChildCollisionShape> getChildren() {
+ return children;
+ }
+
+ /**
+ * WARNING - CompoundCollisionShape scaling has no effect.
+ */
+ @Override
+ public void setScale(Vector3f scale) {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "CompoundCollisionShape cannot be scaled");
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.writeSavableArrayList(children, "children", new ArrayList<ChildCollisionShape>());
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ children = capsule.readSavableArrayList("children", new ArrayList<ChildCollisionShape>());
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ loadChildren();
+ }
+
+ private void loadChildren() {
+ for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
+ ChildCollisionShape child = it.next();
+ addChildShapeDirect(child.shape, child.location, child.rotation);
+ }
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java
new file mode 100644
index 0000000..7103f0d
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java
@@ -0,0 +1,77 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.ConeShape;
+import com.bulletphysics.collision.shapes.ConeShapeX;
+import com.bulletphysics.collision.shapes.ConeShapeZ;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class ConeCollisionShape extends CollisionShape {
+
+ protected float radius;
+ protected float height;
+ protected int axis;
+
+ public ConeCollisionShape() {
+ }
+
+ public ConeCollisionShape(float radius, float height, int axis) {
+ this.radius = radius;
+ this.height = radius;
+ this.axis = axis;
+ createShape();
+ }
+
+ public ConeCollisionShape(float radius, float height) {
+ this.radius = radius;
+ this.height = radius;
+ this.axis = PhysicsSpace.AXIS_Y;
+ createShape();
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(radius, "radius", 0.5f);
+ capsule.write(height, "height", 0.5f);
+ capsule.write(axis, "axis", 0.5f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ radius = capsule.readFloat("radius", 0.5f);
+ radius = capsule.readFloat("height", 0.5f);
+ radius = capsule.readFloat("axis", 0.5f);
+ createShape();
+ }
+
+ protected void createShape() {
+ if (axis == PhysicsSpace.AXIS_X) {
+ cShape = new ConeShapeX(radius, height);
+ } else if (axis == PhysicsSpace.AXIS_Y) {
+ cShape = new ConeShape(radius, height);
+ } else if (axis == PhysicsSpace.AXIS_Z) {
+ cShape = new ConeShapeZ(radius, height);
+ }
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java
new file mode 100644
index 0000000..866a443
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.CylinderShape;
+import com.bulletphysics.collision.shapes.CylinderShapeX;
+import com.bulletphysics.collision.shapes.CylinderShapeZ;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * Basic cylinder collision shape
+ * @author normenhansen
+ */
+public class CylinderCollisionShape extends CollisionShape {
+
+ protected Vector3f halfExtents;
+ protected int axis;
+
+ public CylinderCollisionShape() {
+ }
+
+ /**
+ * creates a cylinder shape from the given halfextents
+ * @param halfExtents the halfextents to use
+ */
+ public CylinderCollisionShape(Vector3f halfExtents) {
+ this.halfExtents = halfExtents;
+ this.axis = 2;
+ createShape();
+ }
+
+ /**
+ * Creates a cylinder shape around the given axis from the given halfextents
+ * @param halfExtents the halfextents to use
+ * @param axis (0=X,1=Y,2=Z)
+ */
+ public CylinderCollisionShape(Vector3f halfExtents, int axis) {
+ this.halfExtents = halfExtents;
+ this.axis = axis;
+ createShape();
+ }
+
+ public final Vector3f getHalfExtents() {
+ return halfExtents;
+ }
+
+ public int getAxis() {
+ return axis;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(halfExtents, "halfExtents", new Vector3f(0.5f, 0.5f, 0.5f));
+ capsule.write(axis, "axis", 1);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ halfExtents = (Vector3f) capsule.readSavable("halfExtents", new Vector3f(0.5f, 0.5f, 0.5f));
+ axis = capsule.readInt("axis", 1);
+ createShape();
+ }
+
+ protected void createShape() {
+ switch (axis) {
+ case 0:
+ cShape = new CylinderShapeX(Converter.convert(halfExtents));
+ break;
+ case 1:
+ cShape = new CylinderShape(Converter.convert(halfExtents));
+ break;
+ case 2:
+ cShape = new CylinderShapeZ(Converter.convert(halfExtents));
+ break;
+ }
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java
new file mode 100644
index 0000000..6d12420
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.IndexedMesh;
+import com.bulletphysics.collision.shapes.TriangleIndexVertexArray;
+import com.bulletphysics.extras.gimpact.GImpactMeshShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Basic mesh collision shape
+ * @author normenhansen
+ */
+public class GImpactCollisionShape extends CollisionShape{
+
+ protected Vector3f worldScale;
+ protected int numVertices, numTriangles, vertexStride, triangleIndexStride;
+ protected ByteBuffer triangleIndexBase, vertexBase;
+ protected IndexedMesh bulletMesh;
+
+ public GImpactCollisionShape() {
+ }
+
+ /**
+ * creates a collision shape from the given Mesh
+ * @param mesh the Mesh to use
+ */
+ public GImpactCollisionShape(Mesh mesh) {
+ createCollisionMesh(mesh, new Vector3f(1,1,1));
+ }
+
+
+ private void createCollisionMesh(Mesh mesh, Vector3f worldScale) {
+ this.worldScale = worldScale;
+ bulletMesh = Converter.convert(mesh);
+ this.numVertices = bulletMesh.numVertices;
+ this.numTriangles = bulletMesh.numTriangles;
+ this.vertexStride = bulletMesh.vertexStride;
+ this.triangleIndexStride = bulletMesh.triangleIndexStride;
+ this.triangleIndexBase = bulletMesh.triangleIndexBase;
+ this.vertexBase = bulletMesh.vertexBase;
+ createShape();
+ }
+
+ /**
+ * creates a jme mesh from the collision shape, only needed for debugging
+ */
+ public Mesh createJmeMesh(){
+ return Converter.convert(bulletMesh);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(worldScale, "worldScale", new Vector3f(1, 1, 1));
+ capsule.write(numVertices, "numVertices", 0);
+ capsule.write(numTriangles, "numTriangles", 0);
+ capsule.write(vertexStride, "vertexStride", 0);
+ capsule.write(triangleIndexStride, "triangleIndexStride", 0);
+
+ capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]);
+ capsule.write(vertexBase.array(), "vertexBase", new byte[0]);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ worldScale = (Vector3f) capsule.readSavable("worldScale", new Vector3f(1, 1, 1));
+ numVertices = capsule.readInt("numVertices", 0);
+ numTriangles = capsule.readInt("numTriangles", 0);
+ vertexStride = capsule.readInt("vertexStride", 0);
+ triangleIndexStride = capsule.readInt("triangleIndexStride", 0);
+
+ triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0]));
+ vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0]));
+ createShape();
+ }
+
+ protected void createShape() {
+ bulletMesh = new IndexedMesh();
+ bulletMesh.numVertices = numVertices;
+ bulletMesh.numTriangles = numTriangles;
+ bulletMesh.vertexStride = vertexStride;
+ bulletMesh.triangleIndexStride = triangleIndexStride;
+ bulletMesh.triangleIndexBase = triangleIndexBase;
+ bulletMesh.vertexBase = vertexBase;
+ bulletMesh.triangleIndexBase = triangleIndexBase;
+ TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride);
+ cShape = new GImpactMeshShape(tiv);
+ cShape.setLocalScaling(Converter.convert(worldScale));
+ ((GImpactMeshShape)cShape).updateBound();
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java
new file mode 100644
index 0000000..7f0611e
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java
@@ -0,0 +1,133 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.dom.HeightfieldTerrainShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import java.io.IOException;
+
+/**
+ * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster
+ * than using a regular mesh.
+ * There are a couple tricks though:
+ * -No rotation or translation is supported.
+ * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being
+ * equal on either side. If not, the whole collision box is shifted vertically and things don't collide
+ * as they should.
+ *
+ * @author Brent Owens
+ */
+public class HeightfieldCollisionShape extends CollisionShape {
+
+ //protected HeightfieldTerrainShape heightfieldShape;
+ protected int heightStickWidth;
+ protected int heightStickLength;
+ protected float[] heightfieldData;
+ protected float heightScale;
+ protected float minHeight;
+ protected float maxHeight;
+ protected int upAxis;
+ protected boolean flipQuadEdges;
+
+ public HeightfieldCollisionShape() {
+
+ }
+
+ public HeightfieldCollisionShape(float[] heightmap) {
+ createCollisionHeightfield(heightmap, Vector3f.UNIT_XYZ);
+ }
+
+ public HeightfieldCollisionShape(float[] heightmap, Vector3f scale) {
+ createCollisionHeightfield(heightmap, scale);
+ }
+
+ protected void createCollisionHeightfield(float[] heightmap, Vector3f worldScale) {
+ this.scale = worldScale;
+ this.heightScale = 1;//don't change away from 1, we use worldScale instead to scale
+
+ this.heightfieldData = heightmap;
+
+ float min = heightfieldData[0];
+ float max = heightfieldData[0];
+ // calculate min and max height
+ for (int i=0; i<heightfieldData.length; i++) {
+ if (heightfieldData[i] < min)
+ min = heightfieldData[i];
+ if (heightfieldData[i] > max)
+ max = heightfieldData[i];
+ }
+ // we need to center the terrain collision box at 0,0,0 for BulletPhysics. And to do that we need to set the
+ // min and max height to be equal on either side of the y axis, otherwise it gets shifted and collision is incorrect.
+ if (max < 0)
+ max = -min;
+ else {
+ if (Math.abs(max) > Math.abs(min))
+ min = -max;
+ else
+ max = -min;
+ }
+ this.minHeight = min;
+ this.maxHeight = max;
+
+ this.upAxis = HeightfieldTerrainShape.YAXIS;
+ this.flipQuadEdges = false;
+
+ heightStickWidth = (int) FastMath.sqrt(heightfieldData.length);
+ heightStickLength = heightStickWidth;
+
+
+ createShape();
+ }
+
+ protected void createShape() {
+
+ HeightfieldTerrainShape shape = new HeightfieldTerrainShape(heightStickWidth, heightStickLength, heightfieldData, heightScale, minHeight, maxHeight, upAxis, flipQuadEdges);
+ shape.setLocalScaling(new javax.vecmath.Vector3f(scale.x, scale.y, scale.z));
+ cShape = shape;
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+ public Mesh createJmeMesh(){
+ //TODO return Converter.convert(bulletMesh);
+ return null;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(heightStickWidth, "heightStickWidth", 0);
+ capsule.write(heightStickLength, "heightStickLength", 0);
+ capsule.write(heightScale, "heightScale", 0);
+ capsule.write(minHeight, "minHeight", 0);
+ capsule.write(maxHeight, "maxHeight", 0);
+ capsule.write(upAxis, "upAxis", 1);
+ capsule.write(heightfieldData, "heightfieldData", new float[0]);
+ capsule.write(flipQuadEdges, "flipQuadEdges", false);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ heightStickWidth = capsule.readInt("heightStickWidth", 0);
+ heightStickLength = capsule.readInt("heightStickLength", 0);
+ heightScale = capsule.readFloat("heightScale", 0);
+ minHeight = capsule.readFloat("minHeight", 0);
+ maxHeight = capsule.readFloat("maxHeight", 0);
+ upAxis = capsule.readInt("upAxis", 1);
+ heightfieldData = capsule.readFloatArray("heightfieldData", new float[0]);
+ flipQuadEdges = capsule.readBoolean("flipQuadEdges", false);
+ createShape();
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java
new file mode 100644
index 0000000..1aa4a67
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java
@@ -0,0 +1,79 @@
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.ConvexHullShape;
+import com.bulletphysics.util.ObjectArrayList;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import javax.vecmath.Vector3f;
+
+public class HullCollisionShape extends CollisionShape {
+
+ private float[] points;
+
+ public HullCollisionShape() {
+ }
+
+ public HullCollisionShape(Mesh mesh) {
+ this.points = getPoints(mesh);
+ createShape(this.points);
+ }
+
+ public HullCollisionShape(float[] points) {
+ this.points = points;
+ createShape(this.points);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(points, "points", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+
+ // for backwards compatability
+ Mesh mesh = (Mesh) capsule.readSavable("hullMesh", null);
+ if (mesh != null) {
+ this.points = getPoints(mesh);
+ } else {
+ this.points = capsule.readFloatArray("points", null);
+
+ }
+ createShape(this.points);
+ }
+
+ protected void createShape(float[] points) {
+ ObjectArrayList<Vector3f> pointList = new ObjectArrayList<Vector3f>();
+ for (int i = 0; i < points.length; i += 3) {
+ pointList.add(new Vector3f(points[i], points[i + 1], points[i + 2]));
+ }
+ cShape = new ConvexHullShape(pointList);
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+ protected float[] getPoints(Mesh mesh) {
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ vertices.rewind();
+ int components = mesh.getVertexCount() * 3;
+ float[] pointsArray = new float[components];
+ for (int i = 0; i < components; i += 3) {
+ pointsArray[i] = vertices.get();
+ pointsArray[i + 1] = vertices.get();
+ pointsArray[i + 2] = vertices.get();
+ }
+ return pointsArray;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java
new file mode 100644
index 0000000..661b8dc
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.BvhTriangleMeshShape;
+import com.bulletphysics.collision.shapes.IndexedMesh;
+import com.bulletphysics.collision.shapes.TriangleIndexVertexArray;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Basic mesh collision shape
+ * @author normenhansen
+ */
+public class MeshCollisionShape extends CollisionShape {
+
+ protected int numVertices, numTriangles, vertexStride, triangleIndexStride;
+ protected ByteBuffer triangleIndexBase, vertexBase;
+ protected IndexedMesh bulletMesh;
+
+ public MeshCollisionShape() {
+ }
+
+ /**
+ * creates a collision shape from the given TriMesh
+ * @param mesh the TriMesh to use
+ */
+ public MeshCollisionShape(Mesh mesh) {
+ createCollisionMesh(mesh, new Vector3f(1, 1, 1));
+ }
+
+ private void createCollisionMesh(Mesh mesh, Vector3f worldScale) {
+ this.scale = worldScale;
+ bulletMesh = Converter.convert(mesh);
+ this.numVertices = bulletMesh.numVertices;
+ this.numTriangles = bulletMesh.numTriangles;
+ this.vertexStride = bulletMesh.vertexStride;
+ this.triangleIndexStride = bulletMesh.triangleIndexStride;
+ this.triangleIndexBase = bulletMesh.triangleIndexBase;
+ this.vertexBase = bulletMesh.vertexBase;
+ createShape();
+ }
+
+ /**
+ * creates a jme mesh from the collision shape, only needed for debugging
+ */
+ public Mesh createJmeMesh(){
+ return Converter.convert(bulletMesh);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(numVertices, "numVertices", 0);
+ capsule.write(numTriangles, "numTriangles", 0);
+ capsule.write(vertexStride, "vertexStride", 0);
+ capsule.write(triangleIndexStride, "triangleIndexStride", 0);
+
+ capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]);
+ capsule.write(vertexBase.array(), "vertexBase", new byte[0]);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ numVertices = capsule.readInt("numVertices", 0);
+ numTriangles = capsule.readInt("numTriangles", 0);
+ vertexStride = capsule.readInt("vertexStride", 0);
+ triangleIndexStride = capsule.readInt("triangleIndexStride", 0);
+
+ triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0]));
+ vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0]));
+ createShape();
+ }
+
+ protected void createShape() {
+ bulletMesh = new IndexedMesh();
+ bulletMesh.numVertices = numVertices;
+ bulletMesh.numTriangles = numTriangles;
+ bulletMesh.vertexStride = vertexStride;
+ bulletMesh.triangleIndexStride = triangleIndexStride;
+ bulletMesh.triangleIndexBase = triangleIndexBase;
+ bulletMesh.vertexBase = vertexBase;
+ bulletMesh.triangleIndexBase = triangleIndexBase;
+ TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride);
+ cShape = new BvhTriangleMeshShape(tiv, true);
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java
new file mode 100644
index 0000000..ce51f18
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java
@@ -0,0 +1,59 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.StaticPlaneShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Plane;
+import java.io.IOException;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class PlaneCollisionShape extends CollisionShape{
+ private Plane plane;
+
+ public PlaneCollisionShape() {
+ }
+
+ /**
+ * Creates a plane Collision shape
+ * @param plane the plane that defines the shape
+ */
+ public PlaneCollisionShape(Plane plane) {
+ this.plane = plane;
+ createShape();
+ }
+
+ public final Plane getPlane() {
+ return plane;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(plane, "collisionPlane", new Plane());
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ plane = (Plane) capsule.readSavable("collisionPlane", new Plane());
+ createShape();
+ }
+
+ protected void createShape() {
+ cShape = new StaticPlaneShape(Converter.convert(plane.getNormal()),plane.getConstant());
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java
new file mode 100644
index 0000000..7ddefd3
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java
@@ -0,0 +1,85 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.BU_Simplex1to4;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * A simple point, line, triangle or quad collisionShape based on one to four points-
+ * @author normenhansen
+ */
+public class SimplexCollisionShape extends CollisionShape {
+
+ private Vector3f vector1, vector2, vector3, vector4;
+
+ public SimplexCollisionShape() {
+ }
+
+ public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3, Vector3f point4) {
+ vector1 = point1;
+ vector2 = point2;
+ vector3 = point3;
+ vector4 = point4;
+ createShape();
+ }
+
+ public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3) {
+ vector1 = point1;
+ vector2 = point2;
+ vector3 = point3;
+ createShape();
+ }
+
+ public SimplexCollisionShape(Vector3f point1, Vector3f point2) {
+ vector1 = point1;
+ vector2 = point2;
+ createShape();
+ }
+
+ public SimplexCollisionShape(Vector3f point1) {
+ vector1 = point1;
+ createShape();
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(vector1, "simplexPoint1", null);
+ capsule.write(vector2, "simplexPoint2", null);
+ capsule.write(vector3, "simplexPoint3", null);
+ capsule.write(vector4, "simplexPoint4", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ vector1 = (Vector3f) capsule.readSavable("simplexPoint1", null);
+ vector2 = (Vector3f) capsule.readSavable("simplexPoint2", null);
+ vector3 = (Vector3f) capsule.readSavable("simplexPoint3", null);
+ vector4 = (Vector3f) capsule.readSavable("simplexPoint4", null);
+ createShape();
+ }
+
+ protected void createShape() {
+ if (vector4 != null) {
+ cShape = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2), Converter.convert(vector3), Converter.convert(vector4));
+ } else if (vector3 != null) {
+ cShape = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2), Converter.convert(vector3));
+ } else if (vector2 != null) {
+ cShape = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2));
+ } else {
+ cShape = new BU_Simplex1to4(Converter.convert(vector1));
+ }
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java
new file mode 100644
index 0000000..787f597
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.collision.shapes;
+
+import com.bulletphysics.collision.shapes.SphereShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * Basic sphere collision shape
+ * @author normenhansen
+ */
+public class SphereCollisionShape extends CollisionShape {
+
+ protected float radius;
+
+ public SphereCollisionShape() {
+ }
+
+ /**
+ * creates a SphereCollisionShape with the given radius
+ * @param radius
+ */
+ public SphereCollisionShape(float radius) {
+ this.radius = radius;
+ createShape();
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(radius, "radius", 0.5f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ radius = capsule.readFloat("radius", 0.5f);
+ createShape();
+ }
+
+ protected void createShape() {
+ cShape = new SphereShape(radius);
+ cShape.setLocalScaling(Converter.convert(getScale()));
+ cShape.setMargin(margin);
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java
new file mode 100644
index 0000000..2dc54f4
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.bulletphysics.dynamics.constraintsolver.ConeTwistConstraint;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * To create ragdolls, the conve twist constraint is very useful for limbs like the upper arm.
+ * It is a special point to point constraint that adds cone and twist axis limits.
+ * The x-axis serves as twist axis.
+ * @author normenhansen
+ */
+public class ConeJoint extends PhysicsJoint {
+
+ protected Matrix3f rotA, rotB;
+ protected float swingSpan1 = 1e30f;
+ protected float swingSpan2 = 1e30f;
+ protected float twistSpan = 1e30f;
+ protected boolean angularOnly = false;
+
+ public ConeJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA = new Matrix3f();
+ this.rotB = new Matrix3f();
+ createJoint();
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA = rotA;
+ this.rotB = rotB;
+ createJoint();
+ }
+
+ public void setLimit(float swingSpan1, float swingSpan2, float twistSpan) {
+ this.swingSpan1 = swingSpan1;
+ this.swingSpan2 = swingSpan2;
+ this.twistSpan = twistSpan;
+ ((ConeTwistConstraint) constraint).setLimit(swingSpan1, swingSpan2, twistSpan);
+ }
+
+ public void setAngularOnly(boolean value) {
+ angularOnly = value;
+ ((ConeTwistConstraint) constraint).setAngularOnly(value);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(rotA, "rotA", new Matrix3f());
+ capsule.write(rotB, "rotB", new Matrix3f());
+
+ capsule.write(angularOnly, "angularOnly", false);
+ capsule.write(swingSpan1, "swingSpan1", 1e30f);
+ capsule.write(swingSpan2, "swingSpan2", 1e30f);
+ capsule.write(twistSpan, "twistSpan", 1e30f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ this.rotA = (Matrix3f) capsule.readSavable("rotA", new Matrix3f());
+ this.rotB = (Matrix3f) capsule.readSavable("rotB", new Matrix3f());
+
+ this.angularOnly = capsule.readBoolean("angularOnly", false);
+ this.swingSpan1 = capsule.readFloat("swingSpan1", 1e30f);
+ this.swingSpan2 = capsule.readFloat("swingSpan2", 1e30f);
+ this.twistSpan = capsule.readFloat("twistSpan", 1e30f);
+ createJoint();
+ }
+
+ protected void createJoint() {
+ Transform transA = new Transform(Converter.convert(rotA));
+ Converter.convert(pivotA, transA.origin);
+ Converter.convert(rotA, transA.basis);
+
+ Transform transB = new Transform(Converter.convert(rotB));
+ Converter.convert(pivotB, transB.origin);
+ Converter.convert(rotB, transB.basis);
+
+ constraint = new ConeTwistConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB);
+ ((ConeTwistConstraint) constraint).setLimit(swingSpan1, swingSpan2, twistSpan);
+ ((ConeTwistConstraint) constraint).setAngularOnly(angularOnly);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java
new file mode 100644
index 0000000..47b0250
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.bulletphysics.dynamics.constraintsolver.HingeConstraint;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * Hinge constraint, or revolute joint restricts two additional angular degrees of freedom,
+ * so the body can only rotate around one axis, the hinge axis.
+ * This can be useful to represent doors or wheels rotating around one axis.
+ * The user can specify limits and motor for the hinge.
+ * @author normenhansen
+ */
+public class HingeJoint extends PhysicsJoint {
+
+ protected Vector3f axisA;
+ protected Vector3f axisB;
+ protected boolean angularOnly = false;
+ protected float biasFactor = 0.3f;
+ protected float relaxationFactor = 1.0f;
+ protected float limitSoftness = 0.9f;
+
+ public HingeJoint() {
+ }
+
+ /**
+ * Creates a new HingeJoint
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public HingeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Vector3f axisA, Vector3f axisB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.axisA = axisA;
+ this.axisB = axisB;
+ createJoint();
+ }
+
+ public void enableMotor(boolean enable, float targetVelocity, float maxMotorImpulse) {
+ ((HingeConstraint) constraint).enableAngularMotor(enable, targetVelocity, maxMotorImpulse);
+ }
+
+ public void setLimit(float low, float high) {
+ ((HingeConstraint) constraint).setLimit(low, high);
+ }
+
+ public void setLimit(float low, float high, float _softness, float _biasFactor, float _relaxationFactor) {
+ biasFactor = _biasFactor;
+ relaxationFactor = _relaxationFactor;
+ limitSoftness = _softness;
+ ((HingeConstraint) constraint).setLimit(low, high, _softness, _biasFactor, _relaxationFactor);
+ }
+
+ public float getUpperLimit(){
+ return ((HingeConstraint) constraint).getUpperLimit();
+ }
+
+ public float getLowerLimit(){
+ return ((HingeConstraint) constraint).getLowerLimit();
+ }
+
+ public void setAngularOnly(boolean angularOnly) {
+ this.angularOnly = angularOnly;
+ ((HingeConstraint) constraint).setAngularOnly(angularOnly);
+ }
+
+ public float getHingeAngle() {
+ return ((HingeConstraint) constraint).getHingeAngle();
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(axisA, "axisA", new Vector3f());
+ capsule.write(axisB, "axisB", new Vector3f());
+
+ capsule.write(angularOnly, "angularOnly", false);
+
+ capsule.write(((HingeConstraint) constraint).getLowerLimit(), "lowerLimit", 1e30f);
+ capsule.write(((HingeConstraint) constraint).getUpperLimit(), "upperLimit", -1e30f);
+
+ capsule.write(biasFactor, "biasFactor", 0.3f);
+ capsule.write(relaxationFactor, "relaxationFactor", 1f);
+ capsule.write(limitSoftness, "limitSoftness", 0.9f);
+
+ capsule.write(((HingeConstraint) constraint).getEnableAngularMotor(), "enableAngularMotor", false);
+ capsule.write(((HingeConstraint) constraint).getMotorTargetVelosity(), "targetVelocity", 0.0f);
+ capsule.write(((HingeConstraint) constraint).getMaxMotorImpulse(), "maxMotorImpulse", 0.0f);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ this.axisA = (Vector3f) capsule.readSavable("axisA", new Vector3f());
+ this.axisB = (Vector3f) capsule.readSavable("axisB", new Vector3f());
+
+ this.angularOnly = capsule.readBoolean("angularOnly", false);
+ float lowerLimit = capsule.readFloat("lowerLimit", 1e30f);
+ float upperLimit = capsule.readFloat("upperLimit", -1e30f);
+
+ this.biasFactor = capsule.readFloat("biasFactor", 0.3f);
+ this.relaxationFactor = capsule.readFloat("relaxationFactor", 1f);
+ this.limitSoftness = capsule.readFloat("limitSoftness", 0.9f);
+
+ boolean enableAngularMotor=capsule.readBoolean("enableAngularMotor", false);
+ float targetVelocity=capsule.readFloat("targetVelocity", 0.0f);
+ float maxMotorImpulse=capsule.readFloat("maxMotorImpulse", 0.0f);
+
+ createJoint();
+ enableMotor(enableAngularMotor, targetVelocity, maxMotorImpulse);
+ ((HingeConstraint) constraint).setLimit(lowerLimit, upperLimit, limitSoftness, biasFactor, relaxationFactor);
+ }
+
+ protected void createJoint() {
+ constraint = new HingeConstraint(nodeA.getObjectId(), nodeB.getObjectId(),
+ Converter.convert(pivotA), Converter.convert(pivotB),
+ Converter.convert(axisA), Converter.convert(axisB));
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java
new file mode 100644
index 0000000..533078a
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.bulletphysics.dynamics.constraintsolver.TypedConstraint;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.export.*;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * <p>PhysicsJoint - Basic Phyiscs Joint</p>
+ * @author normenhansen
+ */
+public abstract class PhysicsJoint implements Savable {
+
+ protected TypedConstraint constraint;
+ protected PhysicsRigidBody nodeA;
+ protected PhysicsRigidBody nodeB;
+ protected Vector3f pivotA;
+ protected Vector3f pivotB;
+ protected boolean collisionBetweenLinkedBodys = true;
+
+ public PhysicsJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public PhysicsJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) {
+ this.nodeA = nodeA;
+ this.nodeB = nodeB;
+ this.pivotA = pivotA;
+ this.pivotB = pivotB;
+ nodeA.addJoint(this);
+ nodeB.addJoint(this);
+ }
+
+ public float getAppliedImpulse(){
+ return constraint.getAppliedImpulse();
+ }
+
+ /**
+ * @return the constraint
+ */
+ public TypedConstraint getObjectId() {
+ return constraint;
+ }
+
+ /**
+ * @return the collisionBetweenLinkedBodys
+ */
+ public boolean isCollisionBetweenLinkedBodys() {
+ return collisionBetweenLinkedBodys;
+ }
+
+ /**
+ * toggles collisions between linked bodys<br>
+ * joint has to be removed from and added to PhyiscsSpace to apply this.
+ * @param collisionBetweenLinkedBodys set to false to have no collisions between linked bodys
+ */
+ public void setCollisionBetweenLinkedBodys(boolean collisionBetweenLinkedBodys) {
+ this.collisionBetweenLinkedBodys = collisionBetweenLinkedBodys;
+ }
+
+ public PhysicsRigidBody getBodyA() {
+ return nodeA;
+ }
+
+ public PhysicsRigidBody getBodyB() {
+ return nodeB;
+ }
+
+ public Vector3f getPivotA() {
+ return pivotA;
+ }
+
+ public Vector3f getPivotB() {
+ return pivotB;
+ }
+
+ /**
+ * destroys this joint and removes it from its connected PhysicsRigidBodys joint lists
+ */
+ public void destroy() {
+ getBodyA().removeJoint(this);
+ getBodyB().removeJoint(this);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(nodeA, "nodeA", null);
+ capsule.write(nodeB, "nodeB", null);
+ capsule.write(pivotA, "pivotA", null);
+ capsule.write(pivotB, "pivotB", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ this.nodeA = ((PhysicsRigidBody) capsule.readSavable("nodeA", new PhysicsRigidBody()));
+ this.nodeB = (PhysicsRigidBody) capsule.readSavable("nodeB", new PhysicsRigidBody());
+ this.pivotA = (Vector3f) capsule.readSavable("pivotA", new Vector3f());
+ this.pivotB = (Vector3f) capsule.readSavable("pivotB", new Vector3f());
+ }
+
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java
new file mode 100644
index 0000000..a3f768a
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.bulletphysics.dynamics.constraintsolver.Point2PointConstraint;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * Point to point constraint, also known as ball socket joint limits the translation
+ * so that the local pivot points of 2 rigidbodies match in worldspace.
+ * A chain of rigidbodies can be connected using this constraint.
+ * @author normenhansen
+ */
+public class Point2PointJoint extends PhysicsJoint {
+
+ public Point2PointJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public Point2PointJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ createJoint();
+ }
+
+ public void setDamping(float value) {
+ ((Point2PointConstraint) constraint).setting.damping = value;
+ }
+
+ public void setImpulseClamp(float value) {
+ ((Point2PointConstraint) constraint).setting.impulseClamp = value;
+ }
+
+ public void setTau(float value) {
+ ((Point2PointConstraint) constraint).setting.tau = value;
+ }
+
+ public float getDamping() {
+ return ((Point2PointConstraint) constraint).setting.damping;
+ }
+
+ public float getImpulseClamp() {
+ return ((Point2PointConstraint) constraint).setting.impulseClamp;
+ }
+
+ public float getTau() {
+ return ((Point2PointConstraint) constraint).setting.tau;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule cap = ex.getCapsule(this);
+ cap.write(getDamping(), "damping", 1.0f);
+ cap.write(getTau(), "tau", 0.3f);
+ cap.write(getImpulseClamp(), "impulseClamp", 0f);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ createJoint();
+ InputCapsule cap=im.getCapsule(this);
+ setDamping(cap.readFloat("damping", 1.0f));
+ setDamping(cap.readFloat("tau", 0.3f));
+ setDamping(cap.readFloat("impulseClamp", 0f));
+ }
+
+ protected void createJoint() {
+ constraint = new Point2PointConstraint(nodeA.getObjectId(), nodeB.getObjectId(), Converter.convert(pivotA), Converter.convert(pivotB));
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java
new file mode 100644
index 0000000..e153760
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.bulletphysics.dynamics.constraintsolver.Generic6DofConstraint;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.joints.motors.RotationalLimitMotor;
+import com.jme3.bullet.joints.motors.TranslationalLimitMotor;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * This generic constraint can emulate a variety of standard constraints,
+ * by configuring each of the 6 degrees of freedom (dof).
+ * The first 3 dof axis are linear axis, which represent translation of rigidbodies,
+ * and the latter 3 dof axis represent the angular motion. Each axis can be either locked,
+ * free or limited. On construction of a new btGeneric6DofConstraint, all axis are locked.
+ * Afterwards the axis can be reconfigured. Note that several combinations that
+ * include free and/or limited angular degrees of freedom are undefined.
+ * @author normenhansen
+ */
+public class SixDofJoint extends PhysicsJoint {
+
+ private boolean useLinearReferenceFrameA = true;
+ private LinkedList<RotationalLimitMotor> rotationalMotors = new LinkedList<RotationalLimitMotor>();
+ private TranslationalLimitMotor translationalMotor;
+ private Vector3f angularUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY);
+ private Vector3f angularLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY);
+ private Vector3f linearUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY);
+ private Vector3f linearLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY);
+
+ public SixDofJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+
+ Transform transA = new Transform(Converter.convert(rotA));
+ Converter.convert(pivotA, transA.origin);
+ Converter.convert(rotA, transA.basis);
+
+ Transform transB = new Transform(Converter.convert(rotB));
+ Converter.convert(pivotB, transB.origin);
+ Converter.convert(rotB, transB.basis);
+
+ constraint = new Generic6DofConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA);
+ gatherMotors();
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+
+ Transform transA = new Transform(Converter.convert(new Matrix3f()));
+ Converter.convert(pivotA, transA.origin);
+
+ Transform transB = new Transform(Converter.convert(new Matrix3f()));
+ Converter.convert(pivotB, transB.origin);
+
+ constraint = new Generic6DofConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA);
+ gatherMotors();
+ }
+
+ private void gatherMotors() {
+ for (int i = 0; i < 3; i++) {
+ RotationalLimitMotor rmot = new RotationalLimitMotor(((Generic6DofConstraint) constraint).getRotationalLimitMotor(i));
+ rotationalMotors.add(rmot);
+ }
+ translationalMotor = new TranslationalLimitMotor(((Generic6DofConstraint) constraint).getTranslationalLimitMotor());
+ }
+
+ /**
+ * returns the TranslationalLimitMotor of this 6DofJoint which allows
+ * manipulating the translational axis
+ * @return the TranslationalLimitMotor
+ */
+ public TranslationalLimitMotor getTranslationalLimitMotor() {
+ return translationalMotor;
+ }
+
+ /**
+ * returns one of the three RotationalLimitMotors of this 6DofJoint which
+ * allow manipulating the rotational axes
+ * @param index the index of the RotationalLimitMotor
+ * @return the RotationalLimitMotor at the given index
+ */
+ public RotationalLimitMotor getRotationalLimitMotor(int index) {
+ return rotationalMotors.get(index);
+ }
+
+ public void setLinearUpperLimit(Vector3f vector) {
+ linearUpperLimit.set(vector);
+ ((Generic6DofConstraint) constraint).setLinearUpperLimit(Converter.convert(vector));
+ }
+
+ public void setLinearLowerLimit(Vector3f vector) {
+ linearLowerLimit.set(vector);
+ ((Generic6DofConstraint) constraint).setLinearLowerLimit(Converter.convert(vector));
+ }
+
+ public void setAngularUpperLimit(Vector3f vector) {
+ angularUpperLimit.set(vector);
+ ((Generic6DofConstraint) constraint).setAngularUpperLimit(Converter.convert(vector));
+ }
+
+ public void setAngularLowerLimit(Vector3f vector) {
+ angularLowerLimit.set(vector);
+ ((Generic6DofConstraint) constraint).setAngularLowerLimit(Converter.convert(vector));
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+
+ Transform transA = new Transform(Converter.convert(new Matrix3f()));
+ Converter.convert(pivotA, transA.origin);
+
+ Transform transB = new Transform(Converter.convert(new Matrix3f()));
+ Converter.convert(pivotB, transB.origin);
+ constraint = new Generic6DofConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA);
+ gatherMotors();
+
+ setAngularUpperLimit((Vector3f) capsule.readSavable("angularUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY)));
+ setAngularLowerLimit((Vector3f) capsule.readSavable("angularLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY)));
+ setLinearUpperLimit((Vector3f) capsule.readSavable("linearUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY)));
+ setLinearLowerLimit((Vector3f) capsule.readSavable("linearLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY)));
+
+ for (int i = 0; i < 3; i++) {
+ RotationalLimitMotor rotationalLimitMotor = getRotationalLimitMotor(i);
+ rotationalLimitMotor.setBounce(capsule.readFloat("rotMotor" + i + "_Bounce", 0.0f));
+ rotationalLimitMotor.setDamping(capsule.readFloat("rotMotor" + i + "_Damping", 1.0f));
+ rotationalLimitMotor.setERP(capsule.readFloat("rotMotor" + i + "_ERP", 0.5f));
+ rotationalLimitMotor.setHiLimit(capsule.readFloat("rotMotor" + i + "_HiLimit", Float.POSITIVE_INFINITY));
+ rotationalLimitMotor.setLimitSoftness(capsule.readFloat("rotMotor" + i + "_LimitSoftness", 0.5f));
+ rotationalLimitMotor.setLoLimit(capsule.readFloat("rotMotor" + i + "_LoLimit", Float.NEGATIVE_INFINITY));
+ rotationalLimitMotor.setMaxLimitForce(capsule.readFloat("rotMotor" + i + "_MaxLimitForce", 300.0f));
+ rotationalLimitMotor.setMaxMotorForce(capsule.readFloat("rotMotor" + i + "_MaxMotorForce", 0.1f));
+ rotationalLimitMotor.setTargetVelocity(capsule.readFloat("rotMotor" + i + "_TargetVelocity", 0));
+ rotationalLimitMotor.setEnableMotor(capsule.readBoolean("rotMotor" + i + "_EnableMotor", false));
+ }
+ getTranslationalLimitMotor().setAccumulatedImpulse((Vector3f) capsule.readSavable("transMotor_AccumulatedImpulse", Vector3f.ZERO));
+ getTranslationalLimitMotor().setDamping(capsule.readFloat("transMotor_Damping", 1.0f));
+ getTranslationalLimitMotor().setLimitSoftness(capsule.readFloat("transMotor_LimitSoftness", 0.7f));
+ getTranslationalLimitMotor().setLowerLimit((Vector3f) capsule.readSavable("transMotor_LowerLimit", Vector3f.ZERO));
+ getTranslationalLimitMotor().setRestitution(capsule.readFloat("transMotor_Restitution", 0.5f));
+ getTranslationalLimitMotor().setUpperLimit((Vector3f) capsule.readSavable("transMotor_UpperLimit", Vector3f.ZERO));
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(angularUpperLimit, "angularUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY));
+ capsule.write(angularLowerLimit, "angularLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY));
+ capsule.write(linearUpperLimit, "linearUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY));
+ capsule.write(linearLowerLimit, "linearLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY));
+ int i = 0;
+ for (Iterator<RotationalLimitMotor> it = rotationalMotors.iterator(); it.hasNext();) {
+ RotationalLimitMotor rotationalLimitMotor = it.next();
+ capsule.write(rotationalLimitMotor.getBounce(), "rotMotor" + i + "_Bounce", 0.0f);
+ capsule.write(rotationalLimitMotor.getDamping(), "rotMotor" + i + "_Damping", 1.0f);
+ capsule.write(rotationalLimitMotor.getERP(), "rotMotor" + i + "_ERP", 0.5f);
+ capsule.write(rotationalLimitMotor.getHiLimit(), "rotMotor" + i + "_HiLimit", Float.POSITIVE_INFINITY);
+ capsule.write(rotationalLimitMotor.getLimitSoftness(), "rotMotor" + i + "_LimitSoftness", 0.5f);
+ capsule.write(rotationalLimitMotor.getLoLimit(), "rotMotor" + i + "_LoLimit", Float.NEGATIVE_INFINITY);
+ capsule.write(rotationalLimitMotor.getMaxLimitForce(), "rotMotor" + i + "_MaxLimitForce", 300.0f);
+ capsule.write(rotationalLimitMotor.getMaxMotorForce(), "rotMotor" + i + "_MaxMotorForce", 0.1f);
+ capsule.write(rotationalLimitMotor.getTargetVelocity(), "rotMotor" + i + "_TargetVelocity", 0);
+ capsule.write(rotationalLimitMotor.isEnableMotor(), "rotMotor" + i + "_EnableMotor", false);
+ i++;
+ }
+ capsule.write(getTranslationalLimitMotor().getAccumulatedImpulse(), "transMotor_AccumulatedImpulse", Vector3f.ZERO);
+ capsule.write(getTranslationalLimitMotor().getDamping(), "transMotor_Damping", 1.0f);
+ capsule.write(getTranslationalLimitMotor().getLimitSoftness(), "transMotor_LimitSoftness", 0.7f);
+ capsule.write(getTranslationalLimitMotor().getLowerLimit(), "transMotor_LowerLimit", Vector3f.ZERO);
+ capsule.write(getTranslationalLimitMotor().getRestitution(), "transMotor_Restitution", 0.5f);
+ capsule.write(getTranslationalLimitMotor().getUpperLimit(), "transMotor_UpperLimit", Vector3f.ZERO);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java
new file mode 100644
index 0000000..4602018
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints;
+
+import com.bulletphysics.dynamics.constraintsolver.SliderConstraint;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * <i>From bullet manual:</i><br>
+ * The slider constraint allows the body to rotate around one axis and translate along this axis.
+ * @author normenhansen
+ */
+public class SliderJoint extends PhysicsJoint {
+ protected Matrix3f rotA, rotB;
+ protected boolean useLinearReferenceFrameA;
+
+ public SliderJoint() {
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA=rotA;
+ this.rotB=rotB;
+ this.useLinearReferenceFrameA=useLinearReferenceFrameA;
+ createJoint();
+ }
+
+ /**
+ * @param pivotA local translation of the joint connection point in node A
+ * @param pivotB local translation of the joint connection point in node B
+ */
+ public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) {
+ super(nodeA, nodeB, pivotA, pivotB);
+ this.rotA=new Matrix3f();
+ this.rotB=new Matrix3f();
+ this.useLinearReferenceFrameA=useLinearReferenceFrameA;
+ createJoint();
+ }
+
+ public float getLowerLinLimit() {
+ return ((SliderConstraint) constraint).getLowerLinLimit();
+ }
+
+ public void setLowerLinLimit(float lowerLinLimit) {
+ ((SliderConstraint) constraint).setLowerLinLimit(lowerLinLimit);
+ }
+
+ public float getUpperLinLimit() {
+ return ((SliderConstraint) constraint).getUpperLinLimit();
+ }
+
+ public void setUpperLinLimit(float upperLinLimit) {
+ ((SliderConstraint) constraint).setUpperLinLimit(upperLinLimit);
+ }
+
+ public float getLowerAngLimit() {
+ return ((SliderConstraint) constraint).getLowerAngLimit();
+ }
+
+ public void setLowerAngLimit(float lowerAngLimit) {
+ ((SliderConstraint) constraint).setLowerAngLimit(lowerAngLimit);
+ }
+
+ public float getUpperAngLimit() {
+ return ((SliderConstraint) constraint).getUpperAngLimit();
+ }
+
+ public void setUpperAngLimit(float upperAngLimit) {
+ ((SliderConstraint) constraint).setUpperAngLimit(upperAngLimit);
+ }
+
+ public float getSoftnessDirLin() {
+ return ((SliderConstraint) constraint).getSoftnessDirLin();
+ }
+
+ public void setSoftnessDirLin(float softnessDirLin) {
+ ((SliderConstraint) constraint).setSoftnessDirLin(softnessDirLin);
+ }
+
+ public float getRestitutionDirLin() {
+ return ((SliderConstraint) constraint).getRestitutionDirLin();
+ }
+
+ public void setRestitutionDirLin(float restitutionDirLin) {
+ ((SliderConstraint) constraint).setRestitutionDirLin(restitutionDirLin);
+ }
+
+ public float getDampingDirLin() {
+ return ((SliderConstraint) constraint).getDampingDirLin();
+ }
+
+ public void setDampingDirLin(float dampingDirLin) {
+ ((SliderConstraint) constraint).setDampingDirLin(dampingDirLin);
+ }
+
+ public float getSoftnessDirAng() {
+ return ((SliderConstraint) constraint).getSoftnessDirAng();
+ }
+
+ public void setSoftnessDirAng(float softnessDirAng) {
+ ((SliderConstraint) constraint).setSoftnessDirAng(softnessDirAng);
+ }
+
+ public float getRestitutionDirAng() {
+ return ((SliderConstraint) constraint).getRestitutionDirAng();
+ }
+
+ public void setRestitutionDirAng(float restitutionDirAng) {
+ ((SliderConstraint) constraint).setRestitutionDirAng(restitutionDirAng);
+ }
+
+ public float getDampingDirAng() {
+ return ((SliderConstraint) constraint).getDampingDirAng();
+ }
+
+ public void setDampingDirAng(float dampingDirAng) {
+ ((SliderConstraint) constraint).setDampingDirAng(dampingDirAng);
+ }
+
+ public float getSoftnessLimLin() {
+ return ((SliderConstraint) constraint).getSoftnessLimLin();
+ }
+
+ public void setSoftnessLimLin(float softnessLimLin) {
+ ((SliderConstraint) constraint).setSoftnessLimLin(softnessLimLin);
+ }
+
+ public float getRestitutionLimLin() {
+ return ((SliderConstraint) constraint).getRestitutionLimLin();
+ }
+
+ public void setRestitutionLimLin(float restitutionLimLin) {
+ ((SliderConstraint) constraint).setRestitutionLimLin(restitutionLimLin);
+ }
+
+ public float getDampingLimLin() {
+ return ((SliderConstraint) constraint).getDampingLimLin();
+ }
+
+ public void setDampingLimLin(float dampingLimLin) {
+ ((SliderConstraint) constraint).setDampingLimLin(dampingLimLin);
+ }
+
+ public float getSoftnessLimAng() {
+ return ((SliderConstraint) constraint).getSoftnessLimAng();
+ }
+
+ public void setSoftnessLimAng(float softnessLimAng) {
+ ((SliderConstraint) constraint).setSoftnessLimAng(softnessLimAng);
+ }
+
+ public float getRestitutionLimAng() {
+ return ((SliderConstraint) constraint).getRestitutionLimAng();
+ }
+
+ public void setRestitutionLimAng(float restitutionLimAng) {
+ ((SliderConstraint) constraint).setRestitutionLimAng(restitutionLimAng);
+ }
+
+ public float getDampingLimAng() {
+ return ((SliderConstraint) constraint).getDampingLimAng();
+ }
+
+ public void setDampingLimAng(float dampingLimAng) {
+ ((SliderConstraint) constraint).setDampingLimAng(dampingLimAng);
+ }
+
+ public float getSoftnessOrthoLin() {
+ return ((SliderConstraint) constraint).getSoftnessOrthoLin();
+ }
+
+ public void setSoftnessOrthoLin(float softnessOrthoLin) {
+ ((SliderConstraint) constraint).setSoftnessOrthoLin(softnessOrthoLin);
+ }
+
+ public float getRestitutionOrthoLin() {
+ return ((SliderConstraint) constraint).getRestitutionOrthoLin();
+ }
+
+ public void setRestitutionOrthoLin(float restitutionOrthoLin) {
+ ((SliderConstraint) constraint).setRestitutionOrthoLin(restitutionOrthoLin);
+ }
+
+ public float getDampingOrthoLin() {
+ return ((SliderConstraint) constraint).getDampingOrthoLin();
+ }
+
+ public void setDampingOrthoLin(float dampingOrthoLin) {
+ ((SliderConstraint) constraint).setDampingOrthoLin(dampingOrthoLin);
+ }
+
+ public float getSoftnessOrthoAng() {
+ return ((SliderConstraint) constraint).getSoftnessOrthoAng();
+ }
+
+ public void setSoftnessOrthoAng(float softnessOrthoAng) {
+ ((SliderConstraint) constraint).setSoftnessOrthoAng(softnessOrthoAng);
+ }
+
+ public float getRestitutionOrthoAng() {
+ return ((SliderConstraint) constraint).getRestitutionOrthoAng();
+ }
+
+ public void setRestitutionOrthoAng(float restitutionOrthoAng) {
+ ((SliderConstraint) constraint).setRestitutionOrthoAng(restitutionOrthoAng);
+ }
+
+ public float getDampingOrthoAng() {
+ return ((SliderConstraint) constraint).getDampingOrthoAng();
+ }
+
+ public void setDampingOrthoAng(float dampingOrthoAng) {
+ ((SliderConstraint) constraint).setDampingOrthoAng(dampingOrthoAng);
+ }
+
+ public boolean isPoweredLinMotor() {
+ return ((SliderConstraint) constraint).getPoweredLinMotor();
+ }
+
+ public void setPoweredLinMotor(boolean poweredLinMotor) {
+ ((SliderConstraint) constraint).setPoweredLinMotor(poweredLinMotor);
+ }
+
+ public float getTargetLinMotorVelocity() {
+ return ((SliderConstraint) constraint).getTargetLinMotorVelocity();
+ }
+
+ public void setTargetLinMotorVelocity(float targetLinMotorVelocity) {
+ ((SliderConstraint) constraint).setTargetLinMotorVelocity(targetLinMotorVelocity);
+ }
+
+ public float getMaxLinMotorForce() {
+ return ((SliderConstraint) constraint).getMaxLinMotorForce();
+ }
+
+ public void setMaxLinMotorForce(float maxLinMotorForce) {
+ ((SliderConstraint) constraint).setMaxLinMotorForce(maxLinMotorForce);
+ }
+
+ public boolean isPoweredAngMotor() {
+ return ((SliderConstraint) constraint).getPoweredAngMotor();
+ }
+
+ public void setPoweredAngMotor(boolean poweredAngMotor) {
+ ((SliderConstraint) constraint).setPoweredAngMotor(poweredAngMotor);
+ }
+
+ public float getTargetAngMotorVelocity() {
+ return ((SliderConstraint) constraint).getTargetAngMotorVelocity();
+ }
+
+ public void setTargetAngMotorVelocity(float targetAngMotorVelocity) {
+ ((SliderConstraint) constraint).setTargetAngMotorVelocity(targetAngMotorVelocity);
+ }
+
+ public float getMaxAngMotorForce() {
+ return ((SliderConstraint) constraint).getMaxAngMotorForce();
+ }
+
+ public void setMaxAngMotorForce(float maxAngMotorForce) {
+ ((SliderConstraint) constraint).setMaxAngMotorForce(maxAngMotorForce);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule capsule = ex.getCapsule(this);
+ //TODO: standard values..
+ capsule.write(((SliderConstraint) constraint).getDampingDirAng(), "dampingDirAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getDampingDirLin(), "dampingDirLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getDampingLimAng(), "dampingLimAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getDampingLimLin(), "dampingLimLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getDampingOrthoAng(), "dampingOrthoAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getDampingOrthoLin(), "dampingOrthoLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getLowerAngLimit(), "lowerAngLimit", 0f);
+ capsule.write(((SliderConstraint) constraint).getLowerLinLimit(), "lowerLinLimit", 0f);
+ capsule.write(((SliderConstraint) constraint).getMaxAngMotorForce(), "maxAngMotorForce", 0f);
+ capsule.write(((SliderConstraint) constraint).getMaxLinMotorForce(), "maxLinMotorForce", 0f);
+ capsule.write(((SliderConstraint) constraint).getPoweredAngMotor(), "poweredAngMotor", false);
+ capsule.write(((SliderConstraint) constraint).getPoweredLinMotor(), "poweredLinMotor", false);
+ capsule.write(((SliderConstraint) constraint).getRestitutionDirAng(), "restitutionDirAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getRestitutionDirLin(), "restitutionDirLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getRestitutionLimAng(), "restitutionLimAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getRestitutionLimLin(), "restitutionLimLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getRestitutionOrthoAng(), "restitutionOrthoAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getRestitutionOrthoLin(), "restitutionOrthoLin", 0f);
+
+ capsule.write(((SliderConstraint) constraint).getSoftnessDirAng(), "softnessDirAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getSoftnessDirLin(), "softnessDirLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getSoftnessLimAng(), "softnessLimAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getSoftnessLimLin(), "softnessLimLin", 0f);
+ capsule.write(((SliderConstraint) constraint).getSoftnessOrthoAng(), "softnessOrthoAng", 0f);
+ capsule.write(((SliderConstraint) constraint).getSoftnessOrthoLin(), "softnessOrthoLin", 0f);
+
+ capsule.write(((SliderConstraint) constraint).getTargetAngMotorVelocity(), "targetAngMotorVelicoty", 0f);
+ capsule.write(((SliderConstraint) constraint).getTargetLinMotorVelocity(), "targetLinMotorVelicoty", 0f);
+
+ capsule.write(((SliderConstraint) constraint).getUpperAngLimit(), "upperAngLimit", 0f);
+ capsule.write(((SliderConstraint) constraint).getUpperLinLimit(), "upperLinLimit", 0f);
+
+ capsule.write(useLinearReferenceFrameA, "useLinearReferenceFrameA", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule capsule = im.getCapsule(this);
+ float dampingDirAng = capsule.readFloat("dampingDirAng", 0f);
+ float dampingDirLin = capsule.readFloat("dampingDirLin", 0f);
+ float dampingLimAng = capsule.readFloat("dampingLimAng", 0f);
+ float dampingLimLin = capsule.readFloat("dampingLimLin", 0f);
+ float dampingOrthoAng = capsule.readFloat("dampingOrthoAng", 0f);
+ float dampingOrthoLin = capsule.readFloat("dampingOrthoLin", 0f);
+ float lowerAngLimit = capsule.readFloat("lowerAngLimit", 0f);
+ float lowerLinLimit = capsule.readFloat("lowerLinLimit", 0f);
+ float maxAngMotorForce = capsule.readFloat("maxAngMotorForce", 0f);
+ float maxLinMotorForce = capsule.readFloat("maxLinMotorForce", 0f);
+ boolean poweredAngMotor = capsule.readBoolean("poweredAngMotor", false);
+ boolean poweredLinMotor = capsule.readBoolean("poweredLinMotor", false);
+ float restitutionDirAng = capsule.readFloat("restitutionDirAng", 0f);
+ float restitutionDirLin = capsule.readFloat("restitutionDirLin", 0f);
+ float restitutionLimAng = capsule.readFloat("restitutionLimAng", 0f);
+ float restitutionLimLin = capsule.readFloat("restitutionLimLin", 0f);
+ float restitutionOrthoAng = capsule.readFloat("restitutionOrthoAng", 0f);
+ float restitutionOrthoLin = capsule.readFloat("restitutionOrthoLin", 0f);
+
+ float softnessDirAng = capsule.readFloat("softnessDirAng", 0f);
+ float softnessDirLin = capsule.readFloat("softnessDirLin", 0f);
+ float softnessLimAng = capsule.readFloat("softnessLimAng", 0f);
+ float softnessLimLin = capsule.readFloat("softnessLimLin", 0f);
+ float softnessOrthoAng = capsule.readFloat("softnessOrthoAng", 0f);
+ float softnessOrthoLin = capsule.readFloat("softnessOrthoLin", 0f);
+
+ float targetAngMotorVelicoty = capsule.readFloat("targetAngMotorVelicoty", 0f);
+ float targetLinMotorVelicoty = capsule.readFloat("targetLinMotorVelicoty", 0f);
+
+ float upperAngLimit = capsule.readFloat("upperAngLimit", 0f);
+ float upperLinLimit = capsule.readFloat("upperLinLimit", 0f);
+
+ useLinearReferenceFrameA = capsule.readBoolean("useLinearReferenceFrameA", false);
+
+ createJoint();
+
+ ((SliderConstraint)constraint).setDampingDirAng(dampingDirAng);
+ ((SliderConstraint)constraint).setDampingDirLin(dampingDirLin);
+ ((SliderConstraint)constraint).setDampingLimAng(dampingLimAng);
+ ((SliderConstraint)constraint).setDampingLimLin(dampingLimLin);
+ ((SliderConstraint)constraint).setDampingOrthoAng(dampingOrthoAng);
+ ((SliderConstraint)constraint).setDampingOrthoLin(dampingOrthoLin);
+ ((SliderConstraint)constraint).setLowerAngLimit(lowerAngLimit);
+ ((SliderConstraint)constraint).setLowerLinLimit(lowerLinLimit);
+ ((SliderConstraint)constraint).setMaxAngMotorForce(maxAngMotorForce);
+ ((SliderConstraint)constraint).setMaxLinMotorForce(maxLinMotorForce);
+ ((SliderConstraint)constraint).setPoweredAngMotor(poweredAngMotor);
+ ((SliderConstraint)constraint).setPoweredLinMotor(poweredLinMotor);
+ ((SliderConstraint)constraint).setRestitutionDirAng(restitutionDirAng);
+ ((SliderConstraint)constraint).setRestitutionDirLin(restitutionDirLin);
+ ((SliderConstraint)constraint).setRestitutionLimAng(restitutionLimAng);
+ ((SliderConstraint)constraint).setRestitutionLimLin(restitutionLimLin);
+ ((SliderConstraint)constraint).setRestitutionOrthoAng(restitutionOrthoAng);
+ ((SliderConstraint)constraint).setRestitutionOrthoLin(restitutionOrthoLin);
+
+ ((SliderConstraint)constraint).setSoftnessDirAng(softnessDirAng);
+ ((SliderConstraint)constraint).setSoftnessDirLin(softnessDirLin);
+ ((SliderConstraint)constraint).setSoftnessLimAng(softnessLimAng);
+ ((SliderConstraint)constraint).setSoftnessLimLin(softnessLimLin);
+ ((SliderConstraint)constraint).setSoftnessOrthoAng(softnessOrthoAng);
+ ((SliderConstraint)constraint).setSoftnessOrthoLin(softnessOrthoLin);
+
+ ((SliderConstraint)constraint).setTargetAngMotorVelocity(targetAngMotorVelicoty);
+ ((SliderConstraint)constraint).setTargetLinMotorVelocity(targetLinMotorVelicoty);
+
+ ((SliderConstraint)constraint).setUpperAngLimit(upperAngLimit);
+ ((SliderConstraint)constraint).setUpperLinLimit(upperLinLimit);
+ }
+
+ protected void createJoint(){
+ Transform transA = new Transform(Converter.convert(rotA));
+ Converter.convert(pivotA, transA.origin);
+ Converter.convert(rotA, transA.basis);
+
+ Transform transB = new Transform(Converter.convert(rotB));
+ Converter.convert(pivotB, transB.origin);
+ Converter.convert(rotB, transB.basis);
+
+ constraint = new SliderConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java b/engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java
new file mode 100644
index 0000000..b9df96d
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints.motors;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class RotationalLimitMotor {
+
+ private com.bulletphysics.dynamics.constraintsolver.RotationalLimitMotor motor;
+
+ public RotationalLimitMotor(com.bulletphysics.dynamics.constraintsolver.RotationalLimitMotor motor) {
+ this.motor = motor;
+ }
+
+ public com.bulletphysics.dynamics.constraintsolver.RotationalLimitMotor getMotor() {
+ return motor;
+ }
+
+ public float getLoLimit() {
+ return motor.loLimit;
+ }
+
+ public void setLoLimit(float loLimit) {
+ motor.loLimit = loLimit;
+ }
+
+ public float getHiLimit() {
+ return motor.hiLimit;
+ }
+
+ public void setHiLimit(float hiLimit) {
+ motor.hiLimit = hiLimit;
+ }
+
+ public float getTargetVelocity() {
+ return motor.targetVelocity;
+ }
+
+ public void setTargetVelocity(float targetVelocity) {
+ motor.targetVelocity = targetVelocity;
+ }
+
+ public float getMaxMotorForce() {
+ return motor.maxMotorForce;
+ }
+
+ public void setMaxMotorForce(float maxMotorForce) {
+ motor.maxMotorForce = maxMotorForce;
+ }
+
+ public float getMaxLimitForce() {
+ return motor.maxLimitForce;
+ }
+
+ public void setMaxLimitForce(float maxLimitForce) {
+ motor.maxLimitForce = maxLimitForce;
+ }
+
+ public float getDamping() {
+ return motor.damping;
+ }
+
+ public void setDamping(float damping) {
+ motor.damping = damping;
+ }
+
+ public float getLimitSoftness() {
+ return motor.limitSoftness;
+ }
+
+ public void setLimitSoftness(float limitSoftness) {
+ motor.limitSoftness = limitSoftness;
+ }
+
+ public float getERP() {
+ return motor.ERP;
+ }
+
+ public void setERP(float ERP) {
+ motor.ERP = ERP;
+ }
+
+ public float getBounce() {
+ return motor.bounce;
+ }
+
+ public void setBounce(float bounce) {
+ motor.bounce = bounce;
+ }
+
+ public boolean isEnableMotor() {
+ return motor.enableMotor;
+ }
+
+ public void setEnableMotor(boolean enableMotor) {
+ motor.enableMotor = enableMotor;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java b/engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java
new file mode 100644
index 0000000..d5c23cb
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.joints.motors;
+
+import com.jme3.bullet.util.Converter;
+import com.jme3.math.Vector3f;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TranslationalLimitMotor {
+
+ private com.bulletphysics.dynamics.constraintsolver.TranslationalLimitMotor motor;
+
+ public TranslationalLimitMotor(com.bulletphysics.dynamics.constraintsolver.TranslationalLimitMotor motor) {
+ this.motor = motor;
+ }
+
+ public com.bulletphysics.dynamics.constraintsolver.TranslationalLimitMotor getMotor() {
+ return motor;
+ }
+
+ public Vector3f getLowerLimit() {
+ return Converter.convert(motor.lowerLimit);
+ }
+
+ public void setLowerLimit(Vector3f lowerLimit) {
+ Converter.convert(lowerLimit, motor.lowerLimit);
+ }
+
+ public Vector3f getUpperLimit() {
+ return Converter.convert(motor.upperLimit);
+ }
+
+ public void setUpperLimit(Vector3f upperLimit) {
+ Converter.convert(upperLimit, motor.upperLimit);
+ }
+
+ public Vector3f getAccumulatedImpulse() {
+ return Converter.convert(motor.accumulatedImpulse);
+ }
+
+ public void setAccumulatedImpulse(Vector3f accumulatedImpulse) {
+ Converter.convert(accumulatedImpulse, motor.accumulatedImpulse);
+ }
+
+ public float getLimitSoftness() {
+ return motor.limitSoftness;
+ }
+
+ public void setLimitSoftness(float limitSoftness) {
+ motor.limitSoftness = limitSoftness;
+ }
+
+ public float getDamping() {
+ return motor.damping;
+ }
+
+ public void setDamping(float damping) {
+ motor.damping = damping;
+ }
+
+ public float getRestitution() {
+ return motor.restitution;
+ }
+
+ public void setRestitution(float restitution) {
+ motor.restitution = restitution;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java
new file mode 100644
index 0000000..932c2e1
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.bulletphysics.collision.dispatch.CollisionFlags;
+import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
+import com.bulletphysics.collision.shapes.ConvexShape;
+import com.bulletphysics.dynamics.character.KinematicCharacterController;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * Basic Bullet Character
+ * @author normenhansen
+ */
+public class PhysicsCharacter extends PhysicsCollisionObject {
+
+ protected KinematicCharacterController character;
+ protected float stepHeight;
+ protected Vector3f walkDirection = new Vector3f();
+ protected float fallSpeed = 55.0f;
+ protected float jumpSpeed = 10.0f;
+ protected int upAxis = 1;
+ protected PairCachingGhostObject gObject;
+ protected boolean locationDirty = false;
+ //TEMP VARIABLES
+ protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
+ private Transform tempTrans = new Transform(Converter.convert(new Matrix3f()));
+ private com.jme3.math.Transform physicsLocation = new com.jme3.math.Transform();
+ private javax.vecmath.Vector3f tempVec = new javax.vecmath.Vector3f();
+
+ public PhysicsCharacter() {
+ }
+
+ /**
+ * @param shape The CollisionShape (no Mesh or CompoundCollisionShapes)
+ * @param stepHeight The quantization size for vertical movement
+ */
+ public PhysicsCharacter(CollisionShape shape, float stepHeight) {
+ this.collisionShape = shape;
+ if (!(shape.getCShape() instanceof ConvexShape)) {
+ throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes"));
+ }
+ this.stepHeight = stepHeight;
+ buildObject();
+ }
+
+ protected void buildObject() {
+ if (gObject == null) {
+ gObject = new PairCachingGhostObject();
+ }
+ gObject.setCollisionFlags(CollisionFlags.CHARACTER_OBJECT);
+ gObject.setCollisionFlags(gObject.getCollisionFlags() & ~CollisionFlags.NO_CONTACT_RESPONSE);
+ gObject.setCollisionShape(collisionShape.getCShape());
+ gObject.setUserPointer(this);
+ character = new KinematicCharacterController(gObject, (ConvexShape) collisionShape.getCShape(), stepHeight);
+ }
+
+ /**
+ * Sets the location of this physics character
+ * @param location
+ */
+ public void warp(Vector3f location) {
+ character.warp(Converter.convert(location, tempVec));
+ }
+
+ /**
+ * Set the walk direction, works continuously.
+ * This should probably be called setPositionIncrementPerSimulatorStep.
+ * This is neither a direction nor a velocity, but the amount to
+ * increment the position each physics tick. So vector length = accuracy*speed in m/s
+ * @param vec the walk direction to set
+ */
+ public void setWalkDirection(Vector3f vec) {
+ walkDirection.set(vec);
+ character.setWalkDirection(Converter.convert(walkDirection, tempVec));
+ }
+
+ /**
+ * @return the currently set walkDirection
+ */
+ public Vector3f getWalkDirection() {
+ return walkDirection;
+ }
+
+ public void setUpAxis(int axis) {
+ upAxis = axis;
+ character.setUpAxis(axis);
+ }
+
+ public int getUpAxis() {
+ return upAxis;
+ }
+
+ public void setFallSpeed(float fallSpeed) {
+ this.fallSpeed = fallSpeed;
+ character.setFallSpeed(fallSpeed);
+ }
+
+ public float getFallSpeed() {
+ return fallSpeed;
+ }
+
+ public void setJumpSpeed(float jumpSpeed) {
+ this.jumpSpeed = jumpSpeed;
+ character.setJumpSpeed(jumpSpeed);
+ }
+
+ public float getJumpSpeed() {
+ return jumpSpeed;
+ }
+
+ //does nothing..
+// public void setMaxJumpHeight(float height) {
+// character.setMaxJumpHeight(height);
+// }
+ public void setGravity(float value) {
+ character.setGravity(value);
+ }
+
+ public float getGravity() {
+ return character.getGravity();
+ }
+
+ public void setMaxSlope(float slopeRadians) {
+ character.setMaxSlope(slopeRadians);
+ }
+
+ public float getMaxSlope() {
+ return character.getMaxSlope();
+ }
+
+ public boolean onGround() {
+ return character.onGround();
+ }
+
+ public void jump() {
+ character.jump();
+ }
+
+ @Override
+ public void setCollisionShape(CollisionShape collisionShape) {
+ if (!(collisionShape.getCShape() instanceof ConvexShape)) {
+ throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes"));
+ }
+ super.setCollisionShape(collisionShape);
+ if (gObject == null) {
+ buildObject();
+ }else{
+ gObject.setCollisionShape(collisionShape.getCShape());
+ }
+ }
+
+ /**
+ * Set the physics location (same as warp())
+ * @param location the location of the actual physics object
+ */
+ public void setPhysicsLocation(Vector3f location) {
+ warp(location);
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation(Vector3f trans) {
+ if (trans == null) {
+ trans = new Vector3f();
+ }
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
+ return trans.set(physicsLocation.getTranslation());
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation() {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
+ return physicsLocation.getTranslation();
+ }
+
+ public void setCcdSweptSphereRadius(float radius) {
+ gObject.setCcdSweptSphereRadius(radius);
+ }
+
+ public void setCcdMotionThreshold(float threshold) {
+ gObject.setCcdMotionThreshold(threshold);
+ }
+
+ public float getCcdSweptSphereRadius() {
+ return gObject.getCcdSweptSphereRadius();
+ }
+
+ public float getCcdMotionThreshold() {
+ return gObject.getCcdMotionThreshold();
+ }
+
+ public float getCcdSquareMotionThreshold() {
+ return gObject.getCcdSquareMotionThreshold();
+ }
+
+ /**
+ * used internally
+ */
+ public KinematicCharacterController getControllerId() {
+ return character;
+ }
+
+ /**
+ * used internally
+ */
+ public PairCachingGhostObject getObjectId() {
+ return gObject;
+ }
+
+ public void destroy() {
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(stepHeight, "stepHeight", 1.0f);
+ capsule.write(getGravity(), "gravity", 9.8f * 3);
+ capsule.write(getMaxSlope(), "maxSlope", 1.0f);
+ capsule.write(fallSpeed, "fallSpeed", 55.0f);
+ capsule.write(jumpSpeed, "jumpSpeed", 10.0f);
+ capsule.write(upAxis, "upAxis", 1);
+ capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
+ capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
+ capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ stepHeight = capsule.readFloat("stepHeight", 1.0f);
+ buildObject();
+ character = new KinematicCharacterController(gObject, (ConvexShape) collisionShape.getCShape(), stepHeight);
+ setGravity(capsule.readFloat("gravity", 9.8f * 3));
+ setMaxSlope(capsule.readFloat("maxSlope", 1.0f));
+ setFallSpeed(capsule.readFloat("fallSpeed", 55.0f));
+ setJumpSpeed(capsule.readFloat("jumpSpeed", 10.0f));
+ setUpAxis(capsule.readInt("upAxis", 1));
+ setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
+ setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
+ setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java
new file mode 100644
index 0000000..2f53a82
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.bulletphysics.collision.dispatch.CollisionFlags;
+import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <i>From Bullet manual:</i><br>
+ * GhostObject can keep track of all objects that are overlapping.
+ * By default, this overlap is based on the AABB.
+ * This is useful for creating a character controller,
+ * collision sensors/triggers, explosions etc.<br>
+ * @author normenhansen
+ */
+public class PhysicsGhostObject extends PhysicsCollisionObject {
+
+ protected PairCachingGhostObject gObject;
+ protected boolean locationDirty = false;
+ //TEMP VARIABLES
+ protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
+ protected Transform tempTrans = new Transform(Converter.convert(new Matrix3f()));
+ private com.jme3.math.Transform physicsLocation = new com.jme3.math.Transform();
+ protected javax.vecmath.Quat4f tempRot = new javax.vecmath.Quat4f();
+ private List<PhysicsCollisionObject> overlappingObjects = new LinkedList<PhysicsCollisionObject>();
+
+ public PhysicsGhostObject() {
+ }
+
+ public PhysicsGhostObject(CollisionShape shape) {
+ collisionShape = shape;
+ buildObject();
+ }
+
+ public PhysicsGhostObject(Spatial child, CollisionShape shape) {
+ collisionShape = shape;
+ buildObject();
+ }
+
+ protected void buildObject() {
+ if (gObject == null) {
+ gObject = new PairCachingGhostObject();
+ gObject.setCollisionFlags(gObject.getCollisionFlags() | CollisionFlags.NO_CONTACT_RESPONSE);
+ }
+ gObject.setCollisionShape(collisionShape.getCShape());
+ gObject.setUserPointer(this);
+ }
+
+ @Override
+ public void setCollisionShape(CollisionShape collisionShape) {
+ super.setCollisionShape(collisionShape);
+ if (gObject == null) {
+ buildObject();
+ }else{
+ gObject.setCollisionShape(collisionShape.getCShape());
+ }
+ }
+
+ /**
+ * Sets the physics object location
+ * @param location the location of the actual physics object
+ */
+ public void setPhysicsLocation(Vector3f location) {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(location, tempTrans.origin);
+ gObject.setWorldTransform(tempTrans);
+ }
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Matrix3f rotation) {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(rotation, tempTrans.basis);
+ gObject.setWorldTransform(tempTrans);
+ }
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Quaternion rotation) {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(rotation, tempTrans.basis);
+ gObject.setWorldTransform(tempTrans);
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public com.jme3.math.Transform getPhysicsTransform() {
+ return physicsLocation;
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation(Vector3f trans) {
+ if (trans == null) {
+ trans = new Vector3f();
+ }
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
+ return trans.set(physicsLocation.getTranslation());
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Quaternion getPhysicsRotation(Quaternion rot) {
+ if (rot == null) {
+ rot = new Quaternion();
+ }
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
+ return rot.set(physicsLocation.getRotation());
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) {
+ if (rot == null) {
+ rot = new Matrix3f();
+ }
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
+ return rot.set(physicsLocation.getRotation());
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Vector3f getPhysicsLocation() {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
+ return physicsLocation.getTranslation();
+ }
+
+ /**
+ * @return the physicsLocation
+ */
+ public Quaternion getPhysicsRotation() {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
+ return physicsLocation.getRotation();
+ }
+
+ public Matrix3f getPhysicsRotationMatrix() {
+ gObject.getWorldTransform(tempTrans);
+ Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
+ return physicsLocation.getRotation().toRotationMatrix();
+ }
+
+ /**
+ * used internally
+ */
+ public PairCachingGhostObject getObjectId() {
+ return gObject;
+ }
+
+ /**
+ * destroys this PhysicsGhostNode and removes it from memory
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Another Object is overlapping with this GhostNode,
+ * if and if only there CollisionShapes overlaps.
+ * They could be both regular PhysicsRigidBodys or PhysicsGhostObjects.
+ * @return All CollisionObjects overlapping with this GhostNode.
+ */
+ public List<PhysicsCollisionObject> getOverlappingObjects() {
+ overlappingObjects.clear();
+ for (com.bulletphysics.collision.dispatch.CollisionObject collObj : gObject.getOverlappingPairs()) {
+ overlappingObjects.add((PhysicsCollisionObject) collObj.getUserPointer());
+ }
+ return overlappingObjects;
+ }
+
+ /**
+ *
+ * @return With how many other CollisionObjects this GhostNode is currently overlapping.
+ */
+ public int getOverlappingCount() {
+ return gObject.getNumOverlappingObjects();
+ }
+
+ /**
+ *
+ * @param index The index of the overlapping Node to retrieve.
+ * @return The Overlapping CollisionObject at the given index.
+ */
+ public PhysicsCollisionObject getOverlapping(int index) {
+ return overlappingObjects.get(index);
+ }
+
+ public void setCcdSweptSphereRadius(float radius) {
+ gObject.setCcdSweptSphereRadius(radius);
+ }
+
+ public void setCcdMotionThreshold(float threshold) {
+ gObject.setCcdMotionThreshold(threshold);
+ }
+
+ public float getCcdSweptSphereRadius() {
+ return gObject.getCcdSweptSphereRadius();
+ }
+
+ public float getCcdMotionThreshold() {
+ return gObject.getCcdMotionThreshold();
+ }
+
+ public float getCcdSquareMotionThreshold() {
+ return gObject.getCcdSquareMotionThreshold();
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
+ capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f());
+ capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
+ capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ buildObject();
+ setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
+ setPhysicsRotation(((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f())));
+ setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
+ setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java
new file mode 100644
index 0000000..d0aea98
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.bulletphysics.collision.dispatch.CollisionFlags;
+import com.bulletphysics.dynamics.RigidBody;
+import com.bulletphysics.dynamics.RigidBodyConstructionInfo;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.joints.PhysicsJoint;
+import com.jme3.bullet.objects.infos.RigidBodyMotionState;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * <p>PhysicsRigidBody - Basic physics object</p>
+ * @author normenhansen
+ */
+public class PhysicsRigidBody extends PhysicsCollisionObject {
+
+ protected RigidBodyConstructionInfo constructionInfo;
+ protected RigidBody rBody;
+ protected RigidBodyMotionState motionState = new RigidBodyMotionState();
+ protected float mass = 1.0f;
+ protected boolean kinematic = false;
+ protected javax.vecmath.Vector3f tempVec = new javax.vecmath.Vector3f();
+ protected javax.vecmath.Vector3f tempVec2 = new javax.vecmath.Vector3f();
+ protected Transform tempTrans = new Transform(new javax.vecmath.Matrix3f());
+ protected javax.vecmath.Matrix3f tempMatrix = new javax.vecmath.Matrix3f();
+ //TEMP VARIABLES
+ protected javax.vecmath.Vector3f localInertia = new javax.vecmath.Vector3f();
+ protected ArrayList<PhysicsJoint> joints = new ArrayList<PhysicsJoint>();
+
+ public PhysicsRigidBody() {
+ }
+
+ /**
+ * Creates a new PhysicsRigidBody with the supplied collision shape
+ * @param shape
+ */
+ public PhysicsRigidBody(CollisionShape shape) {
+ collisionShape = shape;
+ rebuildRigidBody();
+ }
+
+ public PhysicsRigidBody(CollisionShape shape, float mass) {
+ collisionShape = shape;
+ this.mass = mass;
+ rebuildRigidBody();
+ }
+
+ /**
+ * Builds/rebuilds the phyiscs body when parameters have changed
+ */
+ protected void rebuildRigidBody() {
+ boolean removed = false;
+ if(collisionShape instanceof MeshCollisionShape && mass != 0){
+ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!");
+ }
+ if (rBody != null) {
+ if (rBody.isInWorld()) {
+ PhysicsSpace.getPhysicsSpace().remove(this);
+ removed = true;
+ }
+ rBody.destroy();
+ }
+ preRebuild();
+ rBody = new RigidBody(constructionInfo);
+ postRebuild();
+ if (removed) {
+ PhysicsSpace.getPhysicsSpace().add(this);
+ }
+ }
+
+ protected void preRebuild() {
+ collisionShape.calculateLocalInertia(mass, localInertia);
+ if (constructionInfo == null) {
+ constructionInfo = new RigidBodyConstructionInfo(mass, motionState, collisionShape.getCShape(), localInertia);
+ } else {
+ constructionInfo.mass = mass;
+ constructionInfo.collisionShape = collisionShape.getCShape();
+ constructionInfo.motionState = motionState;
+ }
+ }
+
+ protected void postRebuild() {
+ rBody.setUserPointer(this);
+ if (mass == 0.0f) {
+ rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.STATIC_OBJECT);
+ } else {
+ rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.STATIC_OBJECT);
+ }
+ }
+
+ /**
+ * @return the motionState
+ */
+ public RigidBodyMotionState getMotionState() {
+ return motionState;
+ }
+
+ /**
+ * Sets the physics object location
+ * @param location the location of the actual physics object
+ */
+ public void setPhysicsLocation(Vector3f location) {
+ rBody.getCenterOfMassTransform(tempTrans);
+ Converter.convert(location, tempTrans.origin);
+ rBody.setCenterOfMassTransform(tempTrans);
+ motionState.setWorldTransform(tempTrans);
+ }
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Matrix3f rotation) {
+ rBody.getCenterOfMassTransform(tempTrans);
+ Converter.convert(rotation, tempTrans.basis);
+ rBody.setCenterOfMassTransform(tempTrans);
+ motionState.setWorldTransform(tempTrans);
+ }
+
+ /**
+ * Sets the physics object rotation
+ * @param rotation the rotation of the actual physics object
+ */
+ public void setPhysicsRotation(Quaternion rotation) {
+ rBody.getCenterOfMassTransform(tempTrans);
+ Converter.convert(rotation, tempTrans.basis);
+ rBody.setCenterOfMassTransform(tempTrans);
+ motionState.setWorldTransform(tempTrans);
+ }
+
+ /**
+ * Gets the physics object location, instantiates a new Vector3f object
+ */
+ public Vector3f getPhysicsLocation() {
+ return getPhysicsLocation(null);
+ }
+
+ /**
+ * Gets the physics object rotation
+ */
+ public Matrix3f getPhysicsRotationMatrix() {
+ return getPhysicsRotationMatrix(null);
+ }
+
+ /**
+ * Gets the physics object location, no object instantiation
+ * @param location the location of the actual physics object is stored in this Vector3f
+ */
+ public Vector3f getPhysicsLocation(Vector3f location) {
+ if (location == null) {
+ location = new Vector3f();
+ }
+ rBody.getCenterOfMassTransform(tempTrans);
+ return Converter.convert(tempTrans.origin, location);
+ }
+
+ /**
+ * Gets the physics object rotation as a matrix, no conversions and no object instantiation
+ * @param rotation the rotation of the actual physics object is stored in this Matrix3f
+ */
+ public Matrix3f getPhysicsRotationMatrix(Matrix3f rotation) {
+ if (rotation == null) {
+ rotation = new Matrix3f();
+ }
+ rBody.getCenterOfMassTransform(tempTrans);
+ return Converter.convert(tempTrans.basis, rotation);
+ }
+
+ /**
+ * Gets the physics object rotation as a quaternion, converts the bullet Matrix3f value,
+ * instantiates new object
+ */
+ public Quaternion getPhysicsRotation(){
+ return getPhysicsRotation(null);
+ }
+
+ /**
+ * Gets the physics object rotation as a quaternion, converts the bullet Matrix3f value
+ * @param rotation the rotation of the actual physics object is stored in this Quaternion
+ */
+ public Quaternion getPhysicsRotation(Quaternion rotation){
+ if (rotation == null) {
+ rotation = new Quaternion();
+ }
+ rBody.getCenterOfMassTransform(tempTrans);
+ return Converter.convert(tempTrans.basis, rotation);
+ }
+
+ /**
+ * Gets the physics object location
+ * @param location the location of the actual physics object is stored in this Vector3f
+ */
+ public Vector3f getInterpolatedPhysicsLocation(Vector3f location) {
+ if (location == null) {
+ location = new Vector3f();
+ }
+ rBody.getInterpolationWorldTransform(tempTrans);
+ return Converter.convert(tempTrans.origin, location);
+ }
+
+ /**
+ * Gets the physics object rotation
+ * @param rotation the rotation of the actual physics object is stored in this Matrix3f
+ */
+ public Matrix3f getInterpolatedPhysicsRotation(Matrix3f rotation) {
+ if (rotation == null) {
+ rotation = new Matrix3f();
+ }
+ rBody.getInterpolationWorldTransform(tempTrans);
+ return Converter.convert(tempTrans.basis, rotation);
+ }
+
+ /**
+ * Sets the node to kinematic mode. in this mode the node is not affected by physics
+ * but affects other physics objects. Its kinetic force is calculated by the amount
+ * of movement it is exposed to and its weight.
+ * @param kinematic
+ */
+ public void setKinematic(boolean kinematic) {
+ this.kinematic = kinematic;
+ if (kinematic) {
+ rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.KINEMATIC_OBJECT);
+ rBody.setActivationState(com.bulletphysics.collision.dispatch.CollisionObject.DISABLE_DEACTIVATION);
+ } else {
+ rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.KINEMATIC_OBJECT);
+ rBody.setActivationState(com.bulletphysics.collision.dispatch.CollisionObject.ACTIVE_TAG);
+ }
+ }
+
+ public boolean isKinematic() {
+ return kinematic;
+ }
+
+ public void setCcdSweptSphereRadius(float radius) {
+ rBody.setCcdSweptSphereRadius(radius);
+ }
+
+ /**
+ * Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection<br/>
+ * This avoids the problem of fast objects moving through other objects, set to zero to disable (default)
+ * @param threshold
+ */
+ public void setCcdMotionThreshold(float threshold) {
+ rBody.setCcdMotionThreshold(threshold);
+ }
+
+ public float getCcdSweptSphereRadius() {
+ return rBody.getCcdSweptSphereRadius();
+ }
+
+ public float getCcdMotionThreshold() {
+ return rBody.getCcdMotionThreshold();
+ }
+
+ public float getCcdSquareMotionThreshold() {
+ return rBody.getCcdSquareMotionThreshold();
+ }
+
+ public float getMass() {
+ return mass;
+ }
+
+ /**
+ * Sets the mass of this PhysicsRigidBody, objects with mass=0 are static.
+ * @param mass
+ */
+ public void setMass(float mass) {
+ this.mass = mass;
+ if(collisionShape instanceof MeshCollisionShape && mass != 0){
+ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!");
+ }
+ if (collisionShape != null) {
+ collisionShape.calculateLocalInertia(mass, localInertia);
+ }
+ if (rBody != null) {
+ rBody.setMassProps(mass, localInertia);
+ if (mass == 0.0f) {
+ rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.STATIC_OBJECT);
+ } else {
+ rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.STATIC_OBJECT);
+ }
+ }
+ }
+
+ public Vector3f getGravity() {
+ return getGravity(null);
+ }
+
+ public Vector3f getGravity(Vector3f gravity) {
+ if (gravity == null) {
+ gravity = new Vector3f();
+ }
+ rBody.getGravity(tempVec);
+ return Converter.convert(tempVec, gravity);
+ }
+
+ /**
+ * Set the local gravity of this PhysicsRigidBody<br/>
+ * Set this after adding the node to the PhysicsSpace,
+ * the PhysicsSpace assigns its current gravity to the physics node when its added.
+ * @param gravity the gravity vector to set
+ */
+ public void setGravity(Vector3f gravity) {
+ rBody.setGravity(Converter.convert(gravity, tempVec));
+ }
+
+ public float getFriction() {
+ return rBody.getFriction();
+ }
+
+ /**
+ * Sets the friction of this physics object
+ * @param friction the friction of this physics object
+ */
+ public void setFriction(float friction) {
+ constructionInfo.friction = friction;
+ rBody.setFriction(friction);
+ }
+
+ public void setDamping(float linearDamping, float angularDamping) {
+ constructionInfo.linearDamping = linearDamping;
+ constructionInfo.angularDamping = angularDamping;
+ rBody.setDamping(linearDamping, angularDamping);
+ }
+
+ public void setLinearDamping(float linearDamping) {
+ constructionInfo.linearDamping = linearDamping;
+ rBody.setDamping(linearDamping, constructionInfo.angularDamping);
+ }
+
+ public void setAngularDamping(float angularDamping) {
+ constructionInfo.angularDamping = angularDamping;
+ rBody.setDamping(constructionInfo.linearDamping, angularDamping);
+ }
+
+ public float getLinearDamping() {
+ return constructionInfo.linearDamping;
+ }
+
+ public float getAngularDamping() {
+ return constructionInfo.angularDamping;
+ }
+
+ public float getRestitution() {
+ return rBody.getRestitution();
+ }
+
+ /**
+ * The "bouncyness" of the PhysicsRigidBody, best performance if restitution=0
+ * @param restitution
+ */
+ public void setRestitution(float restitution) {
+ constructionInfo.restitution = restitution;
+ rBody.setRestitution(restitution);
+ }
+
+ /**
+ * Get the current angular velocity of this PhysicsRigidBody
+ * @return the current linear velocity
+ */
+ public Vector3f getAngularVelocity() {
+ return Converter.convert(rBody.getAngularVelocity(tempVec));
+ }
+
+ /**
+ * Get the current angular velocity of this PhysicsRigidBody
+ * @param vec the vector to store the velocity in
+ */
+ public void getAngularVelocity(Vector3f vec) {
+ Converter.convert(rBody.getAngularVelocity(tempVec), vec);
+ }
+
+ /**
+ * Sets the angular velocity of this PhysicsRigidBody
+ * @param vec the angular velocity of this PhysicsRigidBody
+ */
+ public void setAngularVelocity(Vector3f vec) {
+ rBody.setAngularVelocity(Converter.convert(vec, tempVec));
+ rBody.activate();
+ }
+
+ /**
+ * Get the current linear velocity of this PhysicsRigidBody
+ * @return the current linear velocity
+ */
+ public Vector3f getLinearVelocity() {
+ return Converter.convert(rBody.getLinearVelocity(tempVec));
+ }
+
+ /**
+ * Get the current linear velocity of this PhysicsRigidBody
+ * @param vec the vector to store the velocity in
+ */
+ public void getLinearVelocity(Vector3f vec) {
+ Converter.convert(rBody.getLinearVelocity(tempVec), vec);
+ }
+
+ /**
+ * Sets the linear velocity of this PhysicsRigidBody
+ * @param vec the linear velocity of this PhysicsRigidBody
+ */
+ public void setLinearVelocity(Vector3f vec) {
+ rBody.setLinearVelocity(Converter.convert(vec, tempVec));
+ rBody.activate();
+ }
+
+ /**
+ * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call
+ * updates the physics space.<br>
+ * To apply an impulse, use applyImpulse, use applyContinuousForce to apply continuous force.
+ * @param force the force
+ * @param location the location of the force
+ */
+ public void applyForce(final Vector3f force, final Vector3f location) {
+ rBody.applyForce(Converter.convert(force, tempVec), Converter.convert(location, tempVec2));
+ rBody.activate();
+ }
+
+ /**
+ * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call
+ * updates the physics space.<br>
+ * To apply an impulse, use applyImpulse.
+ *
+ * @param force the force
+ */
+ public void applyCentralForce(final Vector3f force) {
+ rBody.applyCentralForce(Converter.convert(force, tempVec));
+ rBody.activate();
+ }
+
+ /**
+ * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call
+ * updates the physics space.<br>
+ * To apply an impulse, use applyImpulse.
+ *
+ * @param torque the torque
+ */
+ public void applyTorque(final Vector3f torque) {
+ rBody.applyTorque(Converter.convert(torque, tempVec));
+ rBody.activate();
+ }
+
+ /**
+ * Apply an impulse to the PhysicsRigidBody in the next physics update.
+ * @param impulse applied impulse
+ * @param rel_pos location relative to object
+ */
+ public void applyImpulse(final Vector3f impulse, final Vector3f rel_pos) {
+ rBody.applyImpulse(Converter.convert(impulse, tempVec), Converter.convert(rel_pos, tempVec2));
+ rBody.activate();
+ }
+
+ /**
+ * Apply a torque impulse to the PhysicsRigidBody in the next physics update.
+ * @param vec
+ */
+ public void applyTorqueImpulse(final Vector3f vec) {
+ rBody.applyTorqueImpulse(Converter.convert(vec, tempVec));
+ rBody.activate();
+ }
+
+ /**
+ * Clear all forces from the PhysicsRigidBody
+ *
+ */
+ public void clearForces() {
+ rBody.clearForces();
+ }
+
+ public void setCollisionShape(CollisionShape collisionShape) {
+ super.setCollisionShape(collisionShape);
+ if(collisionShape instanceof MeshCollisionShape && mass!=0){
+ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!");
+ }
+ if (rBody == null) {
+ rebuildRigidBody();
+ } else {
+ collisionShape.calculateLocalInertia(mass, localInertia);
+ constructionInfo.collisionShape = collisionShape.getCShape();
+ rBody.setCollisionShape(collisionShape.getCShape());
+ }
+ }
+
+ /**
+ * reactivates this PhysicsRigidBody when it has been deactivated because it was not moving
+ */
+ public void activate() {
+ rBody.activate();
+ }
+
+ public boolean isActive() {
+ return rBody.isActive();
+ }
+
+ /**
+ * sets the sleeping thresholds, these define when the object gets deactivated
+ * to save ressources. Low values keep the object active when it barely moves
+ * @param linear the linear sleeping threshold
+ * @param angular the angular sleeping threshold
+ */
+ public void setSleepingThresholds(float linear, float angular) {
+ constructionInfo.linearSleepingThreshold = linear;
+ constructionInfo.angularSleepingThreshold = angular;
+ rBody.setSleepingThresholds(linear, angular);
+ }
+
+ public void setLinearSleepingThreshold(float linearSleepingThreshold) {
+ constructionInfo.linearSleepingThreshold = linearSleepingThreshold;
+ rBody.setSleepingThresholds(linearSleepingThreshold, constructionInfo.angularSleepingThreshold);
+ }
+
+ public void setAngularSleepingThreshold(float angularSleepingThreshold) {
+ constructionInfo.angularSleepingThreshold = angularSleepingThreshold;
+ rBody.setSleepingThresholds(constructionInfo.linearSleepingThreshold, angularSleepingThreshold);
+ }
+
+ public float getLinearSleepingThreshold() {
+ return constructionInfo.linearSleepingThreshold;
+ }
+
+ public float getAngularSleepingThreshold() {
+ return constructionInfo.angularSleepingThreshold;
+ }
+
+ public float getAngularFactor() {
+ return rBody.getAngularFactor();
+ }
+
+ public void setAngularFactor(float factor) {
+ rBody.setAngularFactor(factor);
+ }
+
+ /**
+ * do not use manually, joints are added automatically
+ */
+ public void addJoint(PhysicsJoint joint) {
+ if (!joints.contains(joint)) {
+ joints.add(joint);
+ }
+ updateDebugShape();
+ }
+
+ /**
+ *
+ */
+ public void removeJoint(PhysicsJoint joint) {
+ joints.remove(joint);
+ }
+
+ /**
+ * Returns a list of connected joints. This list is only filled when
+ * the PhysicsRigidBody is actually added to the physics space or loaded from disk.
+ * @return list of active joints connected to this PhysicsRigidBody
+ */
+ public List<PhysicsJoint> getJoints() {
+ return joints;
+ }
+
+ /**
+ * used internally
+ */
+ public RigidBody getObjectId() {
+ return rBody;
+ }
+
+ /**
+ * destroys this PhysicsRigidBody and removes it from memory
+ */
+ public void destroy() {
+ rBody.destroy();
+ }
+
+ @Override
+ protected Spatial getDebugShape() {
+ //add joints
+ Spatial shape = super.getDebugShape();
+ Node node = null;
+ if (shape instanceof Node) {
+ node = (Node) shape;
+ } else {
+ node = new Node("DebugShapeNode");
+ node.attachChild(shape);
+ }
+ int i = 0;
+ for (Iterator<PhysicsJoint> it = joints.iterator(); it.hasNext();) {
+ PhysicsJoint physicsJoint = it.next();
+ Vector3f pivot = null;
+ if (physicsJoint.getBodyA() == this) {
+ pivot = physicsJoint.getPivotA();
+ } else {
+ pivot = physicsJoint.getPivotB();
+ }
+ Arrow arrow = new Arrow(pivot);
+ Geometry geom = new Geometry("DebugBone" + i, arrow);
+ geom.setMaterial(debugMaterialGreen);
+ node.attachChild(geom);
+ i++;
+ }
+ return node;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+
+ capsule.write(getMass(), "mass", 1.0f);
+
+ capsule.write(getGravity(), "gravity", Vector3f.ZERO);
+ capsule.write(getFriction(), "friction", 0.5f);
+ capsule.write(getRestitution(), "restitution", 0);
+ capsule.write(getAngularFactor(), "angularFactor", 1);
+ capsule.write(kinematic, "kinematic", false);
+
+ capsule.write(constructionInfo.linearDamping, "linearDamping", 0);
+ capsule.write(constructionInfo.angularDamping, "angularDamping", 0);
+ capsule.write(constructionInfo.linearSleepingThreshold, "linearSleepingThreshold", 0.8f);
+ capsule.write(constructionInfo.angularSleepingThreshold, "angularSleepingThreshold", 1.0f);
+
+ capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
+ capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
+
+ capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
+ capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f());
+
+ capsule.writeSavableArrayList(joints, "joints", null);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+
+ InputCapsule capsule = e.getCapsule(this);
+ float mass = capsule.readFloat("mass", 1.0f);
+ this.mass = mass;
+ rebuildRigidBody();
+ setGravity((Vector3f) capsule.readSavable("gravity", Vector3f.ZERO.clone()));
+ setFriction(capsule.readFloat("friction", 0.5f));
+ setKinematic(capsule.readBoolean("kinematic", false));
+
+ setRestitution(capsule.readFloat("restitution", 0));
+ setAngularFactor(capsule.readFloat("angularFactor", 1));
+ setDamping(capsule.readFloat("linearDamping", 0), capsule.readFloat("angularDamping", 0));
+ setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f));
+ setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
+ setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
+
+ setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
+ setPhysicsRotation((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f()));
+
+ joints = capsule.readSavableArrayList("joints", null);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java
new file mode 100644
index 0000000..eacf534
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.bulletphysics.collision.dispatch.CollisionObject;
+import com.bulletphysics.dynamics.vehicle.*;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * <p>PhysicsVehicleNode - Special PhysicsNode that implements vehicle functions</p>
+ * <p>
+ * <i>From bullet manual:</i><br>
+ * For most vehicle simulations, it is recommended to use the simplified Bullet
+ * vehicle model as provided in btRaycastVehicle. Instead of simulation each wheel
+ * and chassis as separate rigid bodies, connected by constraints, it uses a simplified model.
+ * This simplified model has many benefits, and is widely used in commercial driving games.<br>
+ * The entire vehicle is represented as a single rigidbody, the chassis.
+ * The collision detection of the wheels is approximated by ray casts,
+ * and the tire friction is a basic anisotropic friction model.
+ * </p>
+ * @author normenhansen
+ */
+public class PhysicsVehicle extends PhysicsRigidBody {
+
+ protected RaycastVehicle vehicle;
+ protected VehicleTuning tuning;
+ protected VehicleRaycaster rayCaster;
+ protected ArrayList<VehicleWheel> wheels = new ArrayList<VehicleWheel>();
+ protected PhysicsSpace physicsSpace;
+
+ public PhysicsVehicle() {
+ }
+
+ public PhysicsVehicle(CollisionShape shape) {
+ super(shape);
+ }
+
+ public PhysicsVehicle(CollisionShape shape, float mass) {
+ super(shape, mass);
+ }
+
+ /**
+ * used internally
+ */
+ public void updateWheels() {
+ if (vehicle != null) {
+ for (int i = 0; i < wheels.size(); i++) {
+ vehicle.updateWheelTransform(i, true);
+ wheels.get(i).updatePhysicsState();
+ }
+ }
+ }
+
+ /**
+ * used internally
+ */
+ public void applyWheelTransforms() {
+ if (wheels != null) {
+ for (int i = 0; i < wheels.size(); i++) {
+ wheels.get(i).applyWheelTransform();
+ }
+ }
+ }
+
+ @Override
+ protected void postRebuild() {
+ super.postRebuild();
+ if (tuning == null) {
+ tuning = new VehicleTuning();
+ }
+ rBody.setActivationState(CollisionObject.DISABLE_DEACTIVATION);
+ motionState.setVehicle(this);
+ if (physicsSpace != null) {
+ createVehicle(physicsSpace);
+ }
+ }
+
+ /**
+ * Used internally, creates the actual vehicle constraint when vehicle is added to phyicsspace
+ */
+ public void createVehicle(PhysicsSpace space) {
+ physicsSpace = space;
+ if (space == null) {
+ return;
+ }
+ rayCaster = new DefaultVehicleRaycaster(space.getDynamicsWorld());
+ vehicle = new RaycastVehicle(tuning, rBody, rayCaster);
+ vehicle.setCoordinateSystem(0, 1, 2);
+ for (VehicleWheel wheel : wheels) {
+ wheel.setWheelInfo(vehicle.addWheel(Converter.convert(wheel.getLocation()), Converter.convert(wheel.getDirection()), Converter.convert(wheel.getAxle()),
+ wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel()));
+ }
+ }
+
+ /**
+ * Add a wheel to this vehicle
+ * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space)
+ * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car)
+ * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car)
+ * @param suspensionRestLength The current length of the suspension (metres)
+ * @param wheelRadius the wheel radius
+ * @param isFrontWheel sets if this wheel is a front wheel (steering)
+ * @return the PhysicsVehicleWheel object to get/set infos on the wheel
+ */
+ public VehicleWheel addWheel(Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) {
+ return addWheel(null, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel);
+ }
+
+ /**
+ * Add a wheel to this vehicle
+ * @param spat the wheel Geometry
+ * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space)
+ * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car)
+ * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car)
+ * @param suspensionRestLength The current length of the suspension (metres)
+ * @param wheelRadius the wheel radius
+ * @param isFrontWheel sets if this wheel is a front wheel (steering)
+ * @return the PhysicsVehicleWheel object to get/set infos on the wheel
+ */
+ public VehicleWheel addWheel(Spatial spat, Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) {
+ VehicleWheel wheel = null;
+ if (spat == null) {
+ wheel = new VehicleWheel(connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel);
+ } else {
+ wheel = new VehicleWheel(spat, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel);
+ }
+ if (vehicle != null) {
+ WheelInfo info = vehicle.addWheel(Converter.convert(connectionPoint), Converter.convert(direction), Converter.convert(axle),
+ suspensionRestLength, wheelRadius, tuning, isFrontWheel);
+ wheel.setWheelInfo(info);
+ }
+ wheel.setFrictionSlip(tuning.frictionSlip);
+ wheel.setMaxSuspensionTravelCm(tuning.maxSuspensionTravelCm);
+ wheel.setSuspensionStiffness(tuning.suspensionStiffness);
+ wheel.setWheelsDampingCompression(tuning.suspensionCompression);
+ wheel.setWheelsDampingRelaxation(tuning.suspensionDamping);
+ wheel.setMaxSuspensionForce(tuning.maxSuspensionForce);
+ wheels.add(wheel);
+ if (debugShape != null) {
+ detachDebugShape();
+ }
+// updateDebugShape();
+ return wheel;
+ }
+
+ /**
+ * This rebuilds the vehicle as there is no way in bullet to remove a wheel.
+ * @param wheel
+ */
+ public void removeWheel(int wheel) {
+ wheels.remove(wheel);
+ rebuildRigidBody();
+// updateDebugShape();
+ }
+
+ /**
+ * You can get access to the single wheels via this method.
+ * @param wheel the wheel index
+ * @return the WheelInfo of the selected wheel
+ */
+ public VehicleWheel getWheel(int wheel) {
+ return wheels.get(wheel);
+ }
+
+ public int getNumWheels() {
+ return wheels.size();
+ }
+
+ /**
+ * @return the frictionSlip
+ */
+ public float getFrictionSlip() {
+ return tuning.frictionSlip;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The coefficient of friction between the tyre and the ground.
+ * Should be about 0.8 for realistic cars, but can increased for better handling.
+ * Set large (10000.0) for kart racers
+ * @param frictionSlip the frictionSlip to set
+ */
+ public void setFrictionSlip(float frictionSlip) {
+ tuning.frictionSlip = frictionSlip;
+ }
+
+ /**
+ * The coefficient of friction between the tyre and the ground.
+ * Should be about 0.8 for realistic cars, but can increased for better handling.
+ * Set large (10000.0) for kart racers
+ * @param wheel
+ * @param frictionSlip
+ */
+ public void setFrictionSlip(int wheel, float frictionSlip) {
+ wheels.get(wheel).setFrictionSlip(frictionSlip);
+ }
+
+ /**
+ * Reduces the rolling torque applied from the wheels that cause the vehicle to roll over.
+ * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour.
+ * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over.
+ * You should also try lowering the vehicle's centre of mass
+ */
+ public void setRollInfluence(int wheel, float rollInfluence) {
+ wheels.get(wheel).setRollInfluence(rollInfluence);
+ }
+
+ /**
+ * @return the maxSuspensionTravelCm
+ */
+ public float getMaxSuspensionTravelCm() {
+ return tuning.maxSuspensionTravelCm;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The maximum distance the suspension can be compressed (centimetres)
+ * @param maxSuspensionTravelCm the maxSuspensionTravelCm to set
+ */
+ public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) {
+ tuning.maxSuspensionTravelCm = maxSuspensionTravelCm;
+ }
+
+ /**
+ * The maximum distance the suspension can be compressed (centimetres)
+ * @param wheel
+ * @param maxSuspensionTravelCm
+ */
+ public void setMaxSuspensionTravelCm(int wheel, float maxSuspensionTravelCm) {
+ wheels.get(wheel).setMaxSuspensionTravelCm(maxSuspensionTravelCm);
+ }
+
+ public float getMaxSuspensionForce() {
+ return tuning.maxSuspensionForce;
+ }
+
+ /**
+ * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
+ * handle the weight of your vehcile.
+ * @param maxSuspensionForce
+ */
+ public void setMaxSuspensionForce(float maxSuspensionForce) {
+ tuning.maxSuspensionForce = maxSuspensionForce;
+ }
+
+ /**
+ * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
+ * handle the weight of your vehcile.
+ * @param wheel
+ * @param maxSuspensionForce
+ */
+ public void setMaxSuspensionForce(int wheel, float maxSuspensionForce) {
+ wheels.get(wheel).setMaxSuspensionForce(maxSuspensionForce);
+ }
+
+ /**
+ * @return the suspensionCompression
+ */
+ public float getSuspensionCompression() {
+ return tuning.suspensionCompression;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The damping coefficient for when the suspension is compressed.
+ * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
+ * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
+ * 0.1 to 0.3 are good values
+ * @param suspensionCompression the suspensionCompression to set
+ */
+ public void setSuspensionCompression(float suspensionCompression) {
+ tuning.suspensionCompression = suspensionCompression;
+ }
+
+ /**
+ * The damping coefficient for when the suspension is compressed.
+ * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
+ * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
+ * 0.1 to 0.3 are good values
+ * @param wheel
+ * @param suspensionCompression
+ */
+ public void setSuspensionCompression(int wheel, float suspensionCompression) {
+ wheels.get(wheel).setWheelsDampingCompression(suspensionCompression);
+ }
+
+ /**
+ * @return the suspensionDamping
+ */
+ public float getSuspensionDamping() {
+ return tuning.suspensionDamping;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The damping coefficient for when the suspension is expanding.
+ * See the comments for setSuspensionCompression for how to set k.
+ * @param suspensionDamping the suspensionDamping to set
+ */
+ public void setSuspensionDamping(float suspensionDamping) {
+ tuning.suspensionDamping = suspensionDamping;
+ }
+
+ /**
+ * The damping coefficient for when the suspension is expanding.
+ * See the comments for setSuspensionCompression for how to set k.
+ * @param wheel
+ * @param suspensionDamping
+ */
+ public void setSuspensionDamping(int wheel, float suspensionDamping) {
+ wheels.get(wheel).setWheelsDampingRelaxation(suspensionDamping);
+ }
+
+ /**
+ * @return the suspensionStiffness
+ */
+ public float getSuspensionStiffness() {
+ return tuning.suspensionStiffness;
+ }
+
+ /**
+ * Use before adding wheels, this is the default used when adding wheels.
+ * After adding the wheel, use direct wheel access.<br>
+ * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
+ * @param suspensionStiffness
+ */
+ public void setSuspensionStiffness(float suspensionStiffness) {
+ tuning.suspensionStiffness = suspensionStiffness;
+ }
+
+ /**
+ * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
+ * @param wheel
+ * @param suspensionStiffness
+ */
+ public void setSuspensionStiffness(int wheel, float suspensionStiffness) {
+ wheels.get(wheel).setSuspensionStiffness(suspensionStiffness);
+ }
+
+ /**
+ * Reset the suspension
+ */
+ public void resetSuspension() {
+ vehicle.resetSuspension();
+ }
+
+ /**
+ * Apply the given engine force to all wheels, works continuously
+ * @param force the force
+ */
+ public void accelerate(float force) {
+ for (int i = 0; i < wheels.size(); i++) {
+ vehicle.applyEngineForce(force, i);
+ }
+ }
+
+ /**
+ * Apply the given engine force, works continuously
+ * @param wheel the wheel to apply the force on
+ * @param force the force
+ */
+ public void accelerate(int wheel, float force) {
+ vehicle.applyEngineForce(force, wheel);
+ }
+
+ /**
+ * Set the given steering value to all front wheels (0 = forward)
+ * @param value the steering angle of the front wheels (Pi = 360deg)
+ */
+ public void steer(float value) {
+ for (int i = 0; i < wheels.size(); i++) {
+ if (getWheel(i).isFrontWheel()) {
+ vehicle.setSteeringValue(value, i);
+ }
+ }
+ }
+
+ /**
+ * Set the given steering value to the given wheel (0 = forward)
+ * @param wheel the wheel to set the steering on
+ * @param value the steering angle of the front wheels (Pi = 360deg)
+ */
+ public void steer(int wheel, float value) {
+ vehicle.setSteeringValue(value, wheel);
+ }
+
+ /**
+ * Apply the given brake force to all wheels, works continuously
+ * @param force the force
+ */
+ public void brake(float force) {
+ for (int i = 0; i < wheels.size(); i++) {
+ vehicle.setBrake(force, i);
+ }
+ }
+
+ /**
+ * Apply the given brake force, works continuously
+ * @param wheel the wheel to apply the force on
+ * @param force the force
+ */
+ public void brake(int wheel, float force) {
+ vehicle.setBrake(force, wheel);
+ }
+
+ /**
+ * Get the current speed of the vehicle in km/h
+ * @return
+ */
+ public float getCurrentVehicleSpeedKmHour() {
+ return vehicle.getCurrentSpeedKmHour();
+ }
+
+ /**
+ * Get the current forward vector of the vehicle in world coordinates
+ * @param vector
+ * @return
+ */
+ public Vector3f getForwardVector(Vector3f vector) {
+ if (vector == null) {
+ vector = new Vector3f();
+ }
+ vehicle.getForwardVector(tempVec);
+ Converter.convert(tempVec, vector);
+ return vector;
+ }
+
+ /**
+ * used internally
+ */
+ public RaycastVehicle getVehicleId() {
+ return vehicle;
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ }
+
+ @Override
+ protected Spatial getDebugShape() {
+ Spatial shape = super.getDebugShape();
+ Node node = null;
+ if (shape instanceof Node) {
+ node = (Node) shape;
+ } else {
+ node = new Node("DebugShapeNode");
+ node.attachChild(shape);
+ }
+ int i = 0;
+ for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
+ VehicleWheel physicsVehicleWheel = it.next();
+ Vector3f location = physicsVehicleWheel.getLocation().clone();
+ Vector3f direction = physicsVehicleWheel.getDirection().clone();
+ Vector3f axle = physicsVehicleWheel.getAxle().clone();
+ float restLength = physicsVehicleWheel.getRestLength();
+ float radius = physicsVehicleWheel.getRadius();
+
+ Arrow locArrow = new Arrow(location);
+ Arrow axleArrow = new Arrow(axle.normalizeLocal().multLocal(0.3f));
+ Arrow wheelArrow = new Arrow(direction.normalizeLocal().multLocal(radius));
+ Arrow dirArrow = new Arrow(direction.normalizeLocal().multLocal(restLength));
+ Geometry locGeom = new Geometry("WheelLocationDebugShape" + i, locArrow);
+ Geometry dirGeom = new Geometry("WheelDirectionDebugShape" + i, dirArrow);
+ Geometry axleGeom = new Geometry("WheelAxleDebugShape" + i, axleArrow);
+ Geometry wheelGeom = new Geometry("WheelRadiusDebugShape" + i, wheelArrow);
+ dirGeom.setLocalTranslation(location);
+ axleGeom.setLocalTranslation(location.add(direction));
+ wheelGeom.setLocalTranslation(location.add(direction));
+ locGeom.setMaterial(debugMaterialGreen);
+ dirGeom.setMaterial(debugMaterialGreen);
+ axleGeom.setMaterial(debugMaterialGreen);
+ wheelGeom.setMaterial(debugMaterialGreen);
+ node.attachChild(locGeom);
+ node.attachChild(dirGeom);
+ node.attachChild(axleGeom);
+ node.attachChild(wheelGeom);
+ i++;
+ }
+ return node;
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ tuning = new VehicleTuning();
+ tuning.frictionSlip = capsule.readFloat("frictionSlip", 10.5f);
+ tuning.maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f);
+ tuning.maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f);
+ tuning.suspensionCompression = capsule.readFloat("suspensionCompression", 0.83f);
+ tuning.suspensionDamping = capsule.readFloat("suspensionDamping", 0.88f);
+ tuning.suspensionStiffness = capsule.readFloat("suspensionStiffness", 5.88f);
+ wheels = capsule.readSavableArrayList("wheelsList", new ArrayList<VehicleWheel>());
+ motionState.setVehicle(this);
+ super.read(im);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(tuning.frictionSlip, "frictionSlip", 10.5f);
+ capsule.write(tuning.maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f);
+ capsule.write(tuning.maxSuspensionForce, "maxSuspensionForce", 6000f);
+ capsule.write(tuning.suspensionCompression, "suspensionCompression", 0.83f);
+ capsule.write(tuning.suspensionDamping, "suspensionDamping", 0.88f);
+ capsule.write(tuning.suspensionStiffness, "suspensionStiffness", 5.88f);
+ capsule.writeSavableArrayList(wheels, "wheelsList", new ArrayList<VehicleWheel>());
+ super.write(ex);
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java b/engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java
new file mode 100644
index 0000000..7ac6bb0
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects;
+
+import com.bulletphysics.dynamics.RigidBody;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.util.Converter;
+import com.jme3.export.*;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+
+/**
+ * Stores info about one wheel of a PhysicsVehicle
+ * @author normenhansen
+ */
+public class VehicleWheel implements Savable {
+
+ protected com.bulletphysics.dynamics.vehicle.WheelInfo wheelInfo;
+ protected boolean frontWheel;
+ protected Vector3f location = new Vector3f();
+ protected Vector3f direction = new Vector3f();
+ protected Vector3f axle = new Vector3f();
+ protected float suspensionStiffness = 20.0f;
+ protected float wheelsDampingRelaxation = 2.3f;
+ protected float wheelsDampingCompression = 4.4f;
+ protected float frictionSlip = 10.5f;
+ protected float rollInfluence = 1.0f;
+ protected float maxSuspensionTravelCm = 500f;
+ protected float maxSuspensionForce = 6000f;
+ protected float radius = 0.5f;
+ protected float restLength = 1f;
+ protected Vector3f wheelWorldLocation = new Vector3f();
+ protected Quaternion wheelWorldRotation = new Quaternion();
+ protected Spatial wheelSpatial;
+ protected com.jme3.math.Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f();
+ protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
+ private boolean applyLocal = false;
+
+ public VehicleWheel() {
+ }
+
+ public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle,
+ float restLength, float radius, boolean frontWheel) {
+ this(location, direction, axle, restLength, radius, frontWheel);
+ wheelSpatial = spat;
+ }
+
+ public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle,
+ float restLength, float radius, boolean frontWheel) {
+ this.location.set(location);
+ this.direction.set(direction);
+ this.axle.set(axle);
+ this.frontWheel = frontWheel;
+ this.restLength = restLength;
+ this.radius = radius;
+ }
+
+ public synchronized void updatePhysicsState() {
+ Converter.convert(wheelInfo.worldTransform.origin, wheelWorldLocation);
+ Converter.convert(wheelInfo.worldTransform.basis, tmp_Matrix);
+ wheelWorldRotation.fromRotationMatrix(tmp_Matrix);
+ }
+
+ public synchronized void applyWheelTransform() {
+ if (wheelSpatial == null) {
+ return;
+ }
+ Quaternion localRotationQuat = wheelSpatial.getLocalRotation();
+ Vector3f localLocation = wheelSpatial.getLocalTranslation();
+ if (!applyLocal && wheelSpatial.getParent() != null) {
+ localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation());
+ localLocation.divideLocal(wheelSpatial.getParent().getWorldScale());
+ tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
+
+ localRotationQuat.set(wheelWorldRotation);
+ tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat);
+
+ wheelSpatial.setLocalTranslation(localLocation);
+ wheelSpatial.setLocalRotation(localRotationQuat);
+ } else {
+ wheelSpatial.setLocalTranslation(wheelWorldLocation);
+ wheelSpatial.setLocalRotation(wheelWorldRotation);
+ }
+ }
+
+ public com.bulletphysics.dynamics.vehicle.WheelInfo getWheelInfo() {
+ return wheelInfo;
+ }
+
+ public void setWheelInfo(com.bulletphysics.dynamics.vehicle.WheelInfo wheelInfo) {
+ this.wheelInfo = wheelInfo;
+ applyInfo();
+ }
+
+ public boolean isFrontWheel() {
+ return frontWheel;
+ }
+
+ public void setFrontWheel(boolean frontWheel) {
+ this.frontWheel = frontWheel;
+ applyInfo();
+ }
+
+ public Vector3f getLocation() {
+ return location;
+ }
+
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ public Vector3f getAxle() {
+ return axle;
+ }
+
+ public float getSuspensionStiffness() {
+ return suspensionStiffness;
+ }
+
+ /**
+ * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
+ * @param suspensionStiffness
+ */
+ public void setSuspensionStiffness(float suspensionStiffness) {
+ this.suspensionStiffness = suspensionStiffness;
+ applyInfo();
+ }
+
+ public float getWheelsDampingRelaxation() {
+ return wheelsDampingRelaxation;
+ }
+
+ /**
+ * the damping coefficient for when the suspension is expanding.
+ * See the comments for setWheelsDampingCompression for how to set k.
+ * @param wheelsDampingRelaxation
+ */
+ public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) {
+ this.wheelsDampingRelaxation = wheelsDampingRelaxation;
+ applyInfo();
+ }
+
+ public float getWheelsDampingCompression() {
+ return wheelsDampingCompression;
+ }
+
+ /**
+ * the damping coefficient for when the suspension is compressed.
+ * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
+ * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
+ * 0.1 to 0.3 are good values
+ * @param wheelsDampingCompression
+ */
+ public void setWheelsDampingCompression(float wheelsDampingCompression) {
+ this.wheelsDampingCompression = wheelsDampingCompression;
+ applyInfo();
+ }
+
+ public float getFrictionSlip() {
+ return frictionSlip;
+ }
+
+ /**
+ * the coefficient of friction between the tyre and the ground.
+ * Should be about 0.8 for realistic cars, but can increased for better handling.
+ * Set large (10000.0) for kart racers
+ * @param frictionSlip
+ */
+ public void setFrictionSlip(float frictionSlip) {
+ this.frictionSlip = frictionSlip;
+ applyInfo();
+ }
+
+ public float getRollInfluence() {
+ return rollInfluence;
+ }
+
+ /**
+ * reduces the rolling torque applied from the wheels that cause the vehicle to roll over.
+ * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour.
+ * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over.
+ * You should also try lowering the vehicle's centre of mass
+ * @param rollInfluence the rollInfluence to set
+ */
+ public void setRollInfluence(float rollInfluence) {
+ this.rollInfluence = rollInfluence;
+ applyInfo();
+ }
+
+ public float getMaxSuspensionTravelCm() {
+ return maxSuspensionTravelCm;
+ }
+
+ /**
+ * the maximum distance the suspension can be compressed (centimetres)
+ * @param maxSuspensionTravelCm
+ */
+ public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) {
+ this.maxSuspensionTravelCm = maxSuspensionTravelCm;
+ applyInfo();
+ }
+
+ public float getMaxSuspensionForce() {
+ return maxSuspensionForce;
+ }
+
+ /**
+ * The maximum suspension force, raise this above the default 6000 if your suspension cannot
+ * handle the weight of your vehcile.
+ * @param maxSuspensionForce
+ */
+ public void setMaxSuspensionForce(float maxSuspensionForce) {
+ this.maxSuspensionForce = maxSuspensionForce;
+ applyInfo();
+ }
+
+ private void applyInfo() {
+ if (wheelInfo == null) {
+ return;
+ }
+ wheelInfo.suspensionStiffness = suspensionStiffness;
+ wheelInfo.wheelsDampingRelaxation = wheelsDampingRelaxation;
+ wheelInfo.wheelsDampingCompression = wheelsDampingCompression;
+ wheelInfo.frictionSlip = frictionSlip;
+ wheelInfo.rollInfluence = rollInfluence;
+ wheelInfo.maxSuspensionTravelCm = maxSuspensionTravelCm;
+ wheelInfo.maxSuspensionForce = maxSuspensionForce;
+ wheelInfo.wheelsRadius = radius;
+ wheelInfo.bIsFrontWheel = frontWheel;
+ wheelInfo.suspensionRestLength1 = restLength;
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void setRadius(float radius) {
+ this.radius = radius;
+ applyInfo();
+ }
+
+ public float getRestLength() {
+ return restLength;
+ }
+
+ public void setRestLength(float restLength) {
+ this.restLength = restLength;
+ applyInfo();
+ }
+
+ /**
+ * returns the object this wheel is in contact with or null if no contact
+ * @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject)
+ */
+ public PhysicsCollisionObject getGroundObject() {
+ if (wheelInfo.raycastInfo.groundObject == null) {
+ return null;
+ } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) {
+ System.out.println("RigidBody");
+ return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * returns the location where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionLocation(Vector3f vec) {
+ Converter.convert(wheelInfo.raycastInfo.contactPointWS, vec);
+ return vec;
+ }
+
+ /**
+ * returns the location where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionLocation() {
+ return Converter.convert(wheelInfo.raycastInfo.contactPointWS);
+ }
+
+ /**
+ * returns the normal where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionNormal(Vector3f vec) {
+ Converter.convert(wheelInfo.raycastInfo.contactNormalWS, vec);
+ return vec;
+ }
+
+ /**
+ * returns the normal where the wheel collides with the ground (world space)
+ */
+ public Vector3f getCollisionNormal() {
+ return Converter.convert(wheelInfo.raycastInfo.contactNormalWS);
+ }
+
+ /**
+ * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)<br>
+ * 0.0 = wheels are sliding, 1.0 = wheels have traction.
+ */
+ public float getSkidInfo() {
+ return wheelInfo.skidInfo;
+ }
+
+ /**
+ * returns how many degrees the wheel has turned since the last physics
+ * step.
+ */
+ public float getDeltaRotation() {
+ return wheelInfo.deltaRotation;
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule capsule = im.getCapsule(this);
+ wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null);
+ frontWheel = capsule.readBoolean("frontWheel", false);
+ location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f());
+ direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f());
+ axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f());
+ suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f);
+ wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f);
+ wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f);
+ frictionSlip = capsule.readFloat("frictionSlip", 10.5f);
+ rollInfluence = capsule.readFloat("rollInfluence", 1.0f);
+ maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f);
+ maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f);
+ radius = capsule.readFloat("wheelRadius", 0.5f);
+ restLength = capsule.readFloat("restLength", 1f);
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule capsule = ex.getCapsule(this);
+ capsule.write(wheelSpatial, "wheelSpatial", null);
+ capsule.write(frontWheel, "frontWheel", false);
+ capsule.write(location, "wheelLocation", new Vector3f());
+ capsule.write(direction, "wheelDirection", new Vector3f());
+ capsule.write(axle, "wheelAxle", new Vector3f());
+ capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f);
+ capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f);
+ capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f);
+ capsule.write(frictionSlip, "frictionSlip", 10.5f);
+ capsule.write(rollInfluence, "rollInfluence", 1.0f);
+ capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f);
+ capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f);
+ capsule.write(radius, "wheelRadius", 0.5f);
+ capsule.write(restLength, "restLength", 1f);
+ }
+
+ /**
+ * @return the wheelSpatial
+ */
+ public Spatial getWheelSpatial() {
+ return wheelSpatial;
+ }
+
+ /**
+ * @param wheelSpatial the wheelSpatial to set
+ */
+ public void setWheelSpatial(Spatial wheelSpatial) {
+ this.wheelSpatial = wheelSpatial;
+ }
+
+ public boolean isApplyLocal() {
+ return applyLocal;
+ }
+
+ public void setApplyLocal(boolean applyLocal) {
+ this.applyLocal = applyLocal;
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java b/engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java
new file mode 100644
index 0000000..8dd46bb
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.objects.infos;
+
+import com.bulletphysics.linearmath.MotionState;
+import com.bulletphysics.linearmath.Transform;
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.bullet.util.Converter;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+
+/**
+ * stores transform info of a PhysicsNode in a threadsafe manner to
+ * allow multithreaded access from the jme scenegraph and the bullet physicsspace
+ * @author normenhansen
+ */
+public class RigidBodyMotionState extends MotionState {
+ //stores the bullet transform
+
+ private Transform motionStateTrans = new Transform(Converter.convert(new Matrix3f()));
+ private Vector3f worldLocation = new Vector3f();
+ private Matrix3f worldRotation = new Matrix3f();
+ private Quaternion worldRotationQuat = new Quaternion();
+ private Vector3f localLocation = new Vector3f();
+ private Quaternion localRotationQuat = new Quaternion();
+ //keep track of transform changes
+ private boolean physicsLocationDirty = false;
+ private boolean jmeLocationDirty = false;
+ //temp variable for conversion
+ private Quaternion tmp_inverseWorldRotation = new Quaternion();
+ private PhysicsVehicle vehicle;
+ private boolean applyPhysicsLocal = false;
+// protected LinkedList<PhysicsMotionStateListener> listeners = new LinkedList<PhysicsMotionStateListener>();
+
+ public RigidBodyMotionState() {
+ }
+
+ /**
+ * called from bullet when creating the rigidbody
+ * @param t
+ * @return
+ */
+ public synchronized Transform getWorldTransform(Transform t) {
+ t.set(motionStateTrans);
+ return t;
+ }
+
+ /**
+ * called from bullet when the transform of the rigidbody changes
+ * @param worldTrans
+ */
+ public synchronized void setWorldTransform(Transform worldTrans) {
+ if (jmeLocationDirty) {
+ return;
+ }
+ motionStateTrans.set(worldTrans);
+ Converter.convert(worldTrans.origin, worldLocation);
+ Converter.convert(worldTrans.basis, worldRotation);
+ worldRotationQuat.fromRotationMatrix(worldRotation);
+// for (Iterator<PhysicsMotionStateListener> it = listeners.iterator(); it.hasNext();) {
+// PhysicsMotionStateListener physicsMotionStateListener = it.next();
+// physicsMotionStateListener.stateChanged(worldLocation, worldRotation);
+// }
+ physicsLocationDirty = true;
+ if (vehicle != null) {
+ vehicle.updateWheels();
+ }
+ }
+
+ /**
+ * applies the current transform to the given jme Node if the location has been updated on the physics side
+ * @param spatial
+ */
+ public synchronized boolean applyTransform(Spatial spatial) {
+ if (!physicsLocationDirty) {
+ return false;
+ }
+ if (!applyPhysicsLocal && spatial.getParent() != null) {
+ localLocation.set(worldLocation).subtractLocal(spatial.getParent().getWorldTranslation());
+ localLocation.divideLocal(spatial.getParent().getWorldScale());
+ tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
+
+ localRotationQuat.set(worldRotationQuat);
+ tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat);
+
+ spatial.setLocalTranslation(localLocation);
+ spatial.setLocalRotation(localRotationQuat);
+ } else {
+ spatial.setLocalTranslation(worldLocation);
+ spatial.setLocalRotation(worldRotationQuat);
+ }
+ physicsLocationDirty = false;
+ return true;
+ }
+
+ /**
+ * @return the worldLocation
+ */
+ public Vector3f getWorldLocation() {
+ return worldLocation;
+ }
+
+ /**
+ * @return the worldRotation
+ */
+ public Matrix3f getWorldRotation() {
+ return worldRotation;
+ }
+
+ /**
+ * @return the worldRotationQuat
+ */
+ public Quaternion getWorldRotationQuat() {
+ return worldRotationQuat;
+ }
+
+ /**
+ * @param vehicle the vehicle to set
+ */
+ public void setVehicle(PhysicsVehicle vehicle) {
+ this.vehicle = vehicle;
+ }
+
+ public boolean isApplyPhysicsLocal() {
+ return applyPhysicsLocal;
+ }
+
+ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
+ this.applyPhysicsLocal = applyPhysicsLocal;
+ }
+// public void addMotionStateListener(PhysicsMotionStateListener listener){
+// listeners.add(listener);
+// }
+//
+// public void removeMotionStateListener(PhysicsMotionStateListener listener){
+// listeners.remove(listener);
+// }
+// public synchronized boolean applyTransform(com.jme3.math.Transform trans) {
+// if (!physicsLocationDirty) {
+// return false;
+// }
+// trans.setTranslation(worldLocation);
+// trans.setRotation(worldRotationQuat);
+// physicsLocationDirty = false;
+// return true;
+// }
+//
+// /**
+// * called from jme when the location of the jme Node changes
+// * @param location
+// * @param rotation
+// */
+// public synchronized void setWorldTransform(Vector3f location, Quaternion rotation) {
+// worldLocation.set(location);
+// worldRotationQuat.set(rotation);
+// worldRotation.set(rotation.toRotationMatrix());
+// Converter.convert(worldLocation, motionStateTrans.origin);
+// Converter.convert(worldRotation, motionStateTrans.basis);
+// jmeLocationDirty = true;
+// }
+//
+// /**
+// * applies the current transform to the given RigidBody if the value has been changed on the jme side
+// * @param rBody
+// */
+// public synchronized void applyTransform(RigidBody rBody) {
+// if (!jmeLocationDirty) {
+// return;
+// }
+// assert (rBody != null);
+// rBody.setWorldTransform(motionStateTrans);
+// rBody.activate();
+// jmeLocationDirty = false;
+// }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/util/Converter.java b/engine/src/jbullet/com/jme3/bullet/util/Converter.java
new file mode 100644
index 0000000..15fb42c
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/util/Converter.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.util;
+
+import com.bulletphysics.collision.shapes.IndexedMesh;
+import com.bulletphysics.dom.HeightfieldTerrainShape;
+import com.jme3.math.FastMath;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+/**
+ * Nice convenience methods for conversion between javax.vecmath and com.jme3.math
+ * Objects, also some jme to jbullet mesh conversion.
+ * @author normenhansen
+ */
+public class Converter {
+
+ private Converter() {
+ }
+
+ public static com.jme3.math.Vector3f convert(javax.vecmath.Vector3f oldVec) {
+ com.jme3.math.Vector3f newVec = new com.jme3.math.Vector3f();
+ convert(oldVec, newVec);
+ return newVec;
+ }
+
+ public static com.jme3.math.Vector3f convert(javax.vecmath.Vector3f oldVec, com.jme3.math.Vector3f newVec) {
+ newVec.x = oldVec.x;
+ newVec.y = oldVec.y;
+ newVec.z = oldVec.z;
+ return newVec;
+ }
+
+ public static javax.vecmath.Vector3f convert(com.jme3.math.Vector3f oldVec) {
+ javax.vecmath.Vector3f newVec = new javax.vecmath.Vector3f();
+ convert(oldVec, newVec);
+ return newVec;
+ }
+
+ public static javax.vecmath.Vector3f convert(com.jme3.math.Vector3f oldVec, javax.vecmath.Vector3f newVec) {
+ newVec.x = oldVec.x;
+ newVec.y = oldVec.y;
+ newVec.z = oldVec.z;
+ return newVec;
+ }
+
+ public static javax.vecmath.Quat4f convert(com.jme3.math.Quaternion oldQuat, javax.vecmath.Quat4f newQuat) {
+ newQuat.w = oldQuat.getW();
+ newQuat.x = oldQuat.getX();
+ newQuat.y = oldQuat.getY();
+ newQuat.z = oldQuat.getZ();
+ return newQuat;
+ }
+
+ public static javax.vecmath.Quat4f convert(com.jme3.math.Quaternion oldQuat) {
+ javax.vecmath.Quat4f newQuat = new javax.vecmath.Quat4f();
+ convert(oldQuat, newQuat);
+ return newQuat;
+ }
+
+ public static com.jme3.math.Quaternion convert(javax.vecmath.Quat4f oldQuat, com.jme3.math.Quaternion newQuat) {
+ newQuat.set(oldQuat.x, oldQuat.y, oldQuat.z, oldQuat.w);
+ return newQuat;
+ }
+
+ public static com.jme3.math.Quaternion convert(javax.vecmath.Quat4f oldQuat) {
+ com.jme3.math.Quaternion newQuat = new com.jme3.math.Quaternion();
+ convert(oldQuat, newQuat);
+ return newQuat;
+ }
+
+ public static com.jme3.math.Quaternion convert(javax.vecmath.Matrix3f oldMatrix, com.jme3.math.Quaternion newQuaternion) {
+ // the trace is the sum of the diagonal elements; see
+ // http://mathworld.wolfram.com/MatrixTrace.html
+ float t = oldMatrix.m00 + oldMatrix.m11 + oldMatrix.m22;
+ float w, x, y, z;
+ // we protect the division by s by ensuring that s>=1
+ if (t >= 0) { // |w| >= .5
+ float s = FastMath.sqrt(t + 1); // |s|>=1 ...
+ w = 0.5f * s;
+ s = 0.5f / s; // so this division isn't bad
+ x = (oldMatrix.m21 - oldMatrix.m12) * s;
+ y = (oldMatrix.m02 - oldMatrix.m20) * s;
+ z = (oldMatrix.m10 - oldMatrix.m01) * s;
+ } else if ((oldMatrix.m00 > oldMatrix.m11) && (oldMatrix.m00 > oldMatrix.m22)) {
+ float s = FastMath.sqrt(1.0f + oldMatrix.m00 - oldMatrix.m11 - oldMatrix.m22); // |s|>=1
+ x = s * 0.5f; // |x| >= .5
+ s = 0.5f / s;
+ y = (oldMatrix.m10 + oldMatrix.m01) * s;
+ z = (oldMatrix.m02 + oldMatrix.m20) * s;
+ w = (oldMatrix.m21 - oldMatrix.m12) * s;
+ } else if (oldMatrix.m11 > oldMatrix.m22) {
+ float s = FastMath.sqrt(1.0f + oldMatrix.m11 - oldMatrix.m00 - oldMatrix.m22); // |s|>=1
+ y = s * 0.5f; // |y| >= .5
+ s = 0.5f / s;
+ x = (oldMatrix.m10 + oldMatrix.m01) * s;
+ z = (oldMatrix.m21 + oldMatrix.m12) * s;
+ w = (oldMatrix.m02 - oldMatrix.m20) * s;
+ } else {
+ float s = FastMath.sqrt(1.0f + oldMatrix.m22 - oldMatrix.m00 - oldMatrix.m11); // |s|>=1
+ z = s * 0.5f; // |z| >= .5
+ s = 0.5f / s;
+ x = (oldMatrix.m02 + oldMatrix.m20) * s;
+ y = (oldMatrix.m21 + oldMatrix.m12) * s;
+ w = (oldMatrix.m10 - oldMatrix.m01) * s;
+ }
+ return newQuaternion.set(x, y, z, w);
+ }
+
+ public static javax.vecmath.Matrix3f convert(com.jme3.math.Quaternion oldQuaternion, javax.vecmath.Matrix3f newMatrix) {
+ float norm = oldQuaternion.getW() * oldQuaternion.getW() + oldQuaternion.getX() * oldQuaternion.getX() + oldQuaternion.getY() * oldQuaternion.getY() + oldQuaternion.getZ() * oldQuaternion.getZ();
+ float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
+
+ // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
+ // will be used 2-4 times each.
+ float xs = oldQuaternion.getX() * s;
+ float ys = oldQuaternion.getY() * s;
+ float zs = oldQuaternion.getZ() * s;
+ float xx = oldQuaternion.getX() * xs;
+ float xy = oldQuaternion.getX() * ys;
+ float xz = oldQuaternion.getX() * zs;
+ float xw = oldQuaternion.getW() * xs;
+ float yy = oldQuaternion.getY() * ys;
+ float yz = oldQuaternion.getY() * zs;
+ float yw = oldQuaternion.getW() * ys;
+ float zz = oldQuaternion.getZ() * zs;
+ float zw = oldQuaternion.getW() * zs;
+
+ // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
+ newMatrix.m00 = 1 - (yy + zz);
+ newMatrix.m01 = (xy - zw);
+ newMatrix.m02 = (xz + yw);
+ newMatrix.m10 = (xy + zw);
+ newMatrix.m11 = 1 - (xx + zz);
+ newMatrix.m12 = (yz - xw);
+ newMatrix.m20 = (xz - yw);
+ newMatrix.m21 = (yz + xw);
+ newMatrix.m22 = 1 - (xx + yy);
+
+ return newMatrix;
+ }
+
+ public static com.jme3.math.Matrix3f convert(javax.vecmath.Matrix3f oldMatrix) {
+ com.jme3.math.Matrix3f newMatrix = new com.jme3.math.Matrix3f();
+ convert(oldMatrix, newMatrix);
+ return newMatrix;
+ }
+
+ public static com.jme3.math.Matrix3f convert(javax.vecmath.Matrix3f oldMatrix, com.jme3.math.Matrix3f newMatrix) {
+ newMatrix.set(0, 0, oldMatrix.m00);
+ newMatrix.set(0, 1, oldMatrix.m01);
+ newMatrix.set(0, 2, oldMatrix.m02);
+ newMatrix.set(1, 0, oldMatrix.m10);
+ newMatrix.set(1, 1, oldMatrix.m11);
+ newMatrix.set(1, 2, oldMatrix.m12);
+ newMatrix.set(2, 0, oldMatrix.m20);
+ newMatrix.set(2, 1, oldMatrix.m21);
+ newMatrix.set(2, 2, oldMatrix.m22);
+ return newMatrix;
+ }
+
+ public static javax.vecmath.Matrix3f convert(com.jme3.math.Matrix3f oldMatrix) {
+ javax.vecmath.Matrix3f newMatrix = new javax.vecmath.Matrix3f();
+ convert(oldMatrix, newMatrix);
+ return newMatrix;
+ }
+
+ public static javax.vecmath.Matrix3f convert(com.jme3.math.Matrix3f oldMatrix, javax.vecmath.Matrix3f newMatrix) {
+ newMatrix.m00 = oldMatrix.get(0, 0);
+ newMatrix.m01 = oldMatrix.get(0, 1);
+ newMatrix.m02 = oldMatrix.get(0, 2);
+ newMatrix.m10 = oldMatrix.get(1, 0);
+ newMatrix.m11 = oldMatrix.get(1, 1);
+ newMatrix.m12 = oldMatrix.get(1, 2);
+ newMatrix.m20 = oldMatrix.get(2, 0);
+ newMatrix.m21 = oldMatrix.get(2, 1);
+ newMatrix.m22 = oldMatrix.get(2, 2);
+ return newMatrix;
+ }
+
+ public static com.bulletphysics.linearmath.Transform convert(com.jme3.math.Transform in, com.bulletphysics.linearmath.Transform out) {
+ convert(in.getTranslation(), out.origin);
+ convert(in.getRotation(), out.basis);
+ return out;
+ }
+
+ public static com.jme3.math.Transform convert(com.bulletphysics.linearmath.Transform in, com.jme3.math.Transform out) {
+ convert(in.origin, out.getTranslation());
+ convert(in.basis, out.getRotation());
+ return out;
+ }
+
+ public static IndexedMesh convert(Mesh mesh) {
+ IndexedMesh jBulletIndexedMesh = new IndexedMesh();
+ jBulletIndexedMesh.triangleIndexBase = ByteBuffer.allocate(mesh.getTriangleCount() * 3 * 4);
+ jBulletIndexedMesh.vertexBase = ByteBuffer.allocate(mesh.getVertexCount() * 3 * 4);
+
+ IndexBuffer indices = mesh.getIndicesAsList();
+
+ FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+ vertices.rewind();
+
+ int verticesLength = mesh.getVertexCount() * 3;
+ jBulletIndexedMesh.numVertices = mesh.getVertexCount();
+ jBulletIndexedMesh.vertexStride = 12; //3 verts * 4 bytes per.
+ for (int i = 0; i < verticesLength; i++) {
+ float tempFloat = vertices.get();
+ jBulletIndexedMesh.vertexBase.putFloat(tempFloat);
+ }
+
+ int indicesLength = mesh.getTriangleCount() * 3;
+ jBulletIndexedMesh.numTriangles = mesh.getTriangleCount();
+ jBulletIndexedMesh.triangleIndexStride = 12; //3 index entries * 4 bytes each.
+ for (int i = 0; i < indicesLength; i++) {
+ jBulletIndexedMesh.triangleIndexBase.putInt(indices.get(i));
+ }
+ vertices.rewind();
+ vertices.clear();
+
+ return jBulletIndexedMesh;
+ }
+
+ public static Mesh convert(IndexedMesh mesh) {
+ Mesh jmeMesh = new Mesh();
+
+ jmeMesh.setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(mesh.numTriangles * 3));
+ jmeMesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(mesh.numVertices * 3));
+
+ IndexBuffer indicess = jmeMesh.getIndexBuffer();
+ FloatBuffer vertices = jmeMesh.getFloatBuffer(Type.Position);
+
+ for (int i = 0; i < mesh.numTriangles * 3; i++) {
+ indicess.put(i, mesh.triangleIndexBase.getInt(i * 4));
+ }
+
+ for (int i = 0; i < mesh.numVertices * 3; i++) {
+ vertices.put(i, mesh.vertexBase.getFloat(i * 4));
+ }
+ jmeMesh.updateCounts();
+ jmeMesh.updateBound();
+ jmeMesh.getFloatBuffer(Type.Position).clear();
+
+ return jmeMesh;
+ }
+
+ public static Mesh convert(HeightfieldTerrainShape heightfieldShape) {
+ return null; //TODO!!
+ }
+}
diff --git a/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java b/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java
new file mode 100644
index 0000000..ff0009c
--- /dev/null
+++ b/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bullet.util;
+
+import com.bulletphysics.collision.shapes.ConcaveShape;
+import com.bulletphysics.collision.shapes.ConvexShape;
+import com.bulletphysics.collision.shapes.ShapeHull;
+import com.bulletphysics.collision.shapes.TriangleCallback;
+import com.bulletphysics.util.IntArrayList;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
+import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
+import com.jme3.math.Matrix3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author CJ Hare, normenhansen
+ */
+public class DebugShapeFactory {
+
+ /** The maximum corner for the aabb used for triangles to include in ConcaveShape processing.*/
+ private static final Vector3f aabbMax = new Vector3f(1e30f, 1e30f, 1e30f);
+ /** The minimum corner for the aabb used for triangles to include in ConcaveShape processing.*/
+ private static final Vector3f aabbMin = new Vector3f(-1e30f, -1e30f, -1e30f);
+
+ /**
+ * Creates a debug shape from the given collision shape. This is mostly used internally.<br>
+ * To attach a debug shape to a physics object, call <code>attachDebugShape(AssetManager manager);</code> on it.
+ * @param collisionShape
+ * @return
+ */
+ public static Spatial getDebugShape(CollisionShape collisionShape) {
+ if (collisionShape == null) {
+ return null;
+ }
+ Spatial debugShape;
+ if (collisionShape instanceof CompoundCollisionShape) {
+ CompoundCollisionShape shape = (CompoundCollisionShape) collisionShape;
+ List<ChildCollisionShape> children = shape.getChildren();
+ Node node = new Node("DebugShapeNode");
+ for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
+ ChildCollisionShape childCollisionShape = it.next();
+ CollisionShape ccollisionShape = childCollisionShape.shape;
+ Geometry geometry = createDebugShape(ccollisionShape);
+
+ // apply translation
+ geometry.setLocalTranslation(childCollisionShape.location);
+
+ // apply rotation
+ TempVars vars = TempVars.get();
+
+ Matrix3f tempRot = vars.tempMat3;
+
+ tempRot.set(geometry.getLocalRotation());
+ childCollisionShape.rotation.mult(tempRot, tempRot);
+ geometry.setLocalRotation(tempRot);
+
+ vars.release();
+
+ node.attachChild(geometry);
+ }
+ debugShape = node;
+ } else {
+ debugShape = createDebugShape(collisionShape);
+ }
+ if (debugShape == null) {
+ return null;
+ }
+ debugShape.updateGeometricState();
+ return debugShape;
+ }
+
+ private static Geometry createDebugShape(CollisionShape shape) {
+ Geometry geom = new Geometry();
+ geom.setMesh(DebugShapeFactory.getDebugMesh(shape));
+// geom.setLocalScale(shape.getScale());
+ geom.updateModelBound();
+ return geom;
+ }
+
+ public static Mesh getDebugMesh(CollisionShape shape) {
+ Mesh mesh = null;
+ if (shape.getCShape() instanceof ConvexShape) {
+ mesh = new Mesh();
+ mesh.setBuffer(Type.Position, 3, getVertices((ConvexShape) shape.getCShape()));
+ mesh.getFloatBuffer(Type.Position).clear();
+ } else if (shape.getCShape() instanceof ConcaveShape) {
+ mesh = new Mesh();
+ mesh.setBuffer(Type.Position, 3, getVertices((ConcaveShape) shape.getCShape()));
+ mesh.getFloatBuffer(Type.Position).clear();
+ }
+ return mesh;
+ }
+
+ /**
+ * Constructs the buffer for the vertices of the concave shape.
+ *
+ * @param concaveShape the shape to get the vertices for / from.
+ * @return the shape as stored by the given broadphase rigid body.
+ */
+ private static FloatBuffer getVertices(ConcaveShape concaveShape) {
+ // Create the call back that'll create the vertex buffer
+ BufferedTriangleCallback triangleProcessor = new BufferedTriangleCallback();
+ concaveShape.processAllTriangles(triangleProcessor, aabbMin, aabbMax);
+
+ // Retrieve the vextex and index buffers
+ return triangleProcessor.getVertices();
+ }
+
+ /**
+ * Processes the given convex shape to retrieve a correctly ordered FloatBuffer to
+ * construct the shape from with a TriMesh.
+ *
+ * @param convexShape the shape to retreieve the vertices from.
+ * @return the vertices as a FloatBuffer, ordered as Triangles.
+ */
+ private static FloatBuffer getVertices(ConvexShape convexShape) {
+ // Check there is a hull shape to render
+ if (convexShape.getUserPointer() == null) {
+ // create a hull approximation
+ ShapeHull hull = new ShapeHull(convexShape);
+ float margin = convexShape.getMargin();
+ hull.buildHull(margin);
+ convexShape.setUserPointer(hull);
+ }
+
+ // Assert state - should have a pointer to a hull (shape) that'll be drawn
+ assert convexShape.getUserPointer() != null : "Should have a shape for the userPointer, instead got null";
+ ShapeHull hull = (ShapeHull) convexShape.getUserPointer();
+
+ // Assert we actually have a shape to render
+ assert hull.numTriangles() > 0 : "Expecting the Hull shape to have triangles";
+ int numberOfTriangles = hull.numTriangles();
+
+ // The number of bytes needed is: (floats in a vertex) * (vertices in a triangle) * (# of triangles) * (size of float in bytes)
+ final int numberOfFloats = 3 * 3 * numberOfTriangles;
+ FloatBuffer vertices = BufferUtils.createFloatBuffer(numberOfFloats);
+
+ // Force the limit, set the cap - most number of floats we will use the buffer for
+ vertices.limit(numberOfFloats);
+
+ // Loop variables
+ final IntArrayList hullIndicies = hull.getIndexPointer();
+ final List<Vector3f> hullVertices = hull.getVertexPointer();
+ Vector3f vertexA, vertexB, vertexC;
+ int index = 0;
+
+ for (int i = 0; i < numberOfTriangles; i++) {
+ // Grab the data for this triangle from the hull
+ vertexA = hullVertices.get(hullIndicies.get(index++));
+ vertexB = hullVertices.get(hullIndicies.get(index++));
+ vertexC = hullVertices.get(hullIndicies.get(index++));
+
+ // Put the verticies into the vertex buffer
+ vertices.put(vertexA.x).put(vertexA.y).put(vertexA.z);
+ vertices.put(vertexB.x).put(vertexB.y).put(vertexB.z);
+ vertices.put(vertexC.x).put(vertexC.y).put(vertexC.z);
+ }
+
+ vertices.clear();
+ return vertices;
+ }
+}
+
+/**
+ * A callback is used to process the triangles of the shape as there is no direct access to a concave shapes, shape.
+ * <p/>
+ * The triangles are simply put into a list (which in extreme condition will cause memory problems) then put into a direct buffer.
+ *
+ * @author CJ Hare
+ */
+class BufferedTriangleCallback extends TriangleCallback {
+
+ private ArrayList<Vector3f> vertices;
+
+ public BufferedTriangleCallback() {
+ vertices = new ArrayList<Vector3f>();
+ }
+
+ @Override
+ public void processTriangle(Vector3f[] triangle, int partId, int triangleIndex) {
+ // Three sets of individual lines
+ // The new Vector is needed as the given triangle reference is from a pool
+ vertices.add(new Vector3f(triangle[0]));
+ vertices.add(new Vector3f(triangle[1]));
+ vertices.add(new Vector3f(triangle[2]));
+ }
+
+ /**
+ * Retrieves the vertices from the Triangle buffer.
+ */
+ public FloatBuffer getVertices() {
+ // There are 3 floats needed for each vertex (x,y,z)
+ final int numberOfFloats = vertices.size() * 3;
+ FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(numberOfFloats);
+
+ // Force the limit, set the cap - most number of floats we will use the buffer for
+ verticesBuffer.limit(numberOfFloats);
+
+ // Copy the values from the list to the direct float buffer
+ for (Vector3f v : vertices) {
+ verticesBuffer.put(v.x).put(v.y).put(v.z);
+ }
+
+ vertices.clear();
+ return verticesBuffer;
+ }
+}
diff --git a/engine/src/jogg/com/jme3/audio/plugins/CachedOggStream.java b/engine/src/jogg/com/jme3/audio/plugins/CachedOggStream.java
new file mode 100644
index 0000000..9b7c980
--- /dev/null
+++ b/engine/src/jogg/com/jme3/audio/plugins/CachedOggStream.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio.plugins;
+
+import com.jme3.util.IntMap;
+import de.jarnbjo.ogg.LogicalOggStream;
+import de.jarnbjo.ogg.LogicalOggStreamImpl;
+import de.jarnbjo.ogg.OggPage;
+import de.jarnbjo.ogg.PhysicalOggStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Implementation of the <code>PhysicalOggStream</code> interface for reading
+ * and caching an Ogg stream from a URL. This class reads the data as fast as
+ * possible from the URL, caches it locally either in memory or on disk, and
+ * supports seeking within the available data.
+ */
+public class CachedOggStream implements PhysicalOggStream {
+
+ private boolean closed = false;
+ private boolean eos = false;
+ private boolean bos = false;
+ private InputStream sourceStream;
+ private HashMap<Integer, LogicalOggStream> logicalStreams
+ = new HashMap<Integer, LogicalOggStream>();
+
+ private IntMap<OggPage> oggPages = new IntMap<OggPage>();
+ private OggPage lastPage;
+
+ private int pageNumber;
+
+ public CachedOggStream(InputStream in) throws IOException {
+ sourceStream = in;
+
+ // Read all OGG pages in file
+ long time = System.nanoTime();
+ while (!eos){
+ readOggNextPage();
+ }
+ long dt = System.nanoTime() - time;
+ Logger.getLogger(CachedOggStream.class.getName()).log(Level.INFO, "Took {0} ms to load OGG", dt/1000000);
+ }
+
+ public OggPage getLastOggPage() {
+ return lastPage;
+ }
+
+ private LogicalOggStream getLogicalStream(int serialNumber) {
+ return logicalStreams.get(Integer.valueOf(serialNumber));
+ }
+
+ public Collection<LogicalOggStream> getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public void close() throws IOException {
+ closed = true;
+ sourceStream.close();
+ }
+
+ public OggPage getOggPage(int index) throws IOException {
+ return oggPages.get(index);
+ }
+
+ public void setTime(long granulePosition) throws IOException {
+ for (LogicalOggStream los : getLogicalStreams()){
+ los.setTime(granulePosition);
+ }
+ }
+
+ private int readOggNextPage() throws IOException {
+ if (eos)
+ return -1;
+
+ OggPage op = OggPage.create(sourceStream);
+ if (!op.isBos()){
+ bos = true;
+ }
+ if (op.isEos()){
+ eos = true;
+ lastPage = op;
+ }
+
+ LogicalOggStreamImpl los = (LogicalOggStreamImpl) logicalStreams.get(op.getStreamSerialNumber());
+ if(los == null) {
+ los = new LogicalOggStreamImpl(this, op.getStreamSerialNumber());
+ logicalStreams.put(op.getStreamSerialNumber(), los);
+ los.checkFormat(op);
+ }
+
+ los.addPageNumberMapping(pageNumber);
+ los.addGranulePosition(op.getAbsoluteGranulePosition());
+
+ oggPages.put(pageNumber, op);
+ pageNumber++;
+
+ return pageNumber-1;
+ }
+
+ public boolean isSeekable() {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/engine/src/jogg/com/jme3/audio/plugins/OGGLoader.java b/engine/src/jogg/com/jme3/audio/plugins/OGGLoader.java
new file mode 100644
index 0000000..b342023
--- /dev/null
+++ b/engine/src/jogg/com/jme3/audio/plugins/OGGLoader.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.audio.AudioBuffer;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioKey;
+import com.jme3.audio.AudioStream;
+import com.jme3.audio.SeekableStream;
+import com.jme3.util.BufferUtils;
+import de.jarnbjo.ogg.EndOfOggStreamException;
+import de.jarnbjo.ogg.LogicalOggStream;
+import de.jarnbjo.ogg.PhysicalOggStream;
+import de.jarnbjo.vorbis.IdentificationHeader;
+import de.jarnbjo.vorbis.VorbisStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class OGGLoader implements AssetLoader {
+
+// private static int BLOCK_SIZE = 4096*64;
+
+ private PhysicalOggStream oggStream;
+ private LogicalOggStream loStream;
+ private VorbisStream vorbisStream;
+
+// private CommentHeader commentHdr;
+ private IdentificationHeader streamHdr;
+
+ private static class JOggInputStream extends InputStream {
+
+ private boolean endOfStream = false;
+ protected final VorbisStream vs;
+
+ public JOggInputStream(VorbisStream vs){
+ this.vs = vs;
+ }
+
+ @Override
+ public int read() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public int read(byte[] buf) throws IOException{
+ return read(buf,0,buf.length);
+ }
+
+ @Override
+ public int read(byte[] buf, int offset, int length) throws IOException{
+ if (endOfStream)
+ return -1;
+
+ int bytesRead = 0, cnt = 0;
+ assert length % 2 == 0; // read buffer should be even
+
+ while (bytesRead <length) {
+ if ((cnt = vs.readPcm(buf, offset + bytesRead,length - bytesRead)) <= 0) {
+ System.out.println("Read "+cnt+" bytes");
+ System.out.println("offset "+offset);
+ System.out.println("bytesRead "+bytesRead);
+ System.out.println("buf length "+length);
+ for (int i = 0; i < bytesRead; i++) {
+ System.out.print(buf[i]);
+ }
+ System.out.println("");
+
+
+ System.out.println("EOS");
+ endOfStream = true;
+ break;
+ }
+ bytesRead += cnt;
+ }
+
+ swapBytes(buf, offset, bytesRead);
+ return bytesRead;
+
+ }
+
+ @Override
+ public void close() throws IOException{
+ vs.close();
+ }
+
+ }
+
+ private static class SeekableJOggInputStream extends JOggInputStream implements SeekableStream {
+
+ private LogicalOggStream los;
+ private float duration;
+
+ public SeekableJOggInputStream(VorbisStream vs, LogicalOggStream los, float duration){
+ super(vs);
+ this.los = los;
+ this.duration = duration;
+ }
+
+ public void setTime(float time) {
+ System.out.println("--setTime--)");
+ System.out.println("max granule : "+los.getMaximumGranulePosition());
+ System.out.println("current granule : "+los.getTime());
+ System.out.println("asked Time : "+time);
+ System.out.println("new granule : "+(time/duration*los.getMaximumGranulePosition()));
+ System.out.println("new granule2 : "+(time*vs.getIdentificationHeader().getSampleRate()));
+
+
+
+ try {
+ los.setTime((long)(time*vs.getIdentificationHeader().getSampleRate()));
+ } catch (IOException ex) {
+ Logger.getLogger(OGGLoader.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ }
+
+ /**
+ * Returns the total of expected OGG bytes.
+ *
+ * @param dataBytesTotal The number of bytes in the input
+ * @return If the computed number of bytes is less than the number
+ * of bytes in the input, it is returned, otherwise the number
+ * of bytes in the input is returned.
+ */
+ private int getOggTotalBytes(int dataBytesTotal){
+ // Vorbis stream could have more samples than than the duration of the sound
+ // Must truncate.
+ int numSamples;
+ if (oggStream instanceof CachedOggStream){
+ CachedOggStream cachedOggStream = (CachedOggStream) oggStream;
+ numSamples = (int) cachedOggStream.getLastOggPage().getAbsoluteGranulePosition();
+ }else{
+ UncachedOggStream uncachedOggStream = (UncachedOggStream) oggStream;
+ numSamples = (int) uncachedOggStream.getLastOggPage().getAbsoluteGranulePosition();
+ }
+
+ // Number of Samples * Number of Channels * Bytes Per Sample
+ int totalBytes = numSamples * streamHdr.getChannels() * 2;
+
+// System.out.println("Sample Rate: " + streamHdr.getSampleRate());
+// System.out.println("Channels: " + streamHdr.getChannels());
+// System.out.println("Stream Length: " + numSamples);
+// System.out.println("Bytes Calculated: " + totalBytes);
+// System.out.println("Bytes Available: " + dataBytes.length);
+
+ // Take the minimum of the number of bytes available
+ // and the expected duration of the audio.
+ return Math.min(totalBytes, dataBytesTotal);
+ }
+
+ private float computeStreamDuration(){
+ // for uncached stream sources, the granule position is not known.
+ if (oggStream instanceof UncachedOggStream)
+ return -1;
+
+ // 2 bytes(16bit) * channels * sampleRate
+ int bytesPerSec = 2 * streamHdr.getChannels() * streamHdr.getSampleRate();
+
+ // Don't know how many bytes are in input, pass MAX_VALUE
+ int totalBytes = getOggTotalBytes(Integer.MAX_VALUE);
+
+ return (float)totalBytes / bytesPerSec;
+ }
+
+ private ByteBuffer readToBuffer() throws IOException{
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ byte[] buf = new byte[512];
+ int read = 0;
+
+ try {
+ while ( (read = vorbisStream.readPcm(buf, 0, buf.length)) > 0){
+ baos.write(buf, 0, read);
+ }
+ } catch (EndOfOggStreamException ex){
+ }
+
+
+ byte[] dataBytes = baos.toByteArray();
+ swapBytes(dataBytes, 0, dataBytes.length);
+
+ int bytesToCopy = getOggTotalBytes( dataBytes.length );
+
+ ByteBuffer data = BufferUtils.createByteBuffer(bytesToCopy);
+ data.put(dataBytes, 0, bytesToCopy).flip();
+
+ vorbisStream.close();
+ loStream.close();
+ oggStream.close();
+
+ return data;
+ }
+
+ private static void swapBytes(byte[] b, int off, int len) {
+ byte tempByte;
+ for (int i = off; i < (off+len); i+=2) {
+ tempByte = b[i];
+ b[i] = b[i+1];
+ b[i+1] = tempByte;
+ }
+ }
+
+ private InputStream readToStream(boolean seekable,float streamDuration){
+ if(seekable){
+ return new SeekableJOggInputStream(vorbisStream,loStream,streamDuration);
+ }else{
+ return new JOggInputStream(vorbisStream);
+ }
+ }
+
+ private AudioData load(InputStream in, boolean readStream, boolean streamCache) throws IOException{
+ if (readStream && streamCache){
+ oggStream = new CachedOggStream(in);
+ }else{
+ oggStream = new UncachedOggStream(in);
+ }
+
+ Collection<LogicalOggStream> streams = oggStream.getLogicalStreams();
+ loStream = streams.iterator().next();
+
+// if (loStream == null){
+// throw new IOException("OGG File does not contain vorbis audio stream");
+// }
+
+ vorbisStream = new VorbisStream(loStream);
+ streamHdr = vorbisStream.getIdentificationHeader();
+// commentHdr = vorbisStream.getCommentHeader();
+
+ if (!readStream){
+ AudioBuffer audioBuffer = new AudioBuffer();
+ audioBuffer.setupFormat(streamHdr.getChannels(), 16, streamHdr.getSampleRate());
+ audioBuffer.updateData(readToBuffer());
+ return audioBuffer;
+ }else{
+ AudioStream audioStream = new AudioStream();
+ audioStream.setupFormat(streamHdr.getChannels(), 16, streamHdr.getSampleRate());
+
+ // might return -1 if unknown
+ float streamDuration = computeStreamDuration();
+
+ audioStream.updateData(readToStream(oggStream.isSeekable(),streamDuration), streamDuration);
+ return audioStream;
+ }
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (!(info.getKey() instanceof AudioKey)){
+ throw new IllegalArgumentException("Audio assets must be loaded using an AudioKey");
+ }
+
+ AudioKey key = (AudioKey) info.getKey();
+ boolean readStream = key.isStream();
+ boolean streamCache = key.useStreamCache();
+
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ AudioData data = load(in, readStream, streamCache);
+ if (data instanceof AudioStream){
+ // audio streams must remain open
+ in = null;
+ }
+ return data;
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+
+ }
+
+}
diff --git a/engine/src/jogg/com/jme3/audio/plugins/UncachedOggStream.java b/engine/src/jogg/com/jme3/audio/plugins/UncachedOggStream.java
new file mode 100644
index 0000000..de81af9
--- /dev/null
+++ b/engine/src/jogg/com/jme3/audio/plugins/UncachedOggStream.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio.plugins;
+
+import de.jarnbjo.ogg.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+/**
+ * Single-threaded physical ogg stream. Decodes audio in the same thread
+ * that reads.
+ */
+public class UncachedOggStream implements PhysicalOggStream {
+
+ private boolean closed = false;
+ private boolean eos = false;
+ private boolean bos = false;
+ private InputStream sourceStream;
+ private LinkedList<OggPage> pageCache = new LinkedList<OggPage>();
+ private HashMap<Integer, LogicalOggStream> logicalStreams
+ = new HashMap<Integer, LogicalOggStream>();
+ private OggPage lastPage = null;
+
+ public UncachedOggStream(InputStream in) throws OggFormatException, IOException {
+ this.sourceStream = in;
+
+ // read until beginning of stream
+ while (!bos){
+ readNextOggPage();
+ }
+
+ // now buffer up an addition 25 pages
+// while (pageCache.size() < 25 && !eos){
+// readNextOggPage();
+// }
+ }
+
+ public OggPage getLastOggPage() {
+ return lastPage;
+ }
+
+ private void readNextOggPage() throws IOException {
+ OggPage op = OggPage.create(sourceStream);
+ if (!op.isBos()){
+ bos = true;
+ }
+ if (op.isEos()){
+ eos = true;
+ lastPage = op;
+ }
+
+ LogicalOggStreamImpl los = (LogicalOggStreamImpl) getLogicalStream(op.getStreamSerialNumber());
+ if (los == null){
+ los = new LogicalOggStreamImpl(this, op.getStreamSerialNumber());
+ logicalStreams.put(op.getStreamSerialNumber(), los);
+ los.checkFormat(op);
+ }
+
+ pageCache.add(op);
+ }
+
+ public OggPage getOggPage(int index) throws IOException {
+ if (eos){
+ return null;
+ }
+
+// if (!eos){
+// int num = pageCache.size();
+// long fiveMillis = 5000000;
+// long timeStart = System.nanoTime();
+// do {
+// readNextOggPage();
+// } while ( !eos && (System.nanoTime() - timeStart) < fiveMillis );
+// System.out.println( pageCache.size() - num );
+
+ if (pageCache.size() == 0 /*&& !eos*/){
+ readNextOggPage();
+ }
+// }
+
+ return pageCache.removeFirst();
+ }
+
+ private LogicalOggStream getLogicalStream(int serialNumber) {
+ return logicalStreams.get(Integer.valueOf(serialNumber));
+ }
+
+ public Collection<LogicalOggStream> getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ public void setTime(long granulePosition) throws IOException {
+ }
+
+ public boolean isSeekable() {
+ return false;
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public void close() throws IOException {
+ closed = true;
+ sourceStream.close();
+ }
+}
diff --git a/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java b/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java
new file mode 100644
index 0000000..2570b6a
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio.lwjgl;
+
+import com.jme3.audio.AudioNode.Status;
+import com.jme3.audio.*;
+import com.jme3.math.Vector3f;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.NativeObjectManager;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import static org.lwjgl.openal.AL10.*;
+import org.lwjgl.openal.*;
+
+public class LwjglAudioRenderer implements AudioRenderer, Runnable {
+
+ private static final Logger logger = Logger.getLogger(LwjglAudioRenderer.class.getName());
+
+ private final NativeObjectManager objManager = new NativeObjectManager();
+
+ // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2
+ // which is exactly 1 second of audio.
+ private static final int BUFFER_SIZE = 35280;
+ private static final int STREAMING_BUFFER_COUNT = 5;
+
+ private final static int MAX_NUM_CHANNELS = 64;
+ private IntBuffer ib = BufferUtils.createIntBuffer(1);
+ private final FloatBuffer fb = BufferUtils.createVector3Buffer(2);
+ private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE);
+ private final byte[] arrayBuf = new byte[BUFFER_SIZE];
+
+ private int[] channels;
+ private AudioNode[] chanSrcs;
+ private int nextChan = 0;
+ private ArrayList<Integer> freeChans = new ArrayList<Integer>();
+
+ private Listener listener;
+ private boolean audioDisabled = false;
+
+ private boolean supportEfx = false;
+ private int auxSends = 0;
+ private int reverbFx = -1;
+ private int reverbFxSlot = -1;
+
+ // Update audio 20 times per second
+ private static final float UPDATE_RATE = 0.05f;
+
+ private final Thread audioThread = new Thread(this, "jME3 Audio Thread");
+ private final AtomicBoolean threadLock = new AtomicBoolean(false);
+
+ public LwjglAudioRenderer(){
+ }
+
+ public void initialize(){
+ if (!audioThread.isAlive()){
+ audioThread.setDaemon(true);
+ audioThread.setPriority(Thread.NORM_PRIORITY+1);
+ audioThread.start();
+ }else{
+ throw new IllegalStateException("Initialize already called");
+ }
+ }
+
+ private void checkDead(){
+ if (audioThread.getState() == Thread.State.TERMINATED)
+ throw new IllegalStateException("Audio thread is terminated");
+ }
+
+ public void run(){
+ initInThread();
+ synchronized (threadLock){
+ threadLock.set(true);
+ threadLock.notifyAll();
+ }
+
+ long updateRateNanos = (long) (UPDATE_RATE * 1000000000);
+ mainloop: while (true){
+ long startTime = System.nanoTime();
+
+ if (Thread.interrupted())
+ break;
+
+ synchronized (threadLock){
+ updateInThread(UPDATE_RATE);
+ }
+
+ long endTime = System.nanoTime();
+ long diffTime = endTime - startTime;
+
+ if (diffTime < updateRateNanos){
+ long desiredEndTime = startTime + updateRateNanos;
+ while (System.nanoTime() < desiredEndTime){
+ try{
+ Thread.sleep(1);
+ }catch (InterruptedException ex){
+ break mainloop;
+ }
+ }
+ }
+ }
+
+ synchronized (threadLock){
+ cleanupInThread();
+ }
+ }
+
+ public void initInThread(){
+ try{
+ if (!AL.isCreated()){
+ AL.create();
+ }
+ }catch (OpenALException ex){
+ logger.log(Level.SEVERE, "Failed to load audio library", ex);
+ audioDisabled = true;
+ return;
+ }catch (LWJGLException ex){
+ logger.log(Level.SEVERE, "Failed to load audio library", ex);
+ audioDisabled = true;
+ return;
+ } catch (UnsatisfiedLinkError ex){
+ logger.log(Level.SEVERE, "Failed to load audio library", ex);
+ audioDisabled = true;
+ return;
+ }
+
+ ALCdevice device = AL.getDevice();
+ String deviceName = ALC10.alcGetString(device, ALC10.ALC_DEVICE_SPECIFIER);
+
+ logger.log(Level.FINER, "Audio Device: {0}", deviceName);
+ logger.log(Level.FINER, "Audio Vendor: {0}", alGetString(AL_VENDOR));
+ logger.log(Level.FINER, "Audio Renderer: {0}", alGetString(AL_RENDERER));
+ logger.log(Level.FINER, "Audio Version: {0}", alGetString(AL_VERSION));
+
+ // Find maximum # of sources supported by this implementation
+ ArrayList<Integer> channelList = new ArrayList<Integer>();
+ for (int i = 0; i < MAX_NUM_CHANNELS; i++){
+ int chan = alGenSources();
+ if (alGetError() != 0){
+ break;
+ }else{
+ channelList.add(chan);
+ }
+ }
+
+ channels = new int[channelList.size()];
+ for (int i = 0; i < channels.length; i++){
+ channels[i] = channelList.get(i);
+ }
+
+ ib = BufferUtils.createIntBuffer(channels.length);
+ chanSrcs = new AudioNode[channels.length];
+
+ logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length);
+
+ supportEfx = ALC10.alcIsExtensionPresent(device, "ALC_EXT_EFX");
+ if (supportEfx){
+ ib.position(0).limit(1);
+ ALC10.alcGetInteger(device, EFX10.ALC_EFX_MAJOR_VERSION, ib);
+ int major = ib.get(0);
+ ib.position(0).limit(1);
+ ALC10.alcGetInteger(device, EFX10.ALC_EFX_MINOR_VERSION, ib);
+ int minor = ib.get(0);
+ logger.log(Level.INFO, "Audio effect extension version: {0}.{1}", new Object[]{major, minor});
+
+ ALC10.alcGetInteger(device, EFX10.ALC_MAX_AUXILIARY_SENDS, ib);
+ auxSends = ib.get(0);
+ logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends);
+
+ // create slot
+ ib.position(0).limit(1);
+ EFX10.alGenAuxiliaryEffectSlots(ib);
+ reverbFxSlot = ib.get(0);
+
+ // create effect
+ ib.position(0).limit(1);
+ EFX10.alGenEffects(ib);
+ reverbFx = ib.get(0);
+ EFX10.alEffecti(reverbFx, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_REVERB);
+
+ // attach reverb effect to effect slot
+ EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);
+ }else{
+ logger.log(Level.WARNING, "OpenAL EFX not available! Audio effects won't work.");
+ }
+ }
+
+ public void cleanupInThread(){
+ if (audioDisabled){
+ AL.destroy();
+ return;
+ }
+
+ // stop any playing channels
+ for (int i = 0; i < chanSrcs.length; i++){
+ if (chanSrcs[i] != null){
+ clearChannel(i);
+ }
+ }
+
+ // delete channel-based sources
+ ib.clear();
+ ib.put(channels);
+ ib.flip();
+ alDeleteSources(ib);
+
+ // delete audio buffers and filters
+ objManager.deleteAllObjects(this);
+
+ if (supportEfx){
+ ib.position(0).limit(1);
+ ib.put(0, reverbFx);
+ EFX10.alDeleteEffects(ib);
+
+ // If this is not allocated, why is it deleted?
+ // Commented out to fix native crash in OpenAL.
+ ib.position(0).limit(1);
+ ib.put(0, reverbFxSlot);
+ EFX10.alDeleteAuxiliaryEffectSlots(ib);
+ }
+
+ AL.destroy();
+ }
+
+ public void cleanup(){
+ // kill audio thread
+ if (audioThread.isAlive()){
+ audioThread.interrupt();
+ }
+ }
+
+ private void updateFilter(Filter f){
+ int id = f.getId();
+ if (id == -1){
+ ib.position(0).limit(1);
+ EFX10.alGenFilters(ib);
+ id = ib.get(0);
+ f.setId(id);
+
+ objManager.registerForCleanup(f);
+ }
+
+ if (f instanceof LowPassFilter){
+ LowPassFilter lpf = (LowPassFilter) f;
+ EFX10.alFilteri(id, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS);
+ EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAIN, lpf.getVolume());
+ EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAINHF, lpf.getHighFreqVolume());
+ }else{
+ throw new UnsupportedOperationException("Filter type unsupported: "+
+ f.getClass().getName());
+ }
+
+ f.clearUpdateNeeded();
+ }
+
+ public void updateSourceParam(AudioNode src, AudioParam param){
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ // There is a race condition in AudioNode that can
+ // cause this to be called for a node that has been
+ // detached from its channel. For example, setVolume()
+ // called from the render thread may see that that AudioNode
+ // still has a channel value but the audio thread may
+ // clear that channel before setVolume() gets to call
+ // updateSourceParam() (because the audio stopped playing
+ // on its own right as the volume was set). In this case,
+ // it should be safe to just ignore the update
+ if (src.getChannel() < 0)
+ return;
+
+ assert src.getChannel() >= 0;
+
+ int id = channels[src.getChannel()];
+ switch (param){
+ case Position:
+ if (!src.isPositional())
+ return;
+
+ Vector3f pos = src.getWorldTranslation();
+ alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
+ break;
+ case Velocity:
+ if (!src.isPositional())
+ return;
+
+ Vector3f vel = src.getVelocity();
+ alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);
+ break;
+ case MaxDistance:
+ if (!src.isPositional())
+ return;
+
+ alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());
+ break;
+ case RefDistance:
+ if (!src.isPositional())
+ return;
+
+ alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());
+ break;
+ case ReverbFilter:
+ if (!supportEfx || !src.isPositional() || !src.isReverbEnabled())
+ return;
+
+ int filter = EFX10.AL_FILTER_NULL;
+ if (src.getReverbFilter() != null){
+ Filter f = src.getReverbFilter();
+ if (f.isUpdateNeeded()){
+ updateFilter(f);
+ }
+ filter = f.getId();
+ }
+ AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);
+ break;
+ case ReverbEnabled:
+ if (!supportEfx || !src.isPositional())
+ return;
+
+ if (src.isReverbEnabled()){
+ updateSourceParam(src, AudioParam.ReverbFilter);
+ }else{
+ AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);
+ }
+ break;
+ case IsPositional:
+ if (!src.isPositional()){
+ // play in headspace
+ alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
+ alSource3f(id, AL_POSITION, 0,0,0);
+ alSource3f(id, AL_VELOCITY, 0,0,0);
+ }else{
+ alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
+ updateSourceParam(src, AudioParam.Position);
+ updateSourceParam(src, AudioParam.Velocity);
+ updateSourceParam(src, AudioParam.MaxDistance);
+ updateSourceParam(src, AudioParam.RefDistance);
+ updateSourceParam(src, AudioParam.ReverbEnabled);
+ }
+ break;
+ case Direction:
+ if (!src.isDirectional())
+ return;
+
+ Vector3f dir = src.getDirection();
+ alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);
+ break;
+ case InnerAngle:
+ if (!src.isDirectional())
+ return;
+
+ alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());
+ break;
+ case OuterAngle:
+ if (!src.isDirectional())
+ return;
+
+ alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());
+ break;
+ case IsDirectional:
+ if (src.isDirectional()){
+ updateSourceParam(src, AudioParam.Direction);
+ updateSourceParam(src, AudioParam.InnerAngle);
+ updateSourceParam(src, AudioParam.OuterAngle);
+ alSourcef(id, AL_CONE_OUTER_GAIN, 0);
+ }else{
+ alSourcef(id, AL_CONE_INNER_ANGLE, 360);
+ alSourcef(id, AL_CONE_OUTER_ANGLE, 360);
+ alSourcef(id, AL_CONE_OUTER_GAIN, 1f);
+ }
+ break;
+ case DryFilter:
+ if (!supportEfx)
+ return;
+
+ if (src.getDryFilter() != null){
+ Filter f = src.getDryFilter();
+ if (f.isUpdateNeeded()){
+ updateFilter(f);
+
+ // NOTE: must re-attach filter for changes to apply.
+ alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());
+ }
+ }else{
+ alSourcei(id, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);
+ }
+ break;
+ case Looping:
+ if (src.isLooping()){
+ if (!(src.getAudioData() instanceof AudioStream)){
+ alSourcei(id, AL_LOOPING, AL_TRUE);
+ }
+ }else{
+ alSourcei(id, AL_LOOPING, AL_FALSE);
+ }
+ break;
+ case Volume:
+ alSourcef(id, AL_GAIN, src.getVolume());
+ break;
+ case Pitch:
+ alSourcef(id, AL_PITCH, src.getPitch());
+ break;
+ }
+ }
+ }
+
+ private void setSourceParams(int id, AudioNode src, boolean forceNonLoop){
+ if (src.isPositional()){
+ Vector3f pos = src.getWorldTranslation();
+ Vector3f vel = src.getVelocity();
+ alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
+ alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);
+ alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());
+ alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());
+ alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
+
+ if (src.isReverbEnabled() && supportEfx){
+ int filter = EFX10.AL_FILTER_NULL;
+ if (src.getReverbFilter() != null){
+ Filter f = src.getReverbFilter();
+ if (f.isUpdateNeeded()){
+ updateFilter(f);
+ }
+ filter = f.getId();
+ }
+ AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);
+ }
+ }else{
+ // play in headspace
+ alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
+ alSource3f(id, AL_POSITION, 0,0,0);
+ alSource3f(id, AL_VELOCITY, 0,0,0);
+ }
+
+ if (src.getDryFilter() != null && supportEfx){
+ Filter f = src.getDryFilter();
+ if (f.isUpdateNeeded()){
+ updateFilter(f);
+
+ // NOTE: must re-attach filter for changes to apply.
+ alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());
+ }
+ }
+
+ if (forceNonLoop){
+ alSourcei(id, AL_LOOPING, AL_FALSE);
+ }else{
+ alSourcei(id, AL_LOOPING, src.isLooping() ? AL_TRUE : AL_FALSE);
+ }
+ alSourcef(id, AL_GAIN, src.getVolume());
+ alSourcef(id, AL_PITCH, src.getPitch());
+ alSourcef(id, AL11.AL_SEC_OFFSET, src.getTimeOffset());
+
+ if (src.isDirectional()){
+ Vector3f dir = src.getDirection();
+ alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);
+ alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());
+ alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());
+ alSourcef(id, AL_CONE_OUTER_GAIN, 0);
+ }else{
+ alSourcef(id, AL_CONE_INNER_ANGLE, 360);
+ alSourcef(id, AL_CONE_OUTER_ANGLE, 360);
+ alSourcef(id, AL_CONE_OUTER_GAIN, 1f);
+ }
+ }
+
+ public void updateListenerParam(Listener listener, ListenerParam param){
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ switch (param){
+ case Position:
+ Vector3f pos = listener.getLocation();
+ alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
+ break;
+ case Rotation:
+ Vector3f dir = listener.getDirection();
+ Vector3f up = listener.getUp();
+ fb.rewind();
+ fb.put(dir.x).put(dir.y).put(dir.z);
+ fb.put(up.x).put(up.y).put(up.z);
+ fb.flip();
+ alListener(AL_ORIENTATION, fb);
+ break;
+ case Velocity:
+ Vector3f vel = listener.getVelocity();
+ alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
+ break;
+ case Volume:
+ alListenerf(AL_GAIN, listener.getVolume());
+ break;
+ }
+ }
+ }
+
+ private void setListenerParams(Listener listener){
+ Vector3f pos = listener.getLocation();
+ Vector3f vel = listener.getVelocity();
+ Vector3f dir = listener.getDirection();
+ Vector3f up = listener.getUp();
+
+ alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
+ alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
+ fb.rewind();
+ fb.put(dir.x).put(dir.y).put(dir.z);
+ fb.put(up.x).put(up.y).put(up.z);
+ fb.flip();
+ alListener(AL_ORIENTATION, fb);
+ alListenerf(AL_GAIN, listener.getVolume());
+ }
+
+ private int newChannel(){
+ if (freeChans.size() > 0)
+ return freeChans.remove(0);
+ else if (nextChan < channels.length){
+ return nextChan++;
+ }else{
+ return -1;
+ }
+ }
+
+ private void freeChannel(int index){
+ if (index == nextChan-1){
+ nextChan--;
+ } else{
+ freeChans.add(index);
+ }
+ }
+
+ public void setEnvironment(Environment env){
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled || !supportEfx)
+ return;
+
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DENSITY, env.getDensity());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DIFFUSION, env.getDiffusion());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAIN, env.getGain());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAINHF, env.getGainHf());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_TIME, env.getDecayTime());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_HFRATIO, env.getDecayHFRatio());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_GAIN, env.getReflectGain());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_DELAY, env.getReflectDelay());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_GAIN, env.getLateReverbGain());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_DELAY, env.getLateReverbDelay());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_AIR_ABSORPTION_GAINHF, env.getAirAbsorbGainHf());
+ EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_ROOM_ROLLOFF_FACTOR, env.getRoomRolloffFactor());
+
+ // attach effect to slot
+ EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);
+ }
+ }
+
+ private boolean fillBuffer(AudioStream stream, int id){
+ int size = 0;
+ int result;
+
+ while (size < arrayBuf.length){
+ result = stream.readSamples(arrayBuf, size, arrayBuf.length - size);
+
+ if(result > 0){
+ size += result;
+ }else{
+ break;
+ }
+ }
+
+ if(size == 0)
+ return false;
+
+ nativeBuf.clear();
+ nativeBuf.put(arrayBuf, 0, size);
+ nativeBuf.flip();
+
+ alBufferData(id, convertFormat(stream), nativeBuf, stream.getSampleRate());
+
+ return true;
+ }
+
+ private boolean fillStreamingSource(int sourceId, AudioStream stream){
+ if (!stream.isOpen())
+ return false;
+
+ boolean active = true;
+ int processed = alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);
+
+// while((processed--) != 0){
+ if (processed > 0){
+ int buffer;
+
+ ib.position(0).limit(1);
+ alSourceUnqueueBuffers(sourceId, ib);
+ buffer = ib.get(0);
+
+ active = fillBuffer(stream, buffer);
+
+ ib.position(0).limit(1);
+ ib.put(0, buffer);
+ alSourceQueueBuffers(sourceId, ib);
+ }
+
+ if (!active && stream.isOpen())
+ stream.close();
+
+ return active;
+ }
+
+ private boolean attachStreamToSource(int sourceId, AudioStream stream){
+ boolean active = true;
+ for (int id : stream.getIds()){
+ active = fillBuffer(stream, id);
+ ib.position(0).limit(1);
+ ib.put(id).flip();
+ alSourceQueueBuffers(sourceId, ib);
+ }
+ return active;
+ }
+
+ private boolean attachBufferToSource(int sourceId, AudioBuffer buffer){
+ alSourcei(sourceId, AL_BUFFER, buffer.getId());
+ return true;
+ }
+
+ private boolean attachAudioToSource(int sourceId, AudioData data){
+ if (data instanceof AudioBuffer){
+ return attachBufferToSource(sourceId, (AudioBuffer) data);
+ }else if (data instanceof AudioStream){
+ return attachStreamToSource(sourceId, (AudioStream) data);
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ private void clearChannel(int index){
+ // make room at this channel
+ if (chanSrcs[index] != null){
+ AudioNode src = chanSrcs[index];
+
+ int sourceId = channels[index];
+ alSourceStop(sourceId);
+
+ if (src.getAudioData() instanceof AudioStream){
+ AudioStream str = (AudioStream) src.getAudioData();
+ ib.position(0).limit(STREAMING_BUFFER_COUNT);
+ ib.put(str.getIds()).flip();
+ alSourceUnqueueBuffers(sourceId, ib);
+ }else if (src.getAudioData() instanceof AudioBuffer){
+ alSourcei(sourceId, AL_BUFFER, 0);
+ }
+
+ if (src.getDryFilter() != null && supportEfx){
+ // detach filter
+ alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);
+ }
+ if (src.isPositional()){
+ AudioNode pas = (AudioNode) src;
+ if (pas.isReverbEnabled() && supportEfx) {
+ AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);
+ }
+ }
+
+ chanSrcs[index] = null;
+ }
+ }
+
+ public void update(float tpf){
+ // does nothing
+ }
+
+ public void updateInThread(float tpf){
+ if (audioDisabled)
+ return;
+
+ for (int i = 0; i < channels.length; i++){
+ AudioNode src = chanSrcs[i];
+ if (src == null)
+ continue;
+
+ int sourceId = channels[i];
+
+ // is the source bound to this channel
+ // if false, it's an instanced playback
+ boolean boundSource = i == src.getChannel();
+
+ // source's data is streaming
+ boolean streaming = src.getAudioData() instanceof AudioStream;
+
+ // only buffered sources can be bound
+ assert (boundSource && streaming) || (!streaming);
+
+ int state = alGetSourcei(sourceId, AL_SOURCE_STATE);
+ boolean wantPlaying = src.getStatus() == Status.Playing;
+ boolean stopped = state == AL_STOPPED;
+
+ if (streaming && wantPlaying){
+ AudioStream stream = (AudioStream) src.getAudioData();
+ if (stream.isOpen()){
+ fillStreamingSource(sourceId, stream);
+ if (stopped)
+ alSourcePlay(sourceId);
+ }else{
+ if (stopped){
+ // became inactive
+ src.setStatus(Status.Stopped);
+ src.setChannel(-1);
+ clearChannel(i);
+ freeChannel(i);
+
+ // And free the audio since it cannot be
+ // played again anyway.
+ deleteAudioData(stream);
+ }
+ }
+ }else if (!streaming){
+ boolean paused = state == AL_PAUSED;
+
+ // make sure OAL pause state & source state coincide
+ assert (src.getStatus() == Status.Paused && paused) || (!paused);
+
+ if (stopped){
+ if (boundSource){
+ src.setStatus(Status.Stopped);
+ src.setChannel(-1);
+ }
+ clearChannel(i);
+ freeChannel(i);
+ }
+ }
+ }
+
+ // Delete any unused objects.
+ objManager.deleteUnused(this);
+ }
+
+ public void setListener(Listener listener) {
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ if (this.listener != null){
+ // previous listener no longer associated with current
+ // renderer
+ this.listener.setRenderer(null);
+ }
+
+ this.listener = listener;
+ this.listener.setRenderer(this);
+ setListenerParams(listener);
+ }
+ }
+
+ public void playSourceInstance(AudioNode src){
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ if (src.getAudioData() instanceof AudioStream)
+ throw new UnsupportedOperationException(
+ "Cannot play instances " +
+ "of audio streams. Use playSource() instead.");
+
+ if (src.getAudioData().isUpdateNeeded()){
+ updateAudioData(src.getAudioData());
+ }
+
+ // create a new index for an audio-channel
+ int index = newChannel();
+ if (index == -1)
+ return;
+
+ int sourceId = channels[index];
+
+ clearChannel(index);
+
+ // set parameters, like position and max distance
+ setSourceParams(sourceId, src, true);
+ attachAudioToSource(sourceId, src.getAudioData());
+ chanSrcs[index] = src;
+
+ // play the channel
+ alSourcePlay(sourceId);
+ }
+ }
+
+
+ public void playSource(AudioNode src) {
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ //assert src.getStatus() == Status.Stopped || src.getChannel() == -1;
+
+ if (src.getStatus() == Status.Playing){
+ return;
+ }else if (src.getStatus() == Status.Stopped){
+
+ // allocate channel to this source
+ int index = newChannel();
+ if (index == -1) {
+ logger.log(Level.WARNING, "No channel available to play {0}", src);
+ return;
+ }
+ clearChannel(index);
+ src.setChannel(index);
+
+ AudioData data = src.getAudioData();
+ if (data.isUpdateNeeded())
+ updateAudioData(data);
+
+ chanSrcs[index] = src;
+ setSourceParams(channels[index], src, false);
+ attachAudioToSource(channels[index], data);
+ }
+
+ alSourcePlay(channels[src.getChannel()]);
+ src.setStatus(Status.Playing);
+ }
+ }
+
+
+ public void pauseSource(AudioNode src) {
+ checkDead();
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ if (src.getStatus() == Status.Playing){
+ assert src.getChannel() != -1;
+
+ alSourcePause(channels[src.getChannel()]);
+ src.setStatus(Status.Paused);
+ }
+ }
+ }
+
+
+ public void stopSource(AudioNode src) {
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ if (src.getStatus() != Status.Stopped){
+ int chan = src.getChannel();
+ assert chan != -1; // if it's not stopped, must have id
+
+ src.setStatus(Status.Stopped);
+ src.setChannel(-1);
+ clearChannel(chan);
+ freeChannel(chan);
+
+ if (src.getAudioData() instanceof AudioStream) {
+ AudioStream stream = (AudioStream)src.getAudioData();
+ if (stream.isOpen()) {
+ stream.close();
+ }
+
+ // And free the audio since it cannot be
+ // played again anyway.
+ deleteAudioData(src.getAudioData());
+ }
+ }
+ }
+ }
+
+ private int convertFormat(AudioData ad){
+ switch (ad.getBitsPerSample()){
+ case 8:
+ if (ad.getChannels() == 1)
+ return AL_FORMAT_MONO8;
+ else if (ad.getChannels() == 2)
+ return AL_FORMAT_STEREO8;
+
+ break;
+ case 16:
+ if (ad.getChannels() == 1)
+ return AL_FORMAT_MONO16;
+ else
+ return AL_FORMAT_STEREO16;
+ }
+ throw new UnsupportedOperationException("Unsupported channels/bits combination: "+
+ "bits="+ad.getBitsPerSample()+", channels="+ad.getChannels());
+ }
+
+ private void updateAudioBuffer(AudioBuffer ab){
+ int id = ab.getId();
+ if (ab.getId() == -1){
+ ib.position(0).limit(1);
+ alGenBuffers(ib);
+ id = ib.get(0);
+ ab.setId(id);
+
+ objManager.registerForCleanup(ab);
+ }
+
+ ab.getData().clear();
+ alBufferData(id, convertFormat(ab), ab.getData(), ab.getSampleRate());
+ ab.clearUpdateNeeded();
+ }
+
+ private void updateAudioStream(AudioStream as){
+ if (as.getIds() != null){
+ deleteAudioData(as);
+ }
+
+ int[] ids = new int[STREAMING_BUFFER_COUNT];
+ ib.position(0).limit(STREAMING_BUFFER_COUNT);
+ alGenBuffers(ib);
+ ib.position(0).limit(STREAMING_BUFFER_COUNT);
+ ib.get(ids);
+
+ // Not registered with object manager.
+ // AudioStreams can be handled without object manager
+ // since their lifecycle is known to the audio renderer.
+
+ as.setIds(ids);
+ as.clearUpdateNeeded();
+ }
+
+ private void updateAudioData(AudioData ad){
+ if (ad instanceof AudioBuffer){
+ updateAudioBuffer((AudioBuffer) ad);
+ }else if (ad instanceof AudioStream){
+ updateAudioStream((AudioStream) ad);
+ }
+ }
+
+ public void deleteFilter(Filter filter) {
+ int id = filter.getId();
+ if (id != -1){
+ EFX10.alDeleteFilters(id);
+ }
+ }
+
+ public void deleteAudioData(AudioData ad){
+ synchronized (threadLock){
+ while (!threadLock.get()){
+ try {
+ threadLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ if (audioDisabled)
+ return;
+
+ if (ad instanceof AudioBuffer){
+ AudioBuffer ab = (AudioBuffer) ad;
+ int id = ab.getId();
+ if (id != -1){
+ ib.put(0,id);
+ ib.position(0).limit(1);
+ alDeleteBuffers(ib);
+ ab.resetObject();
+ }
+ }else if (ad instanceof AudioStream){
+ AudioStream as = (AudioStream) ad;
+ int[] ids = as.getIds();
+ if (ids != null){
+ ib.clear();
+ ib.put(ids).flip();
+ alDeleteBuffers(ib);
+ as.resetObject();
+ }
+ }
+ }
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/input/lwjgl/JInputJoyInput.java b/engine/src/lwjgl/com/jme3/input/lwjgl/JInputJoyInput.java
new file mode 100644
index 0000000..28e35f0
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/input/lwjgl/JInputJoyInput.java
@@ -0,0 +1,194 @@
+package com.jme3.input.lwjgl;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.JoyInput;
+import com.jme3.input.Joystick;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.JoyAxisEvent;
+import com.jme3.input.event.JoyButtonEvent;
+import com.jme3.util.IntMap;
+import java.util.HashMap;
+import net.java.games.input.Component.Identifier;
+import net.java.games.input.Component.Identifier.Axis;
+import net.java.games.input.Component.Identifier.Button;
+import net.java.games.input.Component.POV;
+import net.java.games.input.*;
+
+public class JInputJoyInput implements JoyInput {
+
+ private boolean inited = false;
+ private Joystick[] joysticks;
+ private RawInputListener listener;
+
+ private HashMap<Button, Integer>[] buttonIdsToIndices;
+ private HashMap<Axis, Integer>[] axisIdsToIndices;
+ private HashMap<Controller, Integer> controllerToIndices;
+ private IntMap<Controller> indicesToController;
+
+ private int xAxis, yAxis;
+
+ private void loadIdentifiers(int controllerIdx, Controller c){
+ Component[] ces = c.getComponents();
+ int numButtons = 0;
+ int numAxes = 0;
+ xAxis = -1;
+ yAxis = -1;
+ for (Component comp : ces){
+ Identifier id = comp.getIdentifier();
+ if (id instanceof Button){
+ buttonIdsToIndices[controllerIdx].put((Button)id, numButtons);
+ numButtons ++;
+ }else if (id instanceof Axis){
+ Axis axis = (Axis) id;
+ if (axis == Axis.X){
+ xAxis = numAxes;
+ }else if (axis == Axis.Y){
+ yAxis = numAxes;
+ }
+
+ axisIdsToIndices[controllerIdx].put((Axis)id, numAxes);
+ numAxes ++;
+ }
+ }
+ }
+
+ public void setJoyRumble(int joyId, float amount){
+ Controller c = indicesToController.get(joyId);
+ if (c == null)
+ throw new IllegalArgumentException();
+
+ for (Rumbler r : c.getRumblers()){
+ r.rumble(amount);
+ }
+ }
+
+ public Joystick[] loadJoysticks(InputManager inputManager){
+ ControllerEnvironment ce =
+ ControllerEnvironment.getDefaultEnvironment();
+
+ int joyIndex = 0;
+ controllerToIndices = new HashMap<Controller, Integer>();
+ indicesToController = new IntMap<Controller>();
+ Controller[] cs = ce.getControllers();
+ for (int i = 0; i < cs.length; i++){
+ Controller c = cs[i];
+ if (c.getType() == Controller.Type.KEYBOARD
+ || c.getType() == Controller.Type.MOUSE)
+ continue;
+
+ controllerToIndices.put(c, joyIndex);
+ indicesToController.put(joyIndex, c);
+ joyIndex ++;
+ }
+
+ buttonIdsToIndices = new HashMap[joyIndex];
+ axisIdsToIndices = new HashMap[joyIndex];
+ joysticks = new Joystick[joyIndex];
+
+ joyIndex = 0;
+
+ for (int i = 0; i < cs.length; i++){
+ Controller c = cs[i];
+ if (c.getType() == Controller.Type.KEYBOARD
+ || c.getType() == Controller.Type.MOUSE)
+ continue;
+
+ buttonIdsToIndices[joyIndex] = new HashMap<Button, Integer>();
+ axisIdsToIndices[joyIndex] = new HashMap<Axis, Integer>();
+ loadIdentifiers(joyIndex, c);
+ Joystick joy = new Joystick(inputManager,
+ this,
+ joyIndex, c.getName(),
+ buttonIdsToIndices[joyIndex].size(),
+ axisIdsToIndices[joyIndex].size(),
+ xAxis, yAxis);
+ joysticks[joyIndex] = joy;
+ joyIndex++;
+ }
+
+ return joysticks;
+ }
+
+ public void initialize() {
+ inited = true;
+ }
+
+ public void update() {
+ ControllerEnvironment ce =
+ ControllerEnvironment.getDefaultEnvironment();
+
+ Controller[] cs = ce.getControllers();
+ Event e = new Event();
+ for (int i = 0; i < cs.length; i++){
+ Controller c = cs[i];
+ if (c.getType() == Controller.Type.UNKNOWN
+ || c.getType() == Controller.Type.KEYBOARD
+ || c.getType() == Controller.Type.MOUSE)
+ continue;
+
+ if (!c.poll())
+ continue;
+
+ int joyId = controllerToIndices.get(c);
+ EventQueue q = c.getEventQueue();
+ while (q.getNextEvent(e)){
+ Identifier id = e.getComponent().getIdentifier();
+ if (id == Identifier.Axis.POV){
+ float x = 0, y = 0;
+ float v = e.getValue();
+
+ if (v == POV.CENTER){
+ x = 0; y = 0;
+ }else if (v == POV.DOWN){
+ x = 0; y = -1f;
+ }else if (v == POV.DOWN_LEFT){
+ x = -1f; y = -1f;
+ }else if (v == POV.DOWN_RIGHT){
+ x = -1f; y = 1f;
+ }else if (v == POV.LEFT){
+ x = -1f; y = 0;
+ }else if (v == POV.RIGHT){
+ x = 1f; y = 0;
+ }else if (v == POV.UP){
+ x = 0; y = 1f;
+ }else if (v == POV.UP_LEFT){
+ x = -1f; y = 1f;
+ }else if (v == POV.UP_RIGHT){
+ x = 1f; y = 1f;
+ }
+
+ JoyAxisEvent evt1 = new JoyAxisEvent(joyId, JoyInput.AXIS_POV_X, x);
+ JoyAxisEvent evt2 = new JoyAxisEvent(joyId, JoyInput.AXIS_POV_Y, y);
+ listener.onJoyAxisEvent(evt1);
+ listener.onJoyAxisEvent(evt2);
+ }else if (id instanceof Axis){
+ float value = e.getValue();
+ Axis axis = (Axis) id;
+ JoyAxisEvent evt = new JoyAxisEvent(joyId, axisIdsToIndices[joyId].get(axis), value);
+ listener.onJoyAxisEvent(evt);
+ }else if (id instanceof Button){
+ Button button = (Button) id;
+ JoyButtonEvent evt = new JoyButtonEvent(joyId, buttonIdsToIndices[joyId].get(button), e.getValue() == 1f);
+ listener.onJoyButtonEvent(evt);
+ }
+ }
+ }
+ }
+
+ public void destroy() {
+ inited = false;
+ }
+
+ public boolean isInitialized() {
+ return inited;
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return 0;
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/input/lwjgl/LwjglKeyInput.java b/engine/src/lwjgl/com/jme3/input/lwjgl/LwjglKeyInput.java
new file mode 100644
index 0000000..1e31e60
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/input/lwjgl/LwjglKeyInput.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.lwjgl;
+
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.system.lwjgl.LwjglAbstractDisplay;
+import com.jme3.system.lwjgl.LwjglTimer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.Sys;
+import org.lwjgl.input.Keyboard;
+
+public class LwjglKeyInput implements KeyInput {
+
+ private static final Logger logger = Logger.getLogger(LwjglKeyInput.class.getName());
+
+ private LwjglAbstractDisplay context;
+
+ private RawInputListener listener;
+
+ public LwjglKeyInput(LwjglAbstractDisplay context){
+ this.context = context;
+ }
+
+ public void initialize() {
+ if (!context.isRenderable())
+ return;
+
+ try {
+ Keyboard.create();
+ Keyboard.enableRepeatEvents(true);
+ logger.info("Keyboard created.");
+ } catch (LWJGLException ex) {
+ logger.log(Level.SEVERE, "Error while creating keyboard.", ex);
+ }
+ }
+
+ public int getKeyCount(){
+ return Keyboard.KEYBOARD_SIZE;
+ }
+
+ public void update() {
+ if (!context.isRenderable())
+ return;
+
+ Keyboard.poll();
+ while (Keyboard.next()){
+ int keyCode = Keyboard.getEventKey();
+ char keyChar = Keyboard.getEventCharacter();
+ boolean pressed = Keyboard.getEventKeyState();
+ boolean down = Keyboard.isRepeatEvent();
+ long time = Keyboard.getEventNanoseconds();
+ KeyInputEvent evt = new KeyInputEvent(keyCode, keyChar, pressed, down);
+ evt.setTime(time);
+ listener.onKeyEvent(evt);
+ }
+ }
+
+ public void destroy() {
+ if (!context.isRenderable())
+ return;
+
+ Keyboard.destroy();
+ logger.info("Keyboard destroyed.");
+ }
+
+ public boolean isInitialized() {
+ return Keyboard.isCreated();
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS;
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/input/lwjgl/LwjglMouseInput.java b/engine/src/lwjgl/com/jme3/input/lwjgl/LwjglMouseInput.java
new file mode 100644
index 0000000..2e45d94
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/input/lwjgl/LwjglMouseInput.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.lwjgl;
+
+import com.jme3.input.MouseInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.system.lwjgl.LwjglAbstractDisplay;
+import com.jme3.system.lwjgl.LwjglTimer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.Sys;
+import org.lwjgl.input.Cursor;
+import org.lwjgl.input.Mouse;
+
+public class LwjglMouseInput implements MouseInput {
+
+ private static final Logger logger = Logger.getLogger(LwjglMouseInput.class.getName());
+
+ private LwjglAbstractDisplay context;
+
+ private RawInputListener listener;
+
+ private boolean supportHardwareCursor = false;
+ private boolean cursorVisible = true;
+
+ private int curX, curY, curWheel;
+
+ public LwjglMouseInput(LwjglAbstractDisplay context){
+ this.context = context;
+ }
+
+ public void initialize() {
+ if (!context.isRenderable())
+ return;
+
+ try {
+ Mouse.create();
+ logger.info("Mouse created.");
+ supportHardwareCursor = (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) != 0;
+
+ // Recall state that was set before initialization
+ Mouse.setGrabbed(!cursorVisible);
+ } catch (LWJGLException ex) {
+ logger.log(Level.SEVERE, "Error while creating mouse", ex);
+ }
+ }
+
+ public boolean isInitialized(){
+ return Mouse.isCreated();
+ }
+
+ public int getButtonCount(){
+ return Mouse.getButtonCount();
+ }
+
+ public void update() {
+ if (!context.isRenderable())
+ return;
+
+ while (Mouse.next()){
+ int btn = Mouse.getEventButton();
+
+ int wheelDelta = Mouse.getEventDWheel();
+ int xDelta = Mouse.getEventDX();
+ int yDelta = Mouse.getEventDY();
+ int x = Mouse.getX();
+ int y = Mouse.getY();
+
+ curWheel += wheelDelta;
+ if (cursorVisible){
+ xDelta = x - curX;
+ yDelta = y - curY;
+ curX = x;
+ curY = y;
+ }else{
+ x = curX + xDelta;
+ y = curY + yDelta;
+ curX = x;
+ curY = y;
+ }
+
+ if (xDelta != 0 || yDelta != 0 || wheelDelta != 0){
+ MouseMotionEvent evt = new MouseMotionEvent(x, y, xDelta, yDelta, curWheel, wheelDelta);
+ evt.setTime(Mouse.getEventNanoseconds());
+ listener.onMouseMotionEvent(evt);
+ }
+ if (btn != -1){
+ MouseButtonEvent evt = new MouseButtonEvent(btn,
+ Mouse.getEventButtonState(), x, y);
+ evt.setTime(Mouse.getEventNanoseconds());
+ listener.onMouseButtonEvent(evt);
+ }
+ }
+ }
+
+ public void destroy() {
+ if (!context.isRenderable())
+ return;
+
+ Mouse.destroy();
+ logger.info("Mouse destroyed.");
+ }
+
+ public void setCursorVisible(boolean visible){
+ cursorVisible = visible;
+ if (!context.isRenderable())
+ return;
+
+ Mouse.setGrabbed(!visible);
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS;
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java
new file mode 100644
index 0000000..ec41e0c
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java
@@ -0,0 +1,1169 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.light.*;
+import com.jme3.material.FixedFuncBinding;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.GL1Renderer;
+import com.jme3.renderer.RenderContext;
+import com.jme3.renderer.Statistics;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.NativeObjectManager;
+import java.nio.*;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jme3tools.converters.MipMapGenerator;
+import static org.lwjgl.opengl.GL11.*;
+import org.lwjgl.opengl.GL12;
+import org.lwjgl.opengl.GL14;
+import org.lwjgl.opengl.GLContext;
+
+public class LwjglGL1Renderer implements GL1Renderer {
+
+ private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName());
+ private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
+ private final StringBuilder stringBuf = new StringBuilder(250);
+ private final IntBuffer ib1 = BufferUtils.createIntBuffer(1);
+ private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
+ private final FloatBuffer fb16 = BufferUtils.createFloatBuffer(16);
+ private final FloatBuffer fb4Null = BufferUtils.createFloatBuffer(4);
+ private final RenderContext context = new RenderContext();
+ private final NativeObjectManager objManager = new NativeObjectManager();
+ private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
+ private int maxTexSize;
+ private int maxCubeTexSize;
+ private int maxVertCount;
+ private int maxTriCount;
+ private int maxLights;
+ private boolean gl12 = false;
+ private final Statistics statistics = new Statistics();
+ private int vpX, vpY, vpW, vpH;
+ private int clipX, clipY, clipW, clipH;
+
+ private Matrix4f worldMatrix = new Matrix4f();
+ private Matrix4f viewMatrix = new Matrix4f();
+
+ private ArrayList<Light> lightList = new ArrayList<Light>(8);
+ private ColorRGBA materialAmbientColor = new ColorRGBA();
+ private Vector3f tempVec = new Vector3f();
+
+ protected void updateNameBuffer() {
+ int len = stringBuf.length();
+
+ nameBuf.position(0);
+ nameBuf.limit(len);
+ for (int i = 0; i < len; i++) {
+ nameBuf.put((byte) stringBuf.charAt(i));
+ }
+
+ nameBuf.rewind();
+ }
+
+ public Statistics getStatistics() {
+ return statistics;
+ }
+
+ public EnumSet<Caps> getCaps() {
+ return caps;
+ }
+
+ public void initialize() {
+ if (GLContext.getCapabilities().OpenGL12){
+ gl12 = true;
+ }
+
+ // Default values for certain GL state.
+ glShadeModel(GL_SMOOTH);
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+ // Enable rescaling/normaling of normal vectors.
+ // Fixes lighting issues with scaled models.
+ if (gl12){
+ glEnable(GL12.GL_RESCALE_NORMAL);
+ }else{
+ glEnable(GL_NORMALIZE);
+ }
+
+ if (GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
+ caps.add(Caps.NonPowerOfTwoTextures);
+ } else {
+ logger.log(Level.WARNING, "Your graphics card does not "
+ + "support non-power-of-2 textures. "
+ + "Some features might not work.");
+ }
+
+ maxLights = glGetInteger(GL_MAX_LIGHTS);
+
+ }
+
+ public void invalidateState() {
+ context.reset();
+ }
+
+ public void resetGLObjects() {
+ logger.log(Level.INFO, "Reseting objects and invalidating state");
+ objManager.resetObjects();
+ statistics.clearMemory();
+ invalidateState();
+ }
+
+ public void cleanup() {
+ logger.log(Level.INFO, "Deleting objects and invalidating state");
+ objManager.deleteAllObjects(this);
+ statistics.clearMemory();
+ invalidateState();
+ }
+
+ public void setDepthRange(float start, float end) {
+ glDepthRange(start, end);
+ }
+
+ public void clearBuffers(boolean color, boolean depth, boolean stencil) {
+ int bits = 0;
+ if (color) {
+ //See explanations of the depth below, we must enable color write to be able to clear the color buffer
+ if (context.colorWriteEnabled == false) {
+ glColorMask(true, true, true, true);
+ context.colorWriteEnabled = true;
+ }
+ bits = GL_COLOR_BUFFER_BIT;
+ }
+ if (depth) {
+
+ //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
+ //here s some link on openl board
+ //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
+ //if depth clear is requested, we enable the depthMask
+ if (context.depthWriteEnabled == false) {
+ glDepthMask(true);
+ context.depthWriteEnabled = true;
+ }
+ bits |= GL_DEPTH_BUFFER_BIT;
+ }
+ if (stencil) {
+ bits |= GL_STENCIL_BUFFER_BIT;
+ }
+ if (bits != 0) {
+ glClear(bits);
+ }
+ }
+
+ public void setBackgroundColor(ColorRGBA color) {
+ glClearColor(color.r, color.g, color.b, color.a);
+ }
+
+ private void setMaterialColor(int type, ColorRGBA color, ColorRGBA defaultColor) {
+ if (color != null){
+ fb16.put(color.r).put(color.g).put(color.b).put(color.a).flip();
+ }else{
+ fb16.put(defaultColor.r).put(defaultColor.g).put(defaultColor.b).put(defaultColor.a).flip();
+ }
+ glMaterial(GL_FRONT_AND_BACK, type, fb16);
+ }
+
+ /**
+ * Applies fixed function bindings from the context to OpenGL
+ */
+ private void applyFixedFuncBindings(boolean forLighting){
+ if (forLighting){
+ glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, context.shininess);
+ setMaterialColor(GL_AMBIENT, context.ambient, ColorRGBA.DarkGray);
+ setMaterialColor(GL_DIFFUSE, context.diffuse, ColorRGBA.White);
+ setMaterialColor(GL_SPECULAR, context.specular, ColorRGBA.Black);
+
+ if (context.useVertexColor){
+ glEnable(GL_COLOR_MATERIAL);
+ }else{
+ glDisable(GL_COLOR_MATERIAL);
+ }
+ }else{
+ // Ignore other values as they have no effect when
+ // GL_LIGHTING is disabled.
+ ColorRGBA color = context.color;
+ if (color != null){
+ glColor4f(color.r, color.g, color.b, color.a);
+ }else{
+ glColor4f(1,1,1,1);
+ }
+ }
+ }
+
+ /**
+ * Reset fixed function bindings to default values.
+ */
+ private void resetFixedFuncBindings(){
+ context.color = null;
+ context.ambient = null;
+ context.diffuse = null;
+ context.specular = null;
+ context.shininess = 0;
+ context.useVertexColor = false;
+ }
+
+ public void setFixedFuncBinding(FixedFuncBinding ffBinding, Object val) {
+ switch (ffBinding) {
+ case Color:
+ context.color = (ColorRGBA) val;
+ break;
+ case MaterialAmbient:
+ context.ambient = (ColorRGBA) val;
+ break;
+ case MaterialDiffuse:
+ context.diffuse = (ColorRGBA) val;
+ break;
+ case MaterialSpecular:
+ context.specular = (ColorRGBA) val;
+ break;
+ case MaterialShininess:
+ context.shininess = (Float) val;
+ break;
+ case UseVertexColor:
+ context.useVertexColor = (Boolean) val;
+ break;
+ }
+ }
+
+ public void applyRenderState(RenderState state) {
+ if (state.isWireframe() && !context.wireframe) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ context.wireframe = true;
+ } else if (!state.isWireframe() && context.wireframe) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ context.wireframe = false;
+ }
+
+ if (state.isDepthTest() && !context.depthTestEnabled) {
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ context.depthTestEnabled = true;
+ } else if (!state.isDepthTest() && context.depthTestEnabled) {
+ glDisable(GL_DEPTH_TEST);
+ context.depthTestEnabled = false;
+ }
+
+ if (state.isAlphaTest() && !context.alphaTestEnabled) {
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_GREATER, state.getAlphaFallOff());
+ context.alphaTestEnabled = true;
+ } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
+ glDisable(GL_ALPHA_TEST);
+ context.alphaTestEnabled = false;
+ }
+
+ if (state.isDepthWrite() && !context.depthWriteEnabled) {
+ glDepthMask(true);
+ context.depthWriteEnabled = true;
+ } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
+ glDepthMask(false);
+ context.depthWriteEnabled = false;
+ }
+
+ if (state.isColorWrite() && !context.colorWriteEnabled) {
+ glColorMask(true, true, true, true);
+ context.colorWriteEnabled = true;
+ } else if (!state.isColorWrite() && context.colorWriteEnabled) {
+ glColorMask(false, false, false, false);
+ context.colorWriteEnabled = false;
+ }
+
+ if (state.isPointSprite()) {
+ logger.log(Level.WARNING, "Point Sprite unsupported!");
+ }
+
+ if (state.isPolyOffset()) {
+ if (!context.polyOffsetEnabled) {
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ context.polyOffsetEnabled = true;
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ } else {
+ if (state.getPolyOffsetFactor() != context.polyOffsetFactor
+ || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
+ glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }
+ }
+ } else {
+ if (context.polyOffsetEnabled) {
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ context.polyOffsetEnabled = false;
+ context.polyOffsetFactor = 0;
+ context.polyOffsetUnits = 0;
+ }
+ }
+ if (state.getFaceCullMode() != context.cullMode) {
+ if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
+ glDisable(GL_CULL_FACE);
+ } else {
+ glEnable(GL_CULL_FACE);
+ }
+
+ switch (state.getFaceCullMode()) {
+ case Off:
+ break;
+ case Back:
+ glCullFace(GL_BACK);
+ break;
+ case Front:
+ glCullFace(GL_FRONT);
+ break;
+ case FrontAndBack:
+ glCullFace(GL_FRONT_AND_BACK);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized face cull mode: "
+ + state.getFaceCullMode());
+ }
+
+ context.cullMode = state.getFaceCullMode();
+ }
+
+ if (state.getBlendMode() != context.blendMode) {
+ if (state.getBlendMode() == RenderState.BlendMode.Off) {
+ glDisable(GL_BLEND);
+ } else {
+ glEnable(GL_BLEND);
+ switch (state.getBlendMode()) {
+ case Off:
+ break;
+ case Additive:
+ glBlendFunc(GL_ONE, GL_ONE);
+ break;
+ case AlphaAdditive:
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ break;
+ case Color:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
+ break;
+ case Alpha:
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case PremultAlpha:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case Modulate:
+ glBlendFunc(GL_DST_COLOR, GL_ZERO);
+ break;
+ case ModulateX2:
+ glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized blend mode: "
+ + state.getBlendMode());
+ }
+ }
+
+ context.blendMode = state.getBlendMode();
+ }
+
+ if (state.isStencilTest()) {
+ throw new UnsupportedOperationException("OpenGL 1.1 doesn't support two sided stencil operations.");
+ }
+
+ }
+
+ public void setViewPort(int x, int y, int w, int h) {
+ if (x != vpX || vpY != y || vpW != w || vpH != h) {
+ glViewport(x, y, w, h);
+ vpX = x;
+ vpY = y;
+ vpW = w;
+ vpH = h;
+ }
+ }
+
+ public void setClipRect(int x, int y, int width, int height) {
+ if (!context.clipRectEnabled) {
+ glEnable(GL_SCISSOR_TEST);
+ context.clipRectEnabled = true;
+ }
+ if (clipX != x || clipY != y || clipW != width || clipH != height) {
+ glScissor(x, y, width, height);
+ clipX = x;
+ clipY = y;
+ clipW = width;
+ clipH = height;
+ }
+ }
+
+ public void clearClipRect() {
+ if (context.clipRectEnabled) {
+ glDisable(GL_SCISSOR_TEST);
+ context.clipRectEnabled = false;
+
+ clipX = 0;
+ clipY = 0;
+ clipW = 0;
+ clipH = 0;
+ }
+ }
+
+ public void onFrame() {
+ objManager.deleteUnused(this);
+// statistics.clearFrame();
+ }
+
+ private FloatBuffer storeMatrix(Matrix4f matrix, FloatBuffer store) {
+ store.clear();
+ matrix.fillFloatBuffer(store, true);
+ store.clear();
+ return store;
+ }
+
+ private void setModelView(Matrix4f modelMatrix, Matrix4f viewMatrix){
+ if (context.matrixMode != GL_MODELVIEW) {
+ glMatrixMode(GL_MODELVIEW);
+ context.matrixMode = GL_MODELVIEW;
+ }
+
+ glLoadMatrix(storeMatrix(viewMatrix, fb16));
+ glMultMatrix(storeMatrix(modelMatrix, fb16));
+ }
+
+ private void setProjection(Matrix4f projMatrix){
+ if (context.matrixMode != GL_PROJECTION) {
+ glMatrixMode(GL_PROJECTION);
+ context.matrixMode = GL_PROJECTION;
+ }
+
+ glLoadMatrix(storeMatrix(projMatrix, fb16));
+ }
+
+ public void setWorldMatrix(Matrix4f worldMatrix) {
+ this.worldMatrix.set(worldMatrix);
+ }
+
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
+ this.viewMatrix.set(viewMatrix);
+ setProjection(projMatrix);
+ }
+
+ public void setLighting(LightList list) {
+ // XXX: This is abuse of setLighting() to
+ // apply fixed function bindings
+ // and do other book keeping.
+ if (list == null || list.size() == 0){
+ glDisable(GL_LIGHTING);
+ applyFixedFuncBindings(false);
+ setModelView(worldMatrix, viewMatrix);
+ return;
+ }
+
+ // Number of lights set previously
+ int numLightsSetPrev = lightList.size();
+
+ // If more than maxLights are defined, they will be ignored.
+ // The GL1 renderer is not permitted to crash due to a
+ // GL1 limitation. It must render anything that the GL2 renderer
+ // can render (even incorrectly).
+ lightList.clear();
+ materialAmbientColor.set(0, 0, 0, 0);
+
+ for (int i = 0; i < list.size(); i++){
+ Light l = list.get(i);
+ if (l.getType() == Light.Type.Ambient){
+ // Gather
+ materialAmbientColor.addLocal(l.getColor());
+ }else{
+ // Add to list
+ lightList.add(l);
+
+ // Once maximum lights reached, exit loop.
+ if (lightList.size() >= maxLights){
+ break;
+ }
+ }
+ }
+
+ applyFixedFuncBindings(true);
+
+ glEnable(GL_LIGHTING);
+
+ fb16.clear();
+ fb16.put(materialAmbientColor.r)
+ .put(materialAmbientColor.g)
+ .put(materialAmbientColor.b)
+ .put(1).flip();
+
+ glLightModel(GL_LIGHT_MODEL_AMBIENT, fb16);
+
+ if (context.matrixMode != GL_MODELVIEW) {
+ glMatrixMode(GL_MODELVIEW);
+ context.matrixMode = GL_MODELVIEW;
+ }
+ // Lights are already in world space, so just convert
+ // them to view space.
+ glLoadMatrix(storeMatrix(viewMatrix, fb16));
+
+ for (int i = 0; i < lightList.size(); i++){
+ int glLightIndex = GL_LIGHT0 + i;
+ Light light = lightList.get(i);
+ Light.Type lightType = light.getType();
+ ColorRGBA col = light.getColor();
+ Vector3f pos;
+
+ // Enable the light
+ glEnable(glLightIndex);
+
+ // OGL spec states default value for light ambient is black
+ switch (lightType){
+ case Directional:
+ DirectionalLight dLight = (DirectionalLight) light;
+
+ fb16.clear();
+ fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
+ glLight(glLightIndex, GL_DIFFUSE, fb16);
+ glLight(glLightIndex, GL_SPECULAR, fb16);
+
+ pos = tempVec.set(dLight.getDirection()).negateLocal().normalizeLocal();
+ fb16.clear();
+ fb16.put(pos.x).put(pos.y).put(pos.z).put(0.0f).flip();
+ glLight(glLightIndex, GL_POSITION, fb16);
+ glLightf(glLightIndex, GL_SPOT_CUTOFF, 180);
+ break;
+ case Point:
+ PointLight pLight = (PointLight) light;
+
+ fb16.clear();
+ fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
+ glLight(glLightIndex, GL_DIFFUSE, fb16);
+ glLight(glLightIndex, GL_SPECULAR, fb16);
+
+ pos = pLight.getPosition();
+ fb16.clear();
+ fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip();
+ glLight(glLightIndex, GL_POSITION, fb16);
+ glLightf(glLightIndex, GL_SPOT_CUTOFF, 180);
+
+ if (pLight.getRadius() > 0) {
+ // Note: this doesn't follow the same attenuation model
+ // as the one used in the lighting shader.
+ glLightf(glLightIndex, GL_CONSTANT_ATTENUATION, 1);
+ glLightf(glLightIndex, GL_LINEAR_ATTENUATION, pLight.getInvRadius() * 2);
+ glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, pLight.getInvRadius() * pLight.getInvRadius());
+ }else{
+ glLightf(glLightIndex, GL_CONSTANT_ATTENUATION, 1);
+ glLightf(glLightIndex, GL_LINEAR_ATTENUATION, 0);
+ glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, 0);
+ }
+
+ break;
+ case Spot:
+ SpotLight sLight = (SpotLight) light;
+
+ fb16.clear();
+ fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip();
+ glLight(glLightIndex, GL_DIFFUSE, fb16);
+ glLight(glLightIndex, GL_SPECULAR, fb16);
+
+ pos = sLight.getPosition();
+ fb16.clear();
+ fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip();
+ glLight(glLightIndex, GL_POSITION, fb16);
+
+ Vector3f dir = sLight.getDirection();
+ fb16.clear();
+ fb16.put(dir.x).put(dir.y).put(dir.z).put(1.0f).flip();
+ glLight(glLightIndex, GL_SPOT_DIRECTION, fb16);
+
+ float outerAngleRad = sLight.getSpotOuterAngle();
+ float innerAngleRad = sLight.getSpotInnerAngle();
+ float spotCut = outerAngleRad * FastMath.RAD_TO_DEG;
+ float spotExpo = 0.0f;
+ if (outerAngleRad > 0) {
+ spotExpo = (1.0f - (innerAngleRad / outerAngleRad)) * 128.0f;
+ }
+
+ glLightf(glLightIndex, GL_SPOT_CUTOFF, spotCut);
+ glLightf(glLightIndex, GL_SPOT_EXPONENT, spotExpo);
+
+ if (sLight.getSpotRange() > 0) {
+ glLightf(glLightIndex, GL_LINEAR_ATTENUATION, sLight.getInvSpotRange());
+ }else{
+ glLightf(glLightIndex, GL_LINEAR_ATTENUATION, 0);
+ }
+
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "Unrecognized light type: " + lightType);
+ }
+ }
+
+ // Disable lights after the index
+ for (int i = lightList.size(); i < numLightsSetPrev; i++){
+ glDisable(GL_LIGHT0 + i);
+ }
+
+ // This will set view matrix as well.
+ setModelView(worldMatrix, viewMatrix);
+ }
+
+ private int convertTextureType(Texture.Type type) {
+ switch (type) {
+ case TwoDimensional:
+ return GL_TEXTURE_2D;
+// case ThreeDimensional:
+// return GL_TEXTURE_3D;
+// case CubeMap:
+// return GL_TEXTURE_CUBE_MAP;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + type);
+ }
+ }
+
+ private int convertMagFilter(Texture.MagFilter filter) {
+ switch (filter) {
+ case Bilinear:
+ return GL_LINEAR;
+ case Nearest:
+ return GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown mag filter: " + filter);
+ }
+ }
+
+ private int convertMinFilter(Texture.MinFilter filter) {
+ switch (filter) {
+ case Trilinear:
+ return GL_LINEAR_MIPMAP_LINEAR;
+ case BilinearNearestMipMap:
+ return GL_LINEAR_MIPMAP_NEAREST;
+ case NearestLinearMipMap:
+ return GL_NEAREST_MIPMAP_LINEAR;
+ case NearestNearestMipMap:
+ return GL_NEAREST_MIPMAP_NEAREST;
+ case BilinearNoMipMaps:
+ return GL_LINEAR;
+ case NearestNoMipMaps:
+ return GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown min filter: " + filter);
+ }
+ }
+
+ private int convertWrapMode(Texture.WrapMode mode) {
+ switch (mode) {
+ case EdgeClamp:
+ case Clamp:
+ case BorderClamp:
+ return GL_CLAMP;
+ case Repeat:
+ return GL_REPEAT;
+ default:
+ throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
+ }
+ }
+
+ private void setupTextureParams(Texture tex) {
+ int target = convertTextureType(tex.getType());
+
+ // filter things
+ int minFilter = convertMinFilter(tex.getMinFilter());
+ int magFilter = convertMagFilter(tex.getMagFilter());
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
+
+ // repeat modes
+ switch (tex.getType()) {
+// case ThreeDimensional:
+// case CubeMap:
+// glTexParameteri(target, GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
+ case TwoDimensional:
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
+ // fall down here is intentional..
+// case OneDimensional:
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
+ }
+ }
+
+ public void updateTexImageData(Image img, Texture.Type type, boolean mips, int unit) {
+ int texId = img.getId();
+ if (texId == -1) {
+ // create texture
+ glGenTextures(ib1);
+ texId = ib1.get(0);
+ img.setId(texId);
+ objManager.registerForCleanup(img);
+
+ statistics.onNewTexture();
+ }
+
+ // bind texture
+ int target = convertTextureType(type);
+// if (context.boundTextureUnit != unit) {
+// glActiveTexture(GL_TEXTURE0 + unit);
+// context.boundTextureUnit = unit;
+// }
+ if (context.boundTextures[unit] != img) {
+ glEnable(target);
+ glBindTexture(target, texId);
+ context.boundTextures[unit] = img;
+
+ statistics.onTextureUse(img, true);
+ }
+
+ // Check sizes if graphics card doesn't support NPOT
+ if (!GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
+ if (img.getWidth() != 0 && img.getHeight() != 0) {
+ if (!FastMath.isPowerOfTwo(img.getWidth())
+ || !FastMath.isPowerOfTwo(img.getHeight())) {
+
+ // Resize texture to Power-of-2 size
+ MipMapGenerator.resizeToPowerOf2(img);
+
+ }
+ }
+ }
+
+ if (!img.hasMipmaps() && mips) {
+ // No pregenerated mips available,
+ // generate from base level if required
+
+ // Check if hardware mips are supported
+ if (GLContext.getCapabilities().OpenGL14) {
+ glTexParameteri(target, GL14.GL_GENERATE_MIPMAP, GL_TRUE);
+ } else {
+ MipMapGenerator.generateMipMaps(img);
+ }
+ } else {
+ }
+
+ /*
+ if (target == GL_TEXTURE_CUBE_MAP) {
+ List<ByteBuffer> data = img.getData();
+ if (data.size() != 6) {
+ logger.log(Level.WARNING, "Invalid texture: {0}\n"
+ + "Cubemap textures must contain 6 data units.", img);
+ return;
+ }
+ for (int i = 0; i < 6; i++) {
+ TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc);
+ }
+ } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) {
+ List<ByteBuffer> data = img.getData();
+ // -1 index specifies prepare data for 2D Array
+ TextureUtil.uploadTexture(img, target, -1, 0, tdc);
+ for (int i = 0; i < data.size(); i++) {
+ // upload each slice of 2D array in turn
+ // this time with the appropriate index
+ TextureUtil.uploadTexture(img, target, i, 0, tdc);
+ }
+ } else {*/
+ TextureUtil.uploadTexture(img, target, 0, 0, false);
+ //}
+
+ img.clearUpdateNeeded();
+ }
+
+ public void setTexture(int unit, Texture tex) {
+ if (unit != 0 || tex.getType() != Texture.Type.TwoDimensional) {
+ //throw new UnsupportedOperationException();
+ return;
+ }
+
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded()) {
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), unit);
+ }
+
+ int texId = image.getId();
+ assert texId != -1;
+
+ Image[] textures = context.boundTextures;
+
+ int type = convertTextureType(tex.getType());
+// if (!context.textureIndexList.moveToNew(unit)) {
+// if (context.boundTextureUnit != unit){
+// glActiveTexture(GL_TEXTURE0 + unit);
+// context.boundTextureUnit = unit;
+// }
+// glEnable(type);
+// }
+
+// if (context.boundTextureUnit != unit) {
+// glActiveTexture(GL_TEXTURE0 + unit);
+// context.boundTextureUnit = unit;
+// }
+
+ if (textures[unit] != image) {
+ glEnable(type);
+ glBindTexture(type, texId);
+ textures[unit] = image;
+
+ statistics.onTextureUse(image, true);
+ } else {
+ statistics.onTextureUse(image, false);
+ }
+
+ setupTextureParams(tex);
+ }
+
+ private void clearTextureUnits() {
+ Image[] textures = context.boundTextures;
+ if (textures[0] != null) {
+ glDisable(GL_TEXTURE_2D);
+ textures[0] = null;
+ }
+ }
+
+ public void deleteImage(Image image) {
+ int texId = image.getId();
+ if (texId != -1) {
+ ib1.put(0, texId);
+ ib1.position(0).limit(1);
+ glDeleteTextures(ib1);
+ image.resetObject();
+ }
+ }
+
+ private int convertArrayType(VertexBuffer.Type type) {
+ switch (type) {
+ case Position:
+ return GL_VERTEX_ARRAY;
+ case Normal:
+ return GL_NORMAL_ARRAY;
+ case TexCoord:
+ return GL_TEXTURE_COORD_ARRAY;
+ case Color:
+ return GL_COLOR_ARRAY;
+ default:
+ return -1; // unsupported
+ }
+ }
+
+ private int convertVertexFormat(VertexBuffer.Format fmt) {
+ switch (fmt) {
+ case Byte:
+ return GL_BYTE;
+ case Float:
+ return GL_FLOAT;
+ case Int:
+ return GL_INT;
+ case Short:
+ return GL_SHORT;
+ case UnsignedByte:
+ return GL_UNSIGNED_BYTE;
+ case UnsignedInt:
+ return GL_UNSIGNED_INT;
+ case UnsignedShort:
+ return GL_UNSIGNED_SHORT;
+ default:
+ throw new UnsupportedOperationException("Unrecognized vertex format: " + fmt);
+ }
+ }
+
+ private int convertElementMode(Mesh.Mode mode) {
+ switch (mode) {
+ case Points:
+ return GL_POINTS;
+ case Lines:
+ return GL_LINES;
+ case LineLoop:
+ return GL_LINE_LOOP;
+ case LineStrip:
+ return GL_LINE_STRIP;
+ case Triangles:
+ return GL_TRIANGLES;
+ case TriangleFan:
+ return GL_TRIANGLE_FAN;
+ case TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ default:
+ throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
+ }
+ }
+
+ public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
+ if (count > 1) {
+ throw new UnsupportedOperationException();
+ }
+
+ glDrawArrays(convertElementMode(mode), 0, vertCount);
+ }
+
+ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
+ int arrayType = convertArrayType(vb.getBufferType());
+ if (arrayType == -1) {
+ return; // unsupported
+ }
+ glEnableClientState(arrayType);
+ context.boundAttribs[vb.getBufferType().ordinal()] = vb;
+
+ if (vb.getBufferType() == Type.Normal) {
+ // normalize if requested
+ if (vb.isNormalized() && !context.normalizeEnabled) {
+ glEnable(GL_NORMALIZE);
+ context.normalizeEnabled = true;
+ } else if (!vb.isNormalized() && context.normalizeEnabled) {
+ glDisable(GL_NORMALIZE);
+ context.normalizeEnabled = false;
+ }
+ }
+
+ // NOTE: Use data from interleaved buffer if specified
+ Buffer data = idb != null ? idb.getData() : vb.getData();
+ int comps = vb.getNumComponents();
+ int type = convertVertexFormat(vb.getFormat());
+
+ data.rewind();
+
+ switch (vb.getBufferType()) {
+ case Position:
+ if (!(data instanceof FloatBuffer)) {
+ throw new UnsupportedOperationException();
+ }
+
+ glVertexPointer(comps, vb.getStride(), (FloatBuffer) data);
+ break;
+ case Normal:
+ if (!(data instanceof FloatBuffer)) {
+ throw new UnsupportedOperationException();
+ }
+
+ glNormalPointer(vb.getStride(), (FloatBuffer) data);
+ break;
+ case Color:
+ if (data instanceof FloatBuffer) {
+ glColorPointer(comps, vb.getStride(), (FloatBuffer) data);
+ } else if (data instanceof ByteBuffer) {
+ glColorPointer(comps, true, vb.getStride(), (ByteBuffer) data);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ break;
+ case TexCoord:
+ if (!(data instanceof FloatBuffer)) {
+ throw new UnsupportedOperationException();
+ }
+
+ glTexCoordPointer(comps, vb.getStride(), (FloatBuffer) data);
+ break;
+ default:
+ // Ignore, this is an unsupported attribute for OpenGL1.
+ break;
+ }
+ }
+
+ public void setVertexAttrib(VertexBuffer vb) {
+ setVertexAttrib(vb, null);
+ }
+
+ private void drawElements(int mode, int format, Buffer data) {
+ switch (format) {
+ case GL_UNSIGNED_BYTE:
+ glDrawElements(mode, (ByteBuffer) data);
+ break;
+ case GL_UNSIGNED_SHORT:
+ glDrawElements(mode, (ShortBuffer) data);
+ break;
+ case GL_UNSIGNED_INT:
+ glDrawElements(mode, (IntBuffer) data);
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
+ Mesh.Mode mode = mesh.getMode();
+
+ Buffer indexData = indexBuf.getData();
+ indexData.rewind();
+
+ if (mesh.getMode() == Mode.Hybrid) {
+ throw new UnsupportedOperationException();
+ /*
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertVertexFormat(indexBuf.getFormat());
+ // int elSize = indexBuf.getFormat().getComponentSize();
+ // int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++) {
+ if (i == stripStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ } else if (i == fanStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+ indexData.position(curOffset);
+
+ drawElements(elMode,
+ fmt,
+ indexData);
+
+ curOffset += elementLength;
+ }*/
+ } else {
+ drawElements(convertElementMode(mode),
+ convertVertexFormat(indexBuf.getFormat()),
+ indexData);
+ }
+ }
+
+ public void clearVertexAttribs() {
+ for (int i = 0; i < 16; i++) {
+ VertexBuffer vb = context.boundAttribs[i];
+ if (vb != null) {
+ int arrayType = convertArrayType(vb.getBufferType());
+ glDisableClientState(arrayType);
+ context.boundAttribs[vb.getBufferType().ordinal()] = null;
+ }
+ }
+ }
+
+ private void renderMeshDefault(Mesh mesh, int lod, int count) {
+ VertexBuffer indices = null;
+
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()) {
+ updateBufferData(interleavedData);
+ }
+
+ IntMap<VertexBuffer> buffers = mesh.getBuffers();
+ if (mesh.getNumLodLevels() > 0) {
+ indices = mesh.getLodLevel(lod);
+ } else {
+ indices = buffers.get(Type.Index.ordinal());
+ }
+ for (Entry<VertexBuffer> entry : buffers) {
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
+
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttrib(vb);
+ } else {
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+
+ if (indices != null) {
+ drawTriangleList(indices, mesh, count);
+ } else {
+ glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+
+ // TODO: Fix these to use IDList??
+ clearVertexAttribs();
+ clearTextureUnits();
+ resetFixedFuncBindings();
+ }
+
+ public void renderMesh(Mesh mesh, int lod, int count) {
+ if (mesh.getVertexCount() == 0) {
+ return;
+ }
+
+ if (context.pointSize != mesh.getPointSize()) {
+ glPointSize(mesh.getPointSize());
+ context.pointSize = mesh.getPointSize();
+ }
+ if (context.lineWidth != mesh.getLineWidth()) {
+ glLineWidth(mesh.getLineWidth());
+ context.lineWidth = mesh.getLineWidth();
+ }
+
+ boolean dynamic = false;
+ if (mesh.getBuffer(Type.InterleavedData) != null) {
+ throw new UnsupportedOperationException("Interleaved meshes are not supported");
+ }
+
+ if (mesh.getNumLodLevels() == 0) {
+ IntMap<VertexBuffer> bufs = mesh.getBuffers();
+ for (Entry<VertexBuffer> entry : bufs) {
+ if (entry.getValue().getUsage() != VertexBuffer.Usage.Static) {
+ dynamic = true;
+ break;
+ }
+ }
+ } else {
+ dynamic = true;
+ }
+
+ statistics.onMeshDrawn(mesh, lod);
+
+// if (!dynamic) {
+ // dealing with a static object, generate display list
+// renderMeshDisplayList(mesh);
+// } else {
+ renderMeshDefault(mesh, lod, count);
+// }
+
+
+ }
+
+ public void setAlphaToCoverage(boolean value) {
+ }
+
+ public void setShader(Shader shader) {
+ }
+
+ public void deleteShader(Shader shader) {
+ }
+
+ public void deleteShaderSource(ShaderSource source) {
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
+ }
+
+ public void setMainFrameBufferOverride(FrameBuffer fb){
+ }
+
+ public void setFrameBuffer(FrameBuffer fb) {
+ }
+
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+ }
+
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ }
+
+ public void updateBufferData(VertexBuffer vb) {
+ }
+
+ public void deleteBuffer(VertexBuffer vb) {
+ }
+}
diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java
new file mode 100644
index 0000000..2c8707f
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java
@@ -0,0 +1,2468 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.light.LightList;
+import com.jme3.material.RenderState;
+import com.jme3.material.RenderState.StencilOperation;
+import com.jme3.material.RenderState.TestFunction;
+import com.jme3.math.*;
+import com.jme3.renderer.*;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.shader.Attribute;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.shader.Shader.ShaderType;
+import com.jme3.shader.Uniform;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.RenderBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.ListMap;
+import com.jme3.util.NativeObjectManager;
+import com.jme3.util.SafeArrayList;
+import java.nio.*;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jme3tools.converters.MipMapGenerator;
+import static org.lwjgl.opengl.ARBTextureMultisample.*;
+import static org.lwjgl.opengl.EXTFramebufferBlit.*;
+import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
+import static org.lwjgl.opengl.EXTFramebufferObject.*;
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL12.*;
+import static org.lwjgl.opengl.GL13.*;
+import static org.lwjgl.opengl.GL14.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import org.lwjgl.opengl.*;
+//import static org.lwjgl.opengl.ARBDrawInstanced.*;
+
+public class LwjglRenderer implements Renderer {
+
+ private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName());
+ private static final boolean VALIDATE_SHADER = false;
+ private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
+ private final StringBuilder stringBuf = new StringBuilder(250);
+ private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1);
+ private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
+ private final RenderContext context = new RenderContext();
+ private final NativeObjectManager objManager = new NativeObjectManager();
+ private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
+ // current state
+ private Shader boundShader;
+ private int initialDrawBuf, initialReadBuf;
+ private int glslVer;
+ private int vertexTextureUnits;
+ private int fragTextureUnits;
+ private int vertexUniforms;
+ private int fragUniforms;
+ private int vertexAttribs;
+ private int maxFBOSamples;
+ private int maxFBOAttachs;
+ private int maxMRTFBOAttachs;
+ private int maxRBSize;
+ private int maxTexSize;
+ private int maxCubeTexSize;
+ private int maxVertCount;
+ private int maxTriCount;
+ private int maxColorTexSamples;
+ private int maxDepthTexSamples;
+ private boolean tdc;
+ private FrameBuffer lastFb = null;
+ private FrameBuffer mainFbOverride = null;
+ private final Statistics statistics = new Statistics();
+ private int vpX, vpY, vpW, vpH;
+ private int clipX, clipY, clipW, clipH;
+
+ public LwjglRenderer() {
+ }
+
+ protected void updateNameBuffer() {
+ int len = stringBuf.length();
+
+ nameBuf.position(0);
+ nameBuf.limit(len);
+ for (int i = 0; i < len; i++) {
+ nameBuf.put((byte) stringBuf.charAt(i));
+ }
+
+ nameBuf.rewind();
+ }
+
+ public Statistics getStatistics() {
+ return statistics;
+ }
+
+ public EnumSet<Caps> getCaps() {
+ return caps;
+ }
+
+ @SuppressWarnings("fallthrough")
+ public void initialize() {
+ ContextCapabilities ctxCaps = GLContext.getCapabilities();
+ if (ctxCaps.OpenGL20) {
+ caps.add(Caps.OpenGL20);
+ if (ctxCaps.OpenGL21) {
+ caps.add(Caps.OpenGL21);
+ if (ctxCaps.OpenGL30) {
+ caps.add(Caps.OpenGL30);
+ if (ctxCaps.OpenGL31) {
+ caps.add(Caps.OpenGL31);
+ if (ctxCaps.OpenGL32) {
+ caps.add(Caps.OpenGL32);
+ }
+ }
+ }
+ }
+ }
+
+ String versionStr = null;
+ if (ctxCaps.OpenGL20) {
+ versionStr = glGetString(GL_SHADING_LANGUAGE_VERSION);
+ }
+ if (versionStr == null || versionStr.equals("")) {
+ glslVer = -1;
+ throw new UnsupportedOperationException("GLSL and OpenGL2 is "
+ + "required for the LWJGL "
+ + "renderer!");
+ }
+
+ // Fix issue in TestRenderToMemory when GL_FRONT is the main
+ // buffer being used.
+ initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
+ initialReadBuf = glGetInteger(GL_READ_BUFFER);
+
+ // XXX: This has to be GL_BACK for canvas on Mac
+ // Since initialDrawBuf is GL_FRONT for pbuffer, gotta
+ // change this value later on ...
+// initialDrawBuf = GL_BACK;
+// initialReadBuf = GL_BACK;
+
+ int spaceIdx = versionStr.indexOf(" ");
+ if (spaceIdx >= 1) {
+ versionStr = versionStr.substring(0, spaceIdx);
+ }
+
+ float version = Float.parseFloat(versionStr);
+ glslVer = (int) (version * 100);
+
+ switch (glslVer) {
+ default:
+ if (glslVer < 400) {
+ break;
+ }
+
+ // so that future OpenGL revisions wont break jme3
+
+ // fall through intentional
+ case 400:
+ case 330:
+ case 150:
+ caps.add(Caps.GLSL150);
+ case 140:
+ caps.add(Caps.GLSL140);
+ case 130:
+ caps.add(Caps.GLSL130);
+ case 120:
+ caps.add(Caps.GLSL120);
+ case 110:
+ caps.add(Caps.GLSL110);
+ case 100:
+ caps.add(Caps.GLSL100);
+ break;
+ }
+
+ if (!caps.contains(Caps.GLSL100)) {
+ logger.log(Level.WARNING, "Force-adding GLSL100 support, since OpenGL2 is supported.");
+ caps.add(Caps.GLSL100);
+ }
+
+ glGetInteger(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16);
+ vertexTextureUnits = intBuf16.get(0);
+ logger.log(Level.FINER, "VTF Units: {0}", vertexTextureUnits);
+ if (vertexTextureUnits > 0) {
+ caps.add(Caps.VertexTextureFetch);
+ }
+
+ glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16);
+ fragTextureUnits = intBuf16.get(0);
+ logger.log(Level.FINER, "Texture Units: {0}", fragTextureUnits);
+
+ glGetInteger(GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
+ vertexUniforms = intBuf16.get(0);
+ logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
+
+ glGetInteger(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
+ fragUniforms = intBuf16.get(0);
+ logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
+
+ glGetInteger(GL_MAX_VERTEX_ATTRIBS, intBuf16);
+ vertexAttribs = intBuf16.get(0);
+ logger.log(Level.FINER, "Vertex Attributes: {0}", vertexAttribs);
+
+ glGetInteger(GL_SUBPIXEL_BITS, intBuf16);
+ int subpixelBits = intBuf16.get(0);
+ logger.log(Level.FINER, "Subpixel Bits: {0}", subpixelBits);
+
+ glGetInteger(GL_MAX_ELEMENTS_VERTICES, intBuf16);
+ maxVertCount = intBuf16.get(0);
+ logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
+
+ glGetInteger(GL_MAX_ELEMENTS_INDICES, intBuf16);
+ maxTriCount = intBuf16.get(0);
+ logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
+
+ glGetInteger(GL_MAX_TEXTURE_SIZE, intBuf16);
+ maxTexSize = intBuf16.get(0);
+ logger.log(Level.FINER, "Maximum Texture Resolution: {0}", maxTexSize);
+
+ glGetInteger(GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16);
+ maxCubeTexSize = intBuf16.get(0);
+ logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
+
+ if (ctxCaps.GL_ARB_color_buffer_float) {
+ // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
+ if (ctxCaps.GL_ARB_half_float_pixel) {
+ caps.add(Caps.FloatColorBuffer);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_depth_buffer_float) {
+ caps.add(Caps.FloatDepthBuffer);
+ }
+
+ if (ctxCaps.GL_ARB_draw_instanced) {
+ caps.add(Caps.MeshInstancing);
+ }
+
+ if (ctxCaps.GL_ARB_fragment_program) {
+ caps.add(Caps.ARBprogram);
+ }
+
+ if (ctxCaps.GL_ARB_texture_buffer_object) {
+ caps.add(Caps.TextureBuffer);
+ }
+
+ if (ctxCaps.GL_ARB_texture_float) {
+ if (ctxCaps.GL_ARB_half_float_pixel) {
+ caps.add(Caps.FloatTexture);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_vertex_array_object) {
+ caps.add(Caps.VertexBufferArray);
+ }
+
+ if (ctxCaps.GL_ARB_texture_non_power_of_two) {
+ caps.add(Caps.NonPowerOfTwoTextures);
+ } else {
+ logger.log(Level.WARNING, "Your graphics card does not "
+ + "support non-power-of-2 textures. "
+ + "Some features might not work.");
+ }
+
+ boolean latc = ctxCaps.GL_EXT_texture_compression_latc;
+ boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc;
+ if (latc || atdc) {
+ caps.add(Caps.TextureCompressionLATC);
+ if (atdc && !latc) {
+ tdc = true;
+ }
+ }
+
+ if (ctxCaps.GL_EXT_packed_float) {
+ caps.add(Caps.PackedFloatColorBuffer);
+ if (ctxCaps.GL_ARB_half_float_pixel) {
+ // because textures are usually uploaded as RGB16F
+ // need half-float pixel
+ caps.add(Caps.PackedFloatTexture);
+ }
+ }
+
+ if (ctxCaps.GL_EXT_texture_array) {
+ caps.add(Caps.TextureArray);
+ }
+
+ if (ctxCaps.GL_EXT_texture_shared_exponent) {
+ caps.add(Caps.SharedExponentTexture);
+ }
+
+ if (ctxCaps.GL_EXT_framebuffer_object) {
+ caps.add(Caps.FrameBuffer);
+
+ glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
+ maxRBSize = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
+
+ glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16);
+ maxFBOAttachs = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
+
+ if (ctxCaps.GL_EXT_framebuffer_multisample) {
+ caps.add(Caps.FrameBufferMultisample);
+
+ glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
+ maxFBOSamples = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
+ }
+
+ if (ctxCaps.GL_ARB_texture_multisample) {
+ caps.add(Caps.TextureMultisample);
+
+ glGetInteger(GL_MAX_COLOR_TEXTURE_SAMPLES, intBuf16);
+ maxColorTexSamples = intBuf16.get(0);
+ logger.log(Level.FINER, "Texture Multisample Color Samples: {0}", maxColorTexSamples);
+
+ glGetInteger(GL_MAX_DEPTH_TEXTURE_SAMPLES, intBuf16);
+ maxDepthTexSamples = intBuf16.get(0);
+ logger.log(Level.FINER, "Texture Multisample Depth Samples: {0}", maxDepthTexSamples);
+ }
+
+ if (ctxCaps.GL_ARB_draw_buffers) {
+ caps.add(Caps.FrameBufferMRT);
+ glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16);
+ maxMRTFBOAttachs = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_multisample) {
+ glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16);
+ boolean available = intBuf16.get(0) != 0;
+ glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16);
+ int samples = intBuf16.get(0);
+ logger.log(Level.FINER, "Samples: {0}", samples);
+ boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB);
+ if (samples > 0 && available && !enabled) {
+ glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
+ }
+ }
+
+ logger.log(Level.INFO, "Caps: {0}", caps);
+ }
+
+ public void invalidateState() {
+ context.reset();
+ boundShader = null;
+ lastFb = null;
+
+ initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
+ initialReadBuf = glGetInteger(GL_READ_BUFFER);
+ }
+
+ public void resetGLObjects() {
+ logger.log(Level.INFO, "Reseting objects and invalidating state");
+ objManager.resetObjects();
+ statistics.clearMemory();
+ invalidateState();
+ }
+
+ public void cleanup() {
+ logger.log(Level.INFO, "Deleting objects and invalidating state");
+ objManager.deleteAllObjects(this);
+ statistics.clearMemory();
+ invalidateState();
+ }
+
+ private void checkCap(Caps cap) {
+ if (!caps.contains(cap)) {
+ throw new UnsupportedOperationException("Required capability missing: " + cap.name());
+ }
+ }
+
+ /*********************************************************************\
+ |* Render State *|
+ \*********************************************************************/
+ public void setDepthRange(float start, float end) {
+ glDepthRange(start, end);
+ }
+
+ public void clearBuffers(boolean color, boolean depth, boolean stencil) {
+ int bits = 0;
+ if (color) {
+ //See explanations of the depth below, we must enable color write to be able to clear the color buffer
+ if (context.colorWriteEnabled == false) {
+ glColorMask(true, true, true, true);
+ context.colorWriteEnabled = true;
+ }
+ bits = GL_COLOR_BUFFER_BIT;
+ }
+ if (depth) {
+
+ //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
+ //here s some link on openl board
+ //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
+ //if depth clear is requested, we enable the depthMask
+ if (context.depthWriteEnabled == false) {
+ glDepthMask(true);
+ context.depthWriteEnabled = true;
+ }
+ bits |= GL_DEPTH_BUFFER_BIT;
+ }
+ if (stencil) {
+ bits |= GL_STENCIL_BUFFER_BIT;
+ }
+ if (bits != 0) {
+ glClear(bits);
+ }
+ }
+
+ public void setBackgroundColor(ColorRGBA color) {
+ glClearColor(color.r, color.g, color.b, color.a);
+ }
+
+ public void setAlphaToCoverage(boolean value) {
+ if (value) {
+ glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
+ } else {
+ glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
+ }
+ }
+
+ public void applyRenderState(RenderState state) {
+ if (state.isWireframe() && !context.wireframe) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ context.wireframe = true;
+ } else if (!state.isWireframe() && context.wireframe) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ context.wireframe = false;
+ }
+
+ if (state.isDepthTest() && !context.depthTestEnabled) {
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ context.depthTestEnabled = true;
+ } else if (!state.isDepthTest() && context.depthTestEnabled) {
+ glDisable(GL_DEPTH_TEST);
+ context.depthTestEnabled = false;
+ }
+
+ if (state.isAlphaTest() && !context.alphaTestEnabled) {
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_GREATER, state.getAlphaFallOff());
+ context.alphaTestEnabled = true;
+ } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
+ glDisable(GL_ALPHA_TEST);
+ context.alphaTestEnabled = false;
+ }
+
+ if (state.isDepthWrite() && !context.depthWriteEnabled) {
+ glDepthMask(true);
+ context.depthWriteEnabled = true;
+ } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
+ glDepthMask(false);
+ context.depthWriteEnabled = false;
+ }
+
+ if (state.isColorWrite() && !context.colorWriteEnabled) {
+ glColorMask(true, true, true, true);
+ context.colorWriteEnabled = true;
+ } else if (!state.isColorWrite() && context.colorWriteEnabled) {
+ glColorMask(false, false, false, false);
+ context.colorWriteEnabled = false;
+ }
+
+ if (state.isPointSprite() && !context.pointSprite) {
+ // Only enable/disable sprite
+ if (context.boundTextures[0] != null){
+ if (context.boundTextureUnit != 0){
+ glActiveTexture(GL_TEXTURE0);
+ context.boundTextureUnit = 0;
+ }
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ }
+ context.pointSprite = true;
+ } else if (!state.isPointSprite() && context.pointSprite) {
+ if (context.boundTextures[0] != null){
+ if (context.boundTextureUnit != 0){
+ glActiveTexture(GL_TEXTURE0);
+ context.boundTextureUnit = 0;
+ }
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ context.pointSprite = false;
+ }
+ }
+
+ if (state.isPolyOffset()) {
+ if (!context.polyOffsetEnabled) {
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ context.polyOffsetEnabled = true;
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ } else {
+ if (state.getPolyOffsetFactor() != context.polyOffsetFactor
+ || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
+ glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }
+ }
+ } else {
+ if (context.polyOffsetEnabled) {
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ context.polyOffsetEnabled = false;
+ context.polyOffsetFactor = 0;
+ context.polyOffsetUnits = 0;
+ }
+ }
+
+ if (state.getFaceCullMode() != context.cullMode) {
+ if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
+ glDisable(GL_CULL_FACE);
+ } else {
+ glEnable(GL_CULL_FACE);
+ }
+
+ switch (state.getFaceCullMode()) {
+ case Off:
+ break;
+ case Back:
+ glCullFace(GL_BACK);
+ break;
+ case Front:
+ glCullFace(GL_FRONT);
+ break;
+ case FrontAndBack:
+ glCullFace(GL_FRONT_AND_BACK);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized face cull mode: "
+ + state.getFaceCullMode());
+ }
+
+ context.cullMode = state.getFaceCullMode();
+ }
+
+ if (state.getBlendMode() != context.blendMode) {
+ if (state.getBlendMode() == RenderState.BlendMode.Off) {
+ glDisable(GL_BLEND);
+ } else {
+ glEnable(GL_BLEND);
+ switch (state.getBlendMode()) {
+ case Off:
+ break;
+ case Additive:
+ glBlendFunc(GL_ONE, GL_ONE);
+ break;
+ case AlphaAdditive:
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ break;
+ case Color:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
+ break;
+ case Alpha:
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case PremultAlpha:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case Modulate:
+ glBlendFunc(GL_DST_COLOR, GL_ZERO);
+ break;
+ case ModulateX2:
+ glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized blend mode: "
+ + state.getBlendMode());
+ }
+ }
+
+ context.blendMode = state.getBlendMode();
+ }
+
+ if (context.stencilTest != state.isStencilTest()
+ || context.frontStencilStencilFailOperation != state.getFrontStencilStencilFailOperation()
+ || context.frontStencilDepthFailOperation != state.getFrontStencilDepthFailOperation()
+ || context.frontStencilDepthPassOperation != state.getFrontStencilDepthPassOperation()
+ || context.backStencilStencilFailOperation != state.getBackStencilStencilFailOperation()
+ || context.backStencilDepthFailOperation != state.getBackStencilDepthFailOperation()
+ || context.backStencilDepthPassOperation != state.getBackStencilDepthPassOperation()
+ || context.frontStencilFunction != state.getFrontStencilFunction()
+ || context.backStencilFunction != state.getBackStencilFunction()) {
+
+ context.frontStencilStencilFailOperation = state.getFrontStencilStencilFailOperation(); //terrible looking, I know
+ context.frontStencilDepthFailOperation = state.getFrontStencilDepthFailOperation();
+ context.frontStencilDepthPassOperation = state.getFrontStencilDepthPassOperation();
+ context.backStencilStencilFailOperation = state.getBackStencilStencilFailOperation();
+ context.backStencilDepthFailOperation = state.getBackStencilDepthFailOperation();
+ context.backStencilDepthPassOperation = state.getBackStencilDepthPassOperation();
+ context.frontStencilFunction = state.getFrontStencilFunction();
+ context.backStencilFunction = state.getBackStencilFunction();
+
+ if (state.isStencilTest()) {
+ glEnable(GL_STENCIL_TEST);
+ glStencilOpSeparate(GL_FRONT,
+ convertStencilOperation(state.getFrontStencilStencilFailOperation()),
+ convertStencilOperation(state.getFrontStencilDepthFailOperation()),
+ convertStencilOperation(state.getFrontStencilDepthPassOperation()));
+ glStencilOpSeparate(GL_BACK,
+ convertStencilOperation(state.getBackStencilStencilFailOperation()),
+ convertStencilOperation(state.getBackStencilDepthFailOperation()),
+ convertStencilOperation(state.getBackStencilDepthPassOperation()));
+ glStencilFuncSeparate(GL_FRONT,
+ convertTestFunction(state.getFrontStencilFunction()),
+ 0, Integer.MAX_VALUE);
+ glStencilFuncSeparate(GL_BACK,
+ convertTestFunction(state.getBackStencilFunction()),
+ 0, Integer.MAX_VALUE);
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ }
+ }
+ }
+
+ private int convertStencilOperation(StencilOperation stencilOp) {
+ switch (stencilOp) {
+ case Keep:
+ return GL_KEEP;
+ case Zero:
+ return GL_ZERO;
+ case Replace:
+ return GL_REPLACE;
+ case Increment:
+ return GL_INCR;
+ case IncrementWrap:
+ return GL_INCR_WRAP;
+ case Decrement:
+ return GL_DECR;
+ case DecrementWrap:
+ return GL_DECR_WRAP;
+ case Invert:
+ return GL_INVERT;
+ default:
+ throw new UnsupportedOperationException("Unrecognized stencil operation: " + stencilOp);
+ }
+ }
+
+ private int convertTestFunction(TestFunction testFunc) {
+ switch (testFunc) {
+ case Never:
+ return GL_NEVER;
+ case Less:
+ return GL_LESS;
+ case LessOrEqual:
+ return GL_LEQUAL;
+ case Greater:
+ return GL_GREATER;
+ case GreaterOrEqual:
+ return GL_GEQUAL;
+ case Equal:
+ return GL_EQUAL;
+ case NotEqual:
+ return GL_NOTEQUAL;
+ case Always:
+ return GL_ALWAYS;
+ default:
+ throw new UnsupportedOperationException("Unrecognized test function: " + testFunc);
+ }
+ }
+
+ /*********************************************************************\
+ |* Camera and World transforms *|
+ \*********************************************************************/
+ public void setViewPort(int x, int y, int w, int h) {
+ if (x != vpX || vpY != y || vpW != w || vpH != h) {
+ glViewport(x, y, w, h);
+ vpX = x;
+ vpY = y;
+ vpW = w;
+ vpH = h;
+ }
+ }
+
+ public void setClipRect(int x, int y, int width, int height) {
+ if (!context.clipRectEnabled) {
+ glEnable(GL_SCISSOR_TEST);
+ context.clipRectEnabled = true;
+ }
+ if (clipX != x || clipY != y || clipW != width || clipH != height) {
+ glScissor(x, y, width, height);
+ clipX = x;
+ clipY = y;
+ clipW = width;
+ clipH = height;
+ }
+ }
+
+ public void clearClipRect() {
+ if (context.clipRectEnabled) {
+ glDisable(GL_SCISSOR_TEST);
+ context.clipRectEnabled = false;
+
+ clipX = 0;
+ clipY = 0;
+ clipW = 0;
+ clipH = 0;
+ }
+ }
+
+ public void onFrame() {
+ objManager.deleteUnused(this);
+// statistics.clearFrame();
+ }
+
+ public void setWorldMatrix(Matrix4f worldMatrix) {
+ }
+
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
+ }
+
+ /*********************************************************************\
+ |* Shaders *|
+ \*********************************************************************/
+ protected void updateUniformLocation(Shader shader, Uniform uniform) {
+ stringBuf.setLength(0);
+ stringBuf.append(uniform.getName()).append('\0');
+ updateNameBuffer();
+ int loc = glGetUniformLocation(shader.getId(), nameBuf);
+ if (loc < 0) {
+ uniform.setLocation(-1);
+ // uniform is not declared in shader
+ logger.log(Level.INFO, "Uniform {0} is not declared in shader {1}.", new Object[]{uniform.getName(), shader.getSources()});
+ } else {
+ uniform.setLocation(loc);
+ }
+ }
+
+ protected void bindProgram(Shader shader){
+ int shaderId = shader.getId();
+ if (context.boundShaderProgram != shaderId) {
+ glUseProgram(shaderId);
+ statistics.onShaderUse(shader, true);
+ boundShader = shader;
+ context.boundShaderProgram = shaderId;
+ } else {
+ statistics.onShaderUse(shader, false);
+ }
+ }
+
+ protected void updateUniform(Shader shader, Uniform uniform) {
+ int shaderId = shader.getId();
+
+ assert uniform.getName() != null;
+ assert shader.getId() > 0;
+
+ bindProgram(shader);
+
+ int loc = uniform.getLocation();
+ if (loc == -1) {
+ return;
+ }
+
+ if (loc == -2) {
+ // get uniform location
+ updateUniformLocation(shader, uniform);
+ if (uniform.getLocation() == -1) {
+ // not declared, ignore
+ uniform.clearUpdateNeeded();
+ return;
+ }
+ loc = uniform.getLocation();
+ }
+
+ if (uniform.getVarType() == null) {
+ return; // value not set yet..
+ }
+ statistics.onUniformSet();
+
+ uniform.clearUpdateNeeded();
+ FloatBuffer fb;
+ switch (uniform.getVarType()) {
+ case Float:
+ Float f = (Float) uniform.getValue();
+ glUniform1f(loc, f.floatValue());
+ break;
+ case Vector2:
+ Vector2f v2 = (Vector2f) uniform.getValue();
+ glUniform2f(loc, v2.getX(), v2.getY());
+ break;
+ case Vector3:
+ Vector3f v3 = (Vector3f) uniform.getValue();
+ glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
+ break;
+ case Vector4:
+ Object val = uniform.getValue();
+ if (val instanceof ColorRGBA) {
+ ColorRGBA c = (ColorRGBA) val;
+ glUniform4f(loc, c.r, c.g, c.b, c.a);
+ } else if (val instanceof Vector4f) {
+ Vector4f c = (Vector4f) val;
+ glUniform4f(loc, c.x, c.y, c.z, c.w);
+ } else {
+ Quaternion c = (Quaternion) uniform.getValue();
+ glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
+ }
+ break;
+ case Boolean:
+ Boolean b = (Boolean) uniform.getValue();
+ glUniform1i(loc, b.booleanValue() ? GL_TRUE : GL_FALSE);
+ break;
+ case Matrix3:
+ fb = (FloatBuffer) uniform.getValue();
+ assert fb.remaining() == 9;
+ glUniformMatrix3(loc, false, fb);
+ break;
+ case Matrix4:
+ fb = (FloatBuffer) uniform.getValue();
+ assert fb.remaining() == 16;
+ glUniformMatrix4(loc, false, fb);
+ break;
+ case FloatArray:
+ fb = (FloatBuffer) uniform.getValue();
+ glUniform1(loc, fb);
+ break;
+ case Vector2Array:
+ fb = (FloatBuffer) uniform.getValue();
+ glUniform2(loc, fb);
+ break;
+ case Vector3Array:
+ fb = (FloatBuffer) uniform.getValue();
+ glUniform3(loc, fb);
+ break;
+ case Vector4Array:
+ fb = (FloatBuffer) uniform.getValue();
+ glUniform4(loc, fb);
+ break;
+ case Matrix4Array:
+ fb = (FloatBuffer) uniform.getValue();
+ glUniformMatrix4(loc, false, fb);
+ break;
+ case Int:
+ Integer i = (Integer) uniform.getValue();
+ glUniform1i(loc, i.intValue());
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
+ }
+ }
+
+ protected void updateShaderUniforms(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+// for (Uniform uniform : shader.getUniforms()){
+ for (int i = 0; i < uniforms.size(); i++) {
+ Uniform uniform = uniforms.getValue(i);
+ if (uniform.isUpdateNeeded()) {
+ updateUniform(shader, uniform);
+ }
+ }
+ }
+
+ protected void resetUniformLocations(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+// for (Uniform uniform : shader.getUniforms()){
+ for (int i = 0; i < uniforms.size(); i++) {
+ Uniform uniform = uniforms.getValue(i);
+ uniform.reset(); // e.g check location again
+ }
+ }
+
+ /*
+ * (Non-javadoc)
+ * Only used for fixed-function. Ignored.
+ */
+ public void setLighting(LightList list) {
+ }
+
+ public int convertShaderType(ShaderType type) {
+ switch (type) {
+ case Fragment:
+ return GL_FRAGMENT_SHADER;
+ case Vertex:
+ return GL_VERTEX_SHADER;
+// case Geometry:
+// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB;
+ default:
+ throw new UnsupportedOperationException("Unrecognized shader type.");
+ }
+ }
+
+ public void updateShaderSourceData(ShaderSource source, String language) {
+ int id = source.getId();
+ if (id == -1) {
+ // create id
+ id = glCreateShader(convertShaderType(source.getType()));
+ if (id <= 0) {
+ throw new RendererException("Invalid ID received when trying to create shader.");
+ }
+
+ source.setId(id);
+ }else{
+ throw new RendererException("Cannot recompile shader source");
+ }
+
+ // upload shader source
+ // merge the defines and source code
+
+ stringBuf.setLength(0);
+ if (language.startsWith("GLSL")) {
+ int version = Integer.parseInt(language.substring(4));
+ if (version > 100) {
+ stringBuf.append("#version ");
+ stringBuf.append(language.substring(4));
+ if (version >= 150) {
+ stringBuf.append(" core");
+ }
+ stringBuf.append("\n");
+ }
+ }
+ updateNameBuffer();
+
+ byte[] definesCodeData = source.getDefines().getBytes();
+ byte[] sourceCodeData = source.getSource().getBytes();
+ ByteBuffer codeBuf = BufferUtils.createByteBuffer(nameBuf.limit()
+ + definesCodeData.length
+ + sourceCodeData.length);
+ codeBuf.put(nameBuf);
+ codeBuf.put(definesCodeData);
+ codeBuf.put(sourceCodeData);
+ codeBuf.flip();
+
+ glShaderSource(id, codeBuf);
+ glCompileShader(id);
+
+ glGetShader(id, GL_COMPILE_STATUS, intBuf1);
+
+ boolean compiledOK = intBuf1.get(0) == GL_TRUE;
+ String infoLog = null;
+
+ if (VALIDATE_SHADER || !compiledOK) {
+ // even if compile succeeded, check
+ // log for warnings
+ glGetShader(id, GL_INFO_LOG_LENGTH, intBuf1);
+ int length = intBuf1.get(0);
+ if (length > 3) {
+ // get infos
+ ByteBuffer logBuf = BufferUtils.createByteBuffer(length);
+ glGetShaderInfoLog(id, null, logBuf);
+ byte[] logBytes = new byte[length];
+ logBuf.get(logBytes, 0, length);
+ // convert to string, etc
+ infoLog = new String(logBytes);
+ }
+ }
+
+ if (compiledOK) {
+ if (infoLog != null) {
+ logger.log(Level.INFO, "{0} compile success\n{1}",
+ new Object[]{source.getName(), infoLog});
+ } else {
+ logger.log(Level.FINE, "{0} compile success", source.getName());
+ }
+ } else {
+ logger.log(Level.WARNING, "Bad compile of:\n{0}{1}",
+ new Object[]{source.getDefines(), source.getSource()});
+ if (infoLog != null) {
+ throw new RendererException("compile error in:" + source + " error:" + infoLog);
+ } else {
+ throw new RendererException("compile error in:" + source + " error: <not provided>");
+ }
+ }
+
+ source.clearUpdateNeeded();
+ // only usable if compiled
+ source.setUsable(compiledOK);
+ if (!compiledOK) {
+ // make sure to dispose id cause all program's
+ // shaders will be cleared later.
+ glDeleteShader(id);
+ } else {
+ // register for cleanup since the ID is usable
+ // NOTE: From now on cleanup is handled
+ // by the parent shader object so no need
+ // to register.
+ //objManager.registerForCleanup(source);
+ }
+ }
+
+ public void updateShaderData(Shader shader) {
+ int id = shader.getId();
+ boolean needRegister = false;
+ if (id == -1) {
+ // create program
+ id = glCreateProgram();
+ if (id == 0) {
+ throw new RendererException("Invalid ID (" + id + ") received when trying to create shader program.");
+ }
+
+ shader.setId(id);
+ needRegister = true;
+ }
+
+ for (ShaderSource source : shader.getSources()) {
+ if (source.isUpdateNeeded()) {
+ updateShaderSourceData(source, shader.getLanguage());
+ // shader has been compiled here
+ }
+
+ if (!source.isUsable()) {
+ // it's useless.. just forget about everything..
+ shader.setUsable(false);
+ shader.clearUpdateNeeded();
+ return;
+ }
+ glAttachShader(id, source.getId());
+ }
+
+ if (caps.contains(Caps.OpenGL30)) {
+ // Check if GLSL version is 1.5 for shader
+ GL30.glBindFragDataLocation(id, 0, "outFragColor");
+ }
+
+ // link shaders to program
+ glLinkProgram(id);
+ glGetProgram(id, GL_LINK_STATUS, intBuf1);
+ boolean linkOK = intBuf1.get(0) == GL_TRUE;
+ String infoLog = null;
+
+ if (VALIDATE_SHADER || !linkOK) {
+ glGetProgram(id, GL_INFO_LOG_LENGTH, intBuf1);
+ int length = intBuf1.get(0);
+ if (length > 3) {
+ // get infos
+ ByteBuffer logBuf = BufferUtils.createByteBuffer(length);
+ glGetProgramInfoLog(id, null, logBuf);
+
+ // convert to string, etc
+ byte[] logBytes = new byte[length];
+ logBuf.get(logBytes, 0, length);
+ infoLog = new String(logBytes);
+ }
+ }
+
+ if (linkOK) {
+ if (infoLog != null) {
+ logger.log(Level.INFO, "shader link success. \n{0}", infoLog);
+ } else {
+ logger.fine("shader link success");
+ }
+ } else {
+ if (infoLog != null) {
+ throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
+ } else {
+ throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
+ }
+ }
+
+ shader.clearUpdateNeeded();
+ if (!linkOK) {
+ // failure.. forget about everything
+ shader.resetSources();
+ shader.setUsable(false);
+ deleteShader(shader);
+ } else {
+ shader.setUsable(true);
+ if (needRegister) {
+ objManager.registerForCleanup(shader);
+ statistics.onNewShader();
+ } else {
+ // OpenGL spec: uniform locations may change after re-link
+ resetUniformLocations(shader);
+ }
+ }
+ }
+
+ public void setShader(Shader shader) {
+ if (shader == null) {
+ throw new IllegalArgumentException("shader cannot be null");
+// if (context.boundShaderProgram > 0) {
+// glUseProgram(0);
+// statistics.onShaderUse(null, true);
+// context.boundShaderProgram = 0;
+// boundShader = null;
+// }
+ } else {
+ if (shader.isUpdateNeeded()) {
+ updateShaderData(shader);
+ }
+
+ // NOTE: might want to check if any of the
+ // sources need an update?
+
+ if (!shader.isUsable()) {
+ return;
+ }
+
+ assert shader.getId() > 0;
+
+ updateShaderUniforms(shader);
+ bindProgram(shader);
+ }
+ }
+
+ public void deleteShaderSource(ShaderSource source) {
+ if (source.getId() < 0) {
+ logger.warning("Shader source is not uploaded to GPU, cannot delete.");
+ return;
+ }
+ source.setUsable(false);
+ source.clearUpdateNeeded();
+ glDeleteShader(source.getId());
+ source.resetObject();
+ }
+
+ public void deleteShader(Shader shader) {
+ if (shader.getId() == -1) {
+ logger.warning("Shader is not uploaded to GPU, cannot delete.");
+ return;
+ }
+
+ for (ShaderSource source : shader.getSources()) {
+ if (source.getId() != -1) {
+ glDetachShader(shader.getId(), source.getId());
+ deleteShaderSource(source);
+ }
+ }
+
+ // kill all references so sources can be collected
+ // if needed.
+ shader.resetSources();
+ glDeleteProgram(shader.getId());
+
+ statistics.onDeleteShader();
+ }
+
+ /*********************************************************************\
+ |* Framebuffers *|
+ \*********************************************************************/
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
+ copyFrameBuffer(src, dst, true);
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
+ if (GLContext.getCapabilities().GL_EXT_framebuffer_blit) {
+ int srcW = 0;
+ int srcH = 0;
+ int dstW = 0;
+ int dstH = 0;
+ int prevFBO = context.boundFBO;
+
+ if (src != null && src.isUpdateNeeded()) {
+ updateFrameBuffer(src);
+ }
+
+ if (dst != null && dst.isUpdateNeeded()) {
+ updateFrameBuffer(dst);
+ }
+
+ if (src == null) {
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+// srcW = viewWidth;
+// srcH = viewHeight;
+ } else {
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src.getId());
+ srcW = src.getWidth();
+ srcH = src.getHeight();
+ }
+ if (dst == null) {
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+// dstW = viewWidth;
+// dstH = viewHeight;
+ } else {
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
+ dstW = dst.getWidth();
+ dstH = dst.getHeight();
+ }
+ int mask = GL_COLOR_BUFFER_BIT;
+ if (copyDepth) {
+ mask |= GL_DEPTH_BUFFER_BIT;
+ }
+ glBlitFramebufferEXT(0, 0, srcW, srcH,
+ 0, 0, dstW, dstH, mask,
+ GL_NEAREST);
+
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prevFBO);
+ try {
+ checkFrameBufferError();
+ } catch (IllegalStateException ex) {
+ logger.log(Level.SEVERE, "Source FBO:\n{0}", src);
+ logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst);
+ throw ex;
+ }
+ } else {
+ throw new RendererException("EXT_framebuffer_blit required.");
+ // TODO: support non-blit copies?
+ }
+ }
+
+ private String getTargetBufferName(int buffer){
+ switch (buffer){
+ case GL_NONE: return "NONE";
+ case GL_FRONT: return "GL_FRONT";
+ case GL_BACK: return "GL_BACK";
+ default:
+ if ( buffer >= GL_COLOR_ATTACHMENT0_EXT
+ && buffer <= GL_COLOR_ATTACHMENT15_EXT){
+ return "GL_COLOR_ATTACHMENT" +
+ (buffer - GL_COLOR_ATTACHMENT0_EXT);
+ }else{
+ return "UNKNOWN? " + buffer;
+ }
+ }
+ }
+
+ private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name){
+ System.out.println("== Renderbuffer " + name + " ==");
+ System.out.println("RB ID: " + rb.getId());
+ System.out.println("Is proper? " + glIsRenderbufferEXT(rb.getId()));
+
+ int attachment = convertAttachmentSlot(rb.getSlot());
+
+ int type = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT,
+ attachment,
+ GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT);
+
+ int rbName = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT,
+ attachment,
+ GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT);
+
+ switch (type){
+ case GL_NONE:
+ System.out.println("Type: None");
+ return; // note: return from method as other queries will be invalid
+ case GL_TEXTURE:
+ System.out.println("Type: Texture");
+ break;
+ case GL_RENDERBUFFER_EXT:
+ System.out.println("Type: Buffer");
+ System.out.println("RB ID: " + rbName);
+ break;
+ }
+
+
+
+ }
+
+ private void printRealFrameBufferInfo(FrameBuffer fb) {
+ boolean doubleBuffer = glGetBoolean(GL_DOUBLEBUFFER);
+ String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER));
+ String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER));
+
+ int fbId = fb.getId();
+ int curDrawBinding = glGetInteger(ARBFramebufferObject.GL_DRAW_FRAMEBUFFER_BINDING);
+ int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING);
+
+ System.out.println("=== OpenGL FBO State ===");
+ System.out.println("Context doublebuffered? " + doubleBuffer);
+ System.out.println("FBO ID: " + fbId);
+ System.out.println("Is proper? " + glIsFramebufferEXT(fbId));
+ System.out.println("Is bound to draw? " + (fbId == curDrawBinding));
+ System.out.println("Is bound to read? " + (fbId == curReadBinding));
+ System.out.println("Draw buffer: " + drawBuf);
+ System.out.println("Read buffer: " + readBuf);
+
+ if (context.boundFBO != fbId){
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbId);
+ context.boundFBO = fbId;
+ }
+
+ if (fb.getDepthBuffer() != null){
+ printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth");
+ }
+ for (int i = 0; i < fb.getNumColorBuffers(); i++){
+ printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i);
+ }
+ }
+
+ private void checkFrameBufferError() {
+ int status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ switch (status) {
+ case GL_FRAMEBUFFER_COMPLETE_EXT:
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ //Choose different formats
+ throw new IllegalStateException("Framebuffer object format is "
+ + "unsupported by the video hardware.");
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ throw new IllegalStateException("Framebuffer has erronous attachment.");
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached.");
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+ throw new IllegalStateException("Framebuffer attachments must have same dimensions.");
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
+ throw new IllegalStateException("Framebuffer attachments must have same formats.");
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ throw new IllegalStateException("Incomplete draw buffer.");
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ throw new IllegalStateException("Incomplete read buffer.");
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
+ throw new IllegalStateException("Incomplete multisample buffer.");
+ default:
+ //Programming error; will fail on all hardware
+ throw new IllegalStateException("Some video driver error "
+ + "or programming error occured. "
+ + "Framebuffer object status is invalid. ");
+ }
+ }
+
+ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
+ int id = rb.getId();
+ if (id == -1) {
+ glGenRenderbuffersEXT(intBuf1);
+ id = intBuf1.get(0);
+ rb.setId(id);
+ }
+
+ if (context.boundRB != id) {
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
+ context.boundRB = id;
+ }
+
+ if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) {
+ throw new RendererException("Resolution " + fb.getWidth()
+ + ":" + fb.getHeight() + " is not supported.");
+ }
+
+ TextureUtil.checkFormatSupported(rb.getFormat());
+
+ if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) {
+ int samples = fb.getSamples();
+ if (maxFBOSamples < samples) {
+ samples = maxFBOSamples;
+ }
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
+ samples,
+ TextureUtil.convertTextureFormat(rb.getFormat()),
+ fb.getWidth(),
+ fb.getHeight());
+ } else {
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
+ TextureUtil.convertTextureFormat(rb.getFormat()),
+ fb.getWidth(),
+ fb.getHeight());
+ }
+ }
+
+ private int convertAttachmentSlot(int attachmentSlot) {
+ // can also add support for stencil here
+ if (attachmentSlot == -100) {
+ return GL_DEPTH_ATTACHMENT_EXT;
+ } else if (attachmentSlot < 0 || attachmentSlot >= 16) {
+ throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
+ }
+
+ return GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
+ }
+
+ public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
+ Texture tex = rb.getTexture();
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded()) {
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), 0);
+
+ // NOTE: For depth textures, sets nearest/no-mips mode
+ // Required to fix "framebuffer unsupported"
+ // for old NVIDIA drivers!
+ setupTextureParams(tex);
+ }
+
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ convertAttachmentSlot(rb.getSlot()),
+ convertTextureType(tex.getType(), image.getMultiSamples()),
+ image.getId(),
+ 0);
+ }
+
+ public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
+ boolean needAttach;
+ if (rb.getTexture() == null) {
+ // if it hasn't been created yet, then attach is required.
+ needAttach = rb.getId() == -1;
+ updateRenderBuffer(fb, rb);
+ } else {
+ needAttach = false;
+ updateRenderTexture(fb, rb);
+ }
+ if (needAttach) {
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
+ convertAttachmentSlot(rb.getSlot()),
+ GL_RENDERBUFFER_EXT,
+ rb.getId());
+ }
+ }
+
+ public void updateFrameBuffer(FrameBuffer fb) {
+ int id = fb.getId();
+ if (id == -1) {
+ // create FBO
+ glGenFramebuffersEXT(intBuf1);
+ id = intBuf1.get(0);
+ fb.setId(id);
+ objManager.registerForCleanup(fb);
+
+ statistics.onNewFrameBuffer();
+ }
+
+ if (context.boundFBO != id) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
+ // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
+ context.boundDrawBuf = 0;
+ context.boundFBO = id;
+ }
+
+ FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
+ if (depthBuf != null) {
+ updateFrameBufferAttachment(fb, depthBuf);
+ }
+
+ for (int i = 0; i < fb.getNumColorBuffers(); i++) {
+ FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
+ updateFrameBufferAttachment(fb, colorBuf);
+ }
+
+ fb.clearUpdateNeeded();
+ }
+
+ public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) {
+ if (fb.getSamples() <= 1) {
+ throw new IllegalArgumentException("Framebuffer must be multisampled");
+ }
+
+ setFrameBuffer(fb);
+
+ Vector2f[] samplePositions = new Vector2f[fb.getSamples()];
+ FloatBuffer samplePos = BufferUtils.createFloatBuffer(2);
+ for (int i = 0; i < samplePositions.length; i++) {
+ glGetMultisample(GL_SAMPLE_POSITION, i, samplePos);
+ samplePos.clear();
+ samplePositions[i] = new Vector2f(samplePos.get(0) - 0.5f,
+ samplePos.get(1) - 0.5f);
+ }
+ return samplePositions;
+ }
+
+ public void setMainFrameBufferOverride(FrameBuffer fb){
+ mainFbOverride = fb;
+ }
+
+ public void setFrameBuffer(FrameBuffer fb) {
+ if (fb == null && mainFbOverride != null){
+ fb = mainFbOverride;
+ }
+
+ if (lastFb == fb) {
+ if (fb == null || !fb.isUpdateNeeded()){
+ return;
+ }
+ }
+
+ // generate mipmaps for last FB if needed
+ if (lastFb != null) {
+ for (int i = 0; i < lastFb.getNumColorBuffers(); i++) {
+ RenderBuffer rb = lastFb.getColorBuffer(i);
+ Texture tex = rb.getTexture();
+ if (tex != null
+ && tex.getMinFilter().usesMipMapLevels()) {
+ setTexture(0, rb.getTexture());
+
+ int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples());
+ glEnable(textureType);
+ glGenerateMipmapEXT(textureType);
+ glDisable(textureType);
+ }
+ }
+ }
+
+ if (fb == null) {
+ // unbind any fbos
+ if (context.boundFBO != 0) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ statistics.onFrameBufferUse(null, true);
+
+ context.boundFBO = 0;
+ }
+ // select back buffer
+ if (context.boundDrawBuf != -1) {
+ glDrawBuffer(initialDrawBuf);
+ context.boundDrawBuf = -1;
+ }
+ if (context.boundReadBuf != -1) {
+ glReadBuffer(initialReadBuf);
+ context.boundReadBuf = -1;
+ }
+
+ lastFb = null;
+ } else {
+ if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null){
+ throw new IllegalArgumentException("The framebuffer: " + fb +
+ "\nDoesn't have any color/depth buffers");
+ }
+
+ if (fb.isUpdateNeeded()) {
+ updateFrameBuffer(fb);
+ }
+
+ if (context.boundFBO != fb.getId()) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb.getId());
+ statistics.onFrameBufferUse(fb, true);
+
+ // update viewport to reflect framebuffer's resolution
+ setViewPort(0, 0, fb.getWidth(), fb.getHeight());
+
+ context.boundFBO = fb.getId();
+ } else {
+ statistics.onFrameBufferUse(fb, false);
+ }
+ if (fb.getNumColorBuffers() == 0) {
+ // make sure to select NONE as draw buf
+ // no color buffer attached. select NONE
+ if (context.boundDrawBuf != -2) {
+ glDrawBuffer(GL_NONE);
+ context.boundDrawBuf = -2;
+ }
+ if (context.boundReadBuf != -2) {
+ glReadBuffer(GL_NONE);
+ context.boundReadBuf = -2;
+ }
+ } else {
+ if (fb.isMultiTarget()) {
+ if (fb.getNumColorBuffers() > maxMRTFBOAttachs) {
+ throw new RendererException("Framebuffer has more"
+ + " targets than are supported"
+ + " on the system!");
+ }
+
+ if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
+ intBuf16.clear();
+ for (int i = 0; i < fb.getNumColorBuffers(); i++) {
+ intBuf16.put(GL_COLOR_ATTACHMENT0_EXT + i);
+ }
+
+ intBuf16.flip();
+ glDrawBuffers(intBuf16);
+ context.boundDrawBuf = 100 + fb.getNumColorBuffers();
+ }
+ } else {
+ RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
+ // select this draw buffer
+ if (context.boundDrawBuf != rb.getSlot()) {
+ glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ context.boundDrawBuf = rb.getSlot();
+ }
+ }
+ }
+
+ assert fb.getId() >= 0;
+ assert context.boundFBO == fb.getId();
+
+ lastFb = fb;
+
+ try {
+ checkFrameBufferError();
+ } catch (IllegalStateException ex) {
+ logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb);
+ printRealFrameBufferInfo(fb);
+ throw ex;
+ }
+ }
+ }
+
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+ if (fb != null) {
+ RenderBuffer rb = fb.getColorBuffer();
+ if (rb == null) {
+ throw new IllegalArgumentException("Specified framebuffer"
+ + " does not have a colorbuffer");
+ }
+
+ setFrameBuffer(fb);
+ if (context.boundReadBuf != rb.getSlot()) {
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ context.boundReadBuf = rb.getSlot();
+ }
+ } else {
+ setFrameBuffer(null);
+ }
+
+ glReadPixels(vpX, vpY, vpW, vpH, /*GL_RGBA*/ GL_BGRA, GL_UNSIGNED_BYTE, byteBuf);
+ }
+
+ private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
+ intBuf1.put(0, rb.getId());
+ glDeleteRenderbuffersEXT(intBuf1);
+ }
+
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ if (fb.getId() != -1) {
+ if (context.boundFBO == fb.getId()) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ context.boundFBO = 0;
+ }
+
+ if (fb.getDepthBuffer() != null) {
+ deleteRenderBuffer(fb, fb.getDepthBuffer());
+ }
+ if (fb.getColorBuffer() != null) {
+ deleteRenderBuffer(fb, fb.getColorBuffer());
+ }
+
+ intBuf1.put(0, fb.getId());
+ glDeleteFramebuffersEXT(intBuf1);
+ fb.resetObject();
+
+ statistics.onDeleteFrameBuffer();
+ }
+ }
+
+ /*********************************************************************\
+ |* Textures *|
+ \*********************************************************************/
+ private int convertTextureType(Texture.Type type, int samples) {
+ switch (type) {
+ case TwoDimensional:
+ if (samples > 1) {
+ return ARBTextureMultisample.GL_TEXTURE_2D_MULTISAMPLE;
+ } else {
+ return GL_TEXTURE_2D;
+ }
+ case TwoDimensionalArray:
+ if (samples > 1) {
+ return ARBTextureMultisample.GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
+ } else {
+ return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT;
+ }
+ case ThreeDimensional:
+ return GL_TEXTURE_3D;
+ case CubeMap:
+ return GL_TEXTURE_CUBE_MAP;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + type);
+ }
+ }
+
+ private int convertMagFilter(Texture.MagFilter filter) {
+ switch (filter) {
+ case Bilinear:
+ return GL_LINEAR;
+ case Nearest:
+ return GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown mag filter: " + filter);
+ }
+ }
+
+ private int convertMinFilter(Texture.MinFilter filter) {
+ switch (filter) {
+ case Trilinear:
+ return GL_LINEAR_MIPMAP_LINEAR;
+ case BilinearNearestMipMap:
+ return GL_LINEAR_MIPMAP_NEAREST;
+ case NearestLinearMipMap:
+ return GL_NEAREST_MIPMAP_LINEAR;
+ case NearestNearestMipMap:
+ return GL_NEAREST_MIPMAP_NEAREST;
+ case BilinearNoMipMaps:
+ return GL_LINEAR;
+ case NearestNoMipMaps:
+ return GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown min filter: " + filter);
+ }
+ }
+
+ private int convertWrapMode(Texture.WrapMode mode) {
+ switch (mode) {
+ case BorderClamp:
+ return GL_CLAMP_TO_BORDER;
+ case Clamp:
+ return GL_CLAMP;
+ case EdgeClamp:
+ return GL_CLAMP_TO_EDGE;
+ case Repeat:
+ return GL_REPEAT;
+ case MirroredRepeat:
+ return GL_MIRRORED_REPEAT;
+ default:
+ throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
+ }
+ }
+
+ @SuppressWarnings("fallthrough")
+ private void setupTextureParams(Texture tex) {
+ Image image = tex.getImage();
+ int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1);
+
+ // filter things
+ int minFilter = convertMinFilter(tex.getMinFilter());
+ int magFilter = convertMagFilter(tex.getMagFilter());
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
+
+ if (tex.getAnisotropicFilter() > 1) {
+ if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic) {
+ glTexParameterf(target,
+ EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT,
+ tex.getAnisotropicFilter());
+ }
+ }
+
+ if (context.pointSprite) {
+ return; // Attempt to fix glTexParameter crash for some ATI GPUs
+ }
+ // repeat modes
+ switch (tex.getType()) {
+ case ThreeDimensional:
+ case CubeMap: // cubemaps use 3D coords
+ glTexParameteri(target, GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
+ case TwoDimensional:
+ case TwoDimensionalArray:
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
+ // fall down here is intentional..
+// case OneDimensional:
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
+ }
+
+ // R to Texture compare mode
+ if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) {
+ glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
+ glTexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
+ if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) {
+ glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_GEQUAL);
+ } else {
+ glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+ }
+ }
+ }
+
+ public void updateTexImageData(Image img, Texture.Type type, boolean mips, int unit) {
+ int texId = img.getId();
+ if (texId == -1) {
+ // create texture
+ glGenTextures(intBuf1);
+ texId = intBuf1.get(0);
+ img.setId(texId);
+ objManager.registerForCleanup(img);
+
+ statistics.onNewTexture();
+ }
+
+ // bind texture
+ int target = convertTextureType(type, img.getMultiSamples());
+ if (context.boundTextureUnit != unit) {
+ glActiveTexture(GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+ if (context.boundTextures[unit] != img) {
+ glBindTexture(target, texId);
+ context.boundTextures[unit] = img;
+
+ statistics.onTextureUse(img, true);
+ }
+
+ if (!img.hasMipmaps() && mips) {
+ // No pregenerated mips available,
+ // generate from base level if required
+ if (!GLContext.getCapabilities().OpenGL30) {
+ glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
+ }
+ } else {
+// glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 );
+ if (img.getMipMapSizes() != null) {
+ glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length);
+ }
+ }
+
+ int imageSamples = img.getMultiSamples();
+ if (imageSamples > 1) {
+ if (img.getFormat().isDepthFormat()) {
+ img.setMultiSamples(Math.min(maxDepthTexSamples, imageSamples));
+ } else {
+ img.setMultiSamples(Math.min(maxColorTexSamples, imageSamples));
+ }
+ }
+
+ // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT.
+ if (!GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) {
+ if (img.getWidth() != 0 && img.getHeight() != 0) {
+ if (!FastMath.isPowerOfTwo(img.getWidth())
+ || !FastMath.isPowerOfTwo(img.getHeight())) {
+ if (img.getData(0) == null) {
+ throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware");
+ } else {
+ MipMapGenerator.resizeToPowerOf2(img);
+ }
+ }
+ }
+ }
+
+ // Check if graphics card doesn't support multisample textures
+ if (!GLContext.getCapabilities().GL_ARB_texture_multisample) {
+ if (img.getMultiSamples() > 1) {
+ throw new RendererException("Multisample textures not supported by graphics hardware");
+ }
+ }
+
+ if (target == GL_TEXTURE_CUBE_MAP) {
+ List<ByteBuffer> data = img.getData();
+ if (data.size() != 6) {
+ logger.log(Level.WARNING, "Invalid texture: {0}\n"
+ + "Cubemap textures must contain 6 data units.", img);
+ return;
+ }
+ for (int i = 0; i < 6; i++) {
+ TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc);
+ }
+ } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) {
+ List<ByteBuffer> data = img.getData();
+ // -1 index specifies prepare data for 2D Array
+ TextureUtil.uploadTexture(img, target, -1, 0, tdc);
+ for (int i = 0; i < data.size(); i++) {
+ // upload each slice of 2D array in turn
+ // this time with the appropriate index
+ TextureUtil.uploadTexture(img, target, i, 0, tdc);
+ }
+ } else {
+ TextureUtil.uploadTexture(img, target, 0, 0, tdc);
+ }
+
+ if (img.getMultiSamples() != imageSamples) {
+ img.setMultiSamples(imageSamples);
+ }
+
+ if (GLContext.getCapabilities().OpenGL30) {
+ if (!img.hasMipmaps() && mips && img.getData() != null) {
+ // XXX: Required for ATI
+ glEnable(target);
+ glGenerateMipmapEXT(target);
+ glDisable(target);
+ }
+ }
+
+ img.clearUpdateNeeded();
+ }
+
+ public void setTexture(int unit, Texture tex) {
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded()) {
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), unit);
+ }
+
+ int texId = image.getId();
+ assert texId != -1;
+
+ Image[] textures = context.boundTextures;
+
+ int type = convertTextureType(tex.getType(), image.getMultiSamples());
+// if (!context.textureIndexList.moveToNew(unit)) {
+// if (context.boundTextureUnit != unit){
+// glActiveTexture(GL_TEXTURE0 + unit);
+// context.boundTextureUnit = unit;
+// }
+// glEnable(type);
+// }
+
+ if (context.boundTextureUnit != unit) {
+ glActiveTexture(GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+ if (textures[unit] != image) {
+ glBindTexture(type, texId);
+ textures[unit] = image;
+
+ statistics.onTextureUse(image, true);
+ } else {
+ statistics.onTextureUse(image, false);
+ }
+
+ setupTextureParams(tex);
+ }
+
+ public void clearTextureUnits() {
+// IDList textureList = context.textureIndexList;
+// Image[] textures = context.boundTextures;
+// for (int i = 0; i < textureList.oldLen; i++) {
+// int idx = textureList.oldList[i];
+// if (context.boundTextureUnit != idx){
+// glActiveTexture(GL_TEXTURE0 + idx);
+// context.boundTextureUnit = idx;
+// }
+// glDisable(convertTextureType(textures[idx].getType()));
+// textures[idx] = null;
+// }
+// context.textureIndexList.copyNewToOld();
+ }
+
+ public void deleteImage(Image image) {
+ int texId = image.getId();
+ if (texId != -1) {
+ intBuf1.put(0, texId);
+ intBuf1.position(0).limit(1);
+ glDeleteTextures(intBuf1);
+ image.resetObject();
+
+ statistics.onDeleteTexture();
+ }
+ }
+
+ /*********************************************************************\
+ |* Vertex Buffers and Attributes *|
+ \*********************************************************************/
+ private int convertUsage(Usage usage) {
+ switch (usage) {
+ case Static:
+ return GL_STATIC_DRAW;
+ case Dynamic:
+ return GL_DYNAMIC_DRAW;
+ case Stream:
+ return GL_STREAM_DRAW;
+ default:
+ throw new UnsupportedOperationException("Unknown usage type.");
+ }
+ }
+
+ private int convertFormat(Format format) {
+ switch (format) {
+ case Byte:
+ return GL_BYTE;
+ case UnsignedByte:
+ return GL_UNSIGNED_BYTE;
+ case Short:
+ return GL_SHORT;
+ case UnsignedShort:
+ return GL_UNSIGNED_SHORT;
+ case Int:
+ return GL_INT;
+ case UnsignedInt:
+ return GL_UNSIGNED_INT;
+ case Half:
+ return NVHalfFloat.GL_HALF_FLOAT_NV;
+// return ARBHalfFloatVertex.GL_HALF_FLOAT;
+ case Float:
+ return GL_FLOAT;
+ case Double:
+ return GL_DOUBLE;
+ default:
+ throw new UnsupportedOperationException("Unknown buffer format.");
+
+ }
+ }
+
+ public void updateBufferData(VertexBuffer vb) {
+ int bufId = vb.getId();
+ boolean created = false;
+ if (bufId == -1) {
+ // create buffer
+ glGenBuffers(intBuf1);
+ bufId = intBuf1.get(0);
+ vb.setId(bufId);
+ objManager.registerForCleanup(vb);
+
+ //statistics.onNewVertexBuffer();
+
+ created = true;
+ }
+
+ // bind buffer
+ int target;
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ target = GL_ELEMENT_ARRAY_BUFFER;
+ if (context.boundElementArrayVBO != bufId) {
+ glBindBuffer(target, bufId);
+ context.boundElementArrayVBO = bufId;
+ //statistics.onVertexBufferUse(vb, true);
+ }else{
+ //statistics.onVertexBufferUse(vb, false);
+ }
+ } else {
+ target = GL_ARRAY_BUFFER;
+ if (context.boundArrayVBO != bufId) {
+ glBindBuffer(target, bufId);
+ context.boundArrayVBO = bufId;
+ //statistics.onVertexBufferUse(vb, true);
+ }else{
+ //statistics.onVertexBufferUse(vb, false);
+ }
+ }
+
+ int usage = convertUsage(vb.getUsage());
+ vb.getData().rewind();
+
+ if (created || vb.hasDataSizeChanged()) {
+ // upload data based on format
+ switch (vb.getFormat()) {
+ case Byte:
+ case UnsignedByte:
+ glBufferData(target, (ByteBuffer) vb.getData(), usage);
+ break;
+ // case Half:
+ case Short:
+ case UnsignedShort:
+ glBufferData(target, (ShortBuffer) vb.getData(), usage);
+ break;
+ case Int:
+ case UnsignedInt:
+ glBufferData(target, (IntBuffer) vb.getData(), usage);
+ break;
+ case Float:
+ glBufferData(target, (FloatBuffer) vb.getData(), usage);
+ break;
+ case Double:
+ glBufferData(target, (DoubleBuffer) vb.getData(), usage);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown buffer format.");
+ }
+ } else {
+ switch (vb.getFormat()) {
+ case Byte:
+ case UnsignedByte:
+ glBufferSubData(target, 0, (ByteBuffer) vb.getData());
+ break;
+ case Short:
+ case UnsignedShort:
+ glBufferSubData(target, 0, (ShortBuffer) vb.getData());
+ break;
+ case Int:
+ case UnsignedInt:
+ glBufferSubData(target, 0, (IntBuffer) vb.getData());
+ break;
+ case Float:
+ glBufferSubData(target, 0, (FloatBuffer) vb.getData());
+ break;
+ case Double:
+ glBufferSubData(target, 0, (DoubleBuffer) vb.getData());
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown buffer format.");
+ }
+ }
+// }else{
+// if (created || vb.hasDataSizeChanged()){
+// glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage);
+// }
+//
+// ByteBuffer buf = glMapBuffer(target,
+// GL_WRITE_ONLY,
+// vb.getMappedData());
+//
+// if (buf != vb.getMappedData()){
+// buf = buf.order(ByteOrder.nativeOrder());
+// vb.setMappedData(buf);
+// }
+//
+// buf.clear();
+//
+// switch (vb.getFormat()){
+// case Byte:
+// case UnsignedByte:
+// buf.put( (ByteBuffer) vb.getData() );
+// break;
+// case Short:
+// case UnsignedShort:
+// buf.asShortBuffer().put( (ShortBuffer) vb.getData() );
+// break;
+// case Int:
+// case UnsignedInt:
+// buf.asIntBuffer().put( (IntBuffer) vb.getData() );
+// break;
+// case Float:
+// buf.asFloatBuffer().put( (FloatBuffer) vb.getData() );
+// break;
+// case Double:
+// break;
+// default:
+// throw new RuntimeException("Unknown buffer format.");
+// }
+//
+// glUnmapBuffer(target);
+// }
+
+ vb.clearUpdateNeeded();
+ }
+
+ public void deleteBuffer(VertexBuffer vb) {
+ int bufId = vb.getId();
+ if (bufId != -1) {
+ // delete buffer
+ intBuf1.put(0, bufId);
+ intBuf1.position(0).limit(1);
+ glDeleteBuffers(intBuf1);
+ vb.resetObject();
+
+ //statistics.onDeleteVertexBuffer();
+ }
+ }
+
+ public void clearVertexAttribs() {
+ IDList attribList = context.attribIndexList;
+ for (int i = 0; i < attribList.oldLen; i++) {
+ int idx = attribList.oldList[i];
+ glDisableVertexAttribArray(idx);
+ context.boundAttribs[idx] = null;
+ }
+ context.attribIndexList.copyNewToOld();
+ }
+
+ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+ }
+
+ int programId = context.boundShaderProgram;
+ if (programId > 0) {
+ Attribute attrib = boundShader.getAttribute(vb.getBufferType());
+ int loc = attrib.getLocation();
+ if (loc == -1) {
+ return; // not defined
+ }
+ if (loc == -2) {
+ stringBuf.setLength(0);
+ stringBuf.append("in").append(vb.getBufferType().name()).append('\0');
+ updateNameBuffer();
+ loc = glGetAttribLocation(programId, nameBuf);
+
+ // not really the name of it in the shader (inPosition\0) but
+ // the internal name of the enum (Position).
+ if (loc < 0) {
+ attrib.setLocation(-1);
+ return; // not available in shader.
+ } else {
+ attrib.setLocation(loc);
+ }
+ }
+
+ if (vb.isUpdateNeeded() && idb == null) {
+ updateBufferData(vb);
+ }
+
+ VertexBuffer[] attribs = context.boundAttribs;
+ if (!context.attribIndexList.moveToNew(loc)) {
+ glEnableVertexAttribArray(loc);
+ //System.out.println("Enabled ATTRIB IDX: "+loc);
+ }
+ if (attribs[loc] != vb) {
+ // NOTE: Use id from interleaved buffer if specified
+ int bufId = idb != null ? idb.getId() : vb.getId();
+ assert bufId != -1;
+ if (context.boundArrayVBO != bufId) {
+ glBindBuffer(GL_ARRAY_BUFFER, bufId);
+ context.boundArrayVBO = bufId;
+ //statistics.onVertexBufferUse(vb, true);
+ }else{
+ //statistics.onVertexBufferUse(vb, false);
+ }
+
+ glVertexAttribPointer(loc,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ vb.getOffset());
+
+ attribs[loc] = vb;
+ }
+ } else {
+ throw new IllegalStateException("Cannot render mesh without shader bound");
+ }
+ }
+
+ public void setVertexAttrib(VertexBuffer vb) {
+ setVertexAttrib(vb, null);
+ }
+
+ public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
+ if (count > 1) {
+ ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0,
+ vertCount, count);
+ } else {
+ glDrawArrays(convertElementMode(mode), 0, vertCount);
+ }
+ }
+
+ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+ }
+
+ if (indexBuf.isUpdateNeeded()) {
+ updateBufferData(indexBuf);
+ }
+
+ int bufId = indexBuf.getId();
+ assert bufId != -1;
+
+ if (context.boundElementArrayVBO != bufId) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufId);
+ context.boundElementArrayVBO = bufId;
+ //statistics.onVertexBufferUse(indexBuf, true);
+ }else{
+ //statistics.onVertexBufferUse(indexBuf, true);
+ }
+
+ int vertCount = mesh.getVertexCount();
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+
+ if (mesh.getMode() == Mode.Hybrid) {
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+ int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++) {
+ if (i == stripStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ } else if (i == fanStart) {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+
+ if (useInstancing) {
+ ARBDrawInstanced.glDrawElementsInstancedARB(elMode,
+ elementLength,
+ fmt,
+ curOffset,
+ count);
+ } else {
+ glDrawRangeElements(elMode,
+ 0,
+ vertCount,
+ elementLength,
+ fmt,
+ curOffset);
+ }
+
+ curOffset += elementLength * elSize;
+ }
+ } else {
+ if (useInstancing) {
+ ARBDrawInstanced.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
+ indexBuf.getData().limit(),
+ convertFormat(indexBuf.getFormat()),
+ 0,
+ count);
+ } else {
+ glDrawRangeElements(convertElementMode(mesh.getMode()),
+ 0,
+ vertCount,
+ indexBuf.getData().limit(),
+ convertFormat(indexBuf.getFormat()),
+ 0);
+ }
+ }
+ }
+
+ /*********************************************************************\
+ |* Render Calls *|
+ \*********************************************************************/
+ public int convertElementMode(Mesh.Mode mode) {
+ switch (mode) {
+ case Points:
+ return GL_POINTS;
+ case Lines:
+ return GL_LINES;
+ case LineLoop:
+ return GL_LINE_LOOP;
+ case LineStrip:
+ return GL_LINE_STRIP;
+ case Triangles:
+ return GL_TRIANGLES;
+ case TriangleFan:
+ return GL_TRIANGLE_FAN;
+ case TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ default:
+ throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
+ }
+ }
+
+ public void updateVertexArray(Mesh mesh) {
+ int id = mesh.getId();
+ if (id == -1) {
+ IntBuffer temp = intBuf1;
+ ARBVertexArrayObject.glGenVertexArrays(temp);
+ id = temp.get(0);
+ mesh.setId(id);
+ }
+
+ if (context.boundVertexArray != id) {
+ ARBVertexArrayObject.glBindVertexArray(id);
+ context.boundVertexArray = id;
+ }
+
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()) {
+ updateBufferData(interleavedData);
+ }
+
+ IntMap<VertexBuffer> buffers = mesh.getBuffers();
+ for (Entry<VertexBuffer> entry : buffers) {
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
+
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttrib(vb);
+ } else {
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+ }
+
+ private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
+ if (mesh.getId() == -1){
+ updateVertexArray(mesh);
+ }else{
+ // TODO: Check if it was updated
+ }
+
+ if (context.boundVertexArray != mesh.getId()) {
+ ARBVertexArrayObject.glBindVertexArray(mesh.getId());
+ context.boundVertexArray = mesh.getId();
+ }
+
+// IntMap<VertexBuffer> buffers = mesh.getBuffers();
+ VertexBuffer indices = null;
+ if (mesh.getNumLodLevels() > 0) {
+ indices = mesh.getLodLevel(lod);
+ } else {
+ indices = mesh.getBuffer(Type.Index);
+ }
+ if (indices != null) {
+ drawTriangleList(indices, mesh, count);
+ } else {
+ drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ private void renderMeshDefault(Mesh mesh, int lod, int count) {
+ VertexBuffer indices = null;
+
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()) {
+ updateBufferData(interleavedData);
+ }
+
+// IntMap<VertexBuffer> buffers = mesh.getBuffers();
+ SafeArrayList<VertexBuffer> buffersList = mesh.getBufferList();
+
+ if (mesh.getNumLodLevels() > 0) {
+ indices = mesh.getLodLevel(lod);
+ } else {
+ indices = mesh.getBuffer(Type.Index);
+ }
+
+// for (Entry<VertexBuffer> entry : buffers) {
+// VertexBuffer vb = entry.getValue();
+ for (VertexBuffer vb : mesh.getBufferList().getArray()){
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
+
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttrib(vb);
+ } else {
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+
+ if (indices != null) {
+ drawTriangleList(indices, mesh, count);
+ } else {
+ drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ public void renderMesh(Mesh mesh, int lod, int count) {
+ if (mesh.getVertexCount() == 0) {
+ return;
+ }
+
+ if (context.pointSprite && mesh.getMode() != Mode.Points){
+ // XXX: Hack, disable point sprite mode if mesh not in point mode
+ if (context.boundTextures[0] != null){
+ if (context.boundTextureUnit != 0){
+ glActiveTexture(GL_TEXTURE0);
+ context.boundTextureUnit = 0;
+ }
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ context.pointSprite = false;
+ }
+ }
+
+ if (context.pointSize != mesh.getPointSize()) {
+ glPointSize(mesh.getPointSize());
+ context.pointSize = mesh.getPointSize();
+ }
+ if (context.lineWidth != mesh.getLineWidth()) {
+ glLineWidth(mesh.getLineWidth());
+ context.lineWidth = mesh.getLineWidth();
+ }
+
+ statistics.onMeshDrawn(mesh, lod);
+// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
+// renderMeshVertexArray(mesh, lod, count);
+// }else{
+ renderMeshDefault(mesh, lod, count);
+// }
+ }
+}
diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java
new file mode 100644
index 0000000..2edeea4
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.nio.ByteBuffer;
+import static org.lwjgl.opengl.ATITextureCompression3DC.GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI;
+import static org.lwjgl.opengl.EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT;
+import static org.lwjgl.opengl.EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_LATC1_EXT;
+import static org.lwjgl.opengl.EXTTextureCompressionS3TC.*;
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL12.*;
+import static org.lwjgl.opengl.GL13.glCompressedTexImage2D;
+import static org.lwjgl.opengl.GL13.glCompressedTexImage3D;
+import static org.lwjgl.opengl.GL14.*;
+import org.lwjgl.opengl.*;
+
+public class TextureUtil {
+
+ private static boolean isFormatSupported(Format fmt, ContextCapabilities caps){
+ switch (fmt){
+ case ARGB4444:
+ return false;
+ case BGR8:
+ return caps.OpenGL12 || caps.GL_EXT_bgra;
+ case DXT1:
+ case DXT1A:
+ case DXT3:
+ case DXT5:
+ return caps.GL_EXT_texture_compression_s3tc;
+ case Depth:
+ case Depth16:
+ case Depth24:
+ case Depth32:
+ return caps.OpenGL14 || caps.GL_ARB_depth_texture;
+ case Depth32F:
+ case Luminance16F:
+ case Luminance16FAlpha16F:
+ case Luminance32F:
+ case RGBA16F:
+ case RGBA32F:
+ return caps.OpenGL30 || caps.GL_ARB_texture_float;
+ case LATC:
+ case LTC:
+ return caps.GL_EXT_texture_compression_latc;
+ case RGB9E5:
+ case RGB16F_to_RGB9E5:
+ return caps.OpenGL30 || caps.GL_EXT_texture_shared_exponent;
+ case RGB111110F:
+ case RGB16F_to_RGB111110F:
+ return caps.OpenGL30 || caps.GL_EXT_packed_float;
+ default:
+ return true;
+ }
+ }
+
+ public static void checkFormatSupported(Format fmt) {
+ if (!isFormatSupported(fmt, GLContext.getCapabilities())) {
+ throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware.");
+ }
+ }
+
+ public static int convertTextureFormat(Format fmt){
+ switch (fmt){
+ case Alpha16:
+ return GL_ALPHA16;
+ case Alpha8:
+ return GL_ALPHA8;
+ case DXT1:
+ return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+ case DXT1A:
+ return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ case DXT3:
+ return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ case DXT5:
+ return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ case LATC:
+ return GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT;
+ case Depth:
+ return GL_DEPTH_COMPONENT;
+ case Depth16:
+ return GL_DEPTH_COMPONENT16;
+ case Depth24:
+ return GL_DEPTH_COMPONENT24;
+ case Depth32:
+ return GL_DEPTH_COMPONENT32;
+ case Depth32F:
+ return ARBDepthBufferFloat.GL_DEPTH_COMPONENT32F;
+ case Luminance8Alpha8:
+ return GL_LUMINANCE8_ALPHA8;
+ case Luminance16Alpha16:
+ return GL_LUMINANCE16_ALPHA16;
+ case Luminance16FAlpha16F:
+ return ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB;
+ case Intensity8:
+ return GL_INTENSITY8;
+ case Intensity16:
+ return GL_INTENSITY16;
+ case Luminance8:
+ return GL_LUMINANCE8;
+ case Luminance16:
+ return GL_LUMINANCE16;
+ case Luminance16F:
+ return ARBTextureFloat.GL_LUMINANCE16F_ARB;
+ case Luminance32F:
+ return ARBTextureFloat.GL_LUMINANCE32F_ARB;
+ case RGB10:
+ return GL_RGB10;
+ case RGB16:
+ return GL_RGB16;
+ case RGB111110F:
+ return EXTPackedFloat.GL_R11F_G11F_B10F_EXT;
+ case RGB9E5:
+ return EXTTextureSharedExponent.GL_RGB9_E5_EXT;
+ case RGB16F:
+ return ARBTextureFloat.GL_RGB16F_ARB;
+ case RGBA16F:
+ return ARBTextureFloat.GL_RGBA16F_ARB;
+ case RGB32F:
+ return ARBTextureFloat.GL_RGB32F_ARB;
+ case RGB5A1:
+ return GL_RGB5_A1;
+ case BGR8:
+ return GL_RGB8;
+ case RGB8:
+ return GL_RGB8;
+ case RGBA16:
+ return GL_RGBA16;
+ case RGBA8:
+ return GL_RGBA8;
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: "+fmt);
+ }
+ }
+
+ public static void uploadTexture(Image img,
+ int target,
+ int index,
+ int border,
+ boolean tdc){
+ Image.Format fmt = img.getFormat();
+
+ checkFormatSupported(fmt);
+
+ ByteBuffer data;
+ if (index >= 0 && img.getData() != null && img.getData().size() > 0){
+ data = img.getData(index);
+ }else{
+ data = null;
+ }
+
+ int width = img.getWidth();
+ int height = img.getHeight();
+ int depth = img.getDepth();
+
+ boolean compress = false;
+ int internalFormat = -1;
+ int format = -1;
+ int dataType = -1;
+
+ switch (fmt){
+ case Alpha16:
+ internalFormat = GL_ALPHA16;
+ format = GL_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Alpha8:
+ internalFormat = GL_ALPHA8;
+ format = GL_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case DXT1:
+ compress = true;
+ internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+ format = GL_RGB;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case DXT1A:
+ compress = true;
+ internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ format = GL_RGBA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case DXT3:
+ compress = true;
+ internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ format = GL_RGBA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case DXT5:
+ compress = true;
+ internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ format = GL_RGBA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case LATC:
+ compress = true;
+ if (tdc){
+ internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI;
+ }else{
+ internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT;
+ }
+ format = GL_LUMINANCE_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case LTC:
+ compress = true;
+ internalFormat = GL_COMPRESSED_LUMINANCE_LATC1_EXT;
+ format = GL_LUMINANCE_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Depth:
+ internalFormat = GL_DEPTH_COMPONENT;
+ format = GL_DEPTH_COMPONENT;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Depth16:
+ internalFormat = GL_DEPTH_COMPONENT16;
+ format = GL_DEPTH_COMPONENT;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Depth24:
+ internalFormat = GL_DEPTH_COMPONENT24;
+ format = GL_DEPTH_COMPONENT;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Depth32:
+ internalFormat = GL_DEPTH_COMPONENT32;
+ format = GL_DEPTH_COMPONENT;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Depth32F:
+ internalFormat = NVDepthBufferFloat.GL_DEPTH_COMPONENT32F_NV;
+ format = GL_DEPTH_COMPONENT;
+ dataType = GL_FLOAT;
+ break;
+ case Luminance16FAlpha16F:
+ internalFormat = ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB;
+ format = GL_LUMINANCE_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Intensity8:
+ internalFormat = GL_INTENSITY8;
+ format = GL_INTENSITY;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Intensity16:
+ internalFormat = GL_INTENSITY16;
+ format = GL_INTENSITY;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Luminance8:
+ internalFormat = GL_LUMINANCE8;
+ format = GL_LUMINANCE;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Luminance8Alpha8:
+ internalFormat = GL_LUMINANCE8_ALPHA8;
+ format = GL_LUMINANCE_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16Alpha16:
+ internalFormat = GL_LUMINANCE16_ALPHA16;
+ format = GL_LUMINANCE_ALPHA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16:
+ internalFormat = GL_LUMINANCE16;
+ format = GL_LUMINANCE;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16F:
+ internalFormat = ARBTextureFloat.GL_LUMINANCE16F_ARB;
+ format = GL_LUMINANCE;
+ dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB;
+ break;
+ case Luminance32F:
+ internalFormat = ARBTextureFloat.GL_LUMINANCE32F_ARB;
+ format = GL_LUMINANCE;
+ dataType = GL_FLOAT;
+ break;
+ case RGB10:
+ internalFormat = GL_RGB10;
+ format = GL_RGB;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case RGB16:
+ internalFormat = GL_RGB16;
+ format = GL_RGB;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case RGB111110F:
+ internalFormat = EXTPackedFloat.GL_R11F_G11F_B10F_EXT;
+ format = GL_RGB;
+ dataType = EXTPackedFloat.GL_UNSIGNED_INT_10F_11F_11F_REV_EXT;
+ break;
+ case RGB16F_to_RGB111110F:
+ internalFormat = EXTPackedFloat.GL_R11F_G11F_B10F_EXT;
+ format = GL_RGB;
+ dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB;
+ break;
+ case RGB16F_to_RGB9E5:
+ internalFormat = EXTTextureSharedExponent.GL_RGB9_E5_EXT;
+ format = GL_RGB;
+ dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB;
+ break;
+ case RGB9E5:
+ internalFormat = EXTTextureSharedExponent.GL_RGB9_E5_EXT;
+ format = GL_RGB;
+ dataType = EXTTextureSharedExponent.GL_UNSIGNED_INT_5_9_9_9_REV_EXT;
+ break;
+ case RGB16F:
+ internalFormat = ARBTextureFloat.GL_RGB16F_ARB;
+ format = GL_RGB;
+ dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB;
+ break;
+ case RGBA16F:
+ internalFormat = ARBTextureFloat.GL_RGBA16F_ARB;
+ format = GL_RGBA;
+ dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB;
+ break;
+ case RGB32F:
+ internalFormat = ARBTextureFloat.GL_RGB32F_ARB;
+ format = GL_RGB;
+ dataType = GL_FLOAT;
+ break;
+ case RGBA32F:
+ internalFormat = ARBTextureFloat.GL_RGBA32F_ARB;
+ format = GL_RGBA;
+ dataType = GL_FLOAT;
+ break;
+ case RGB5A1:
+ internalFormat = GL_RGB5_A1;
+ format = GL_RGBA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case RGB8:
+ internalFormat = GL_RGB8;
+ format = GL_RGB;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case BGR8:
+ internalFormat = GL_RGB8;
+ format = GL_BGR;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case RGBA16:
+ internalFormat = GL_RGBA16;
+ format = GL_RGBA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case RGBA8:
+ internalFormat = GL_RGBA8;
+ format = GL_RGBA;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ case ABGR8:
+ internalFormat = GL_RGBA8;
+ format = EXTAbgr.GL_ABGR_EXT;
+ dataType = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: "+fmt);
+ }
+
+ if (data != null)
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ int[] mipSizes = img.getMipMapSizes();
+ int pos = 0;
+ // TODO: Remove unneccessary allocation
+ if (mipSizes == null){
+ if (data != null)
+ mipSizes = new int[]{ data.capacity() };
+ else
+ mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 };
+ }
+
+ boolean subtex = false;
+ int samples = img.getMultiSamples();
+
+ for (int i = 0; i < mipSizes.length; i++){
+ int mipWidth = Math.max(1, width >> i);
+ int mipHeight = Math.max(1, height >> i);
+ int mipDepth = Math.max(1, depth >> i);
+
+ if (data != null){
+ data.position(pos);
+ data.limit(pos + mipSizes[i]);
+ }
+
+ if (compress && data != null){
+ if (target == GL_TEXTURE_3D){
+ glCompressedTexImage3D(target,
+ i,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ mipDepth,
+ border,
+ data);
+ }else{
+ //all other targets use 2D: array, cubemap, 2d
+ glCompressedTexImage2D(target,
+ i,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ border,
+ data);
+ }
+ }else{
+ if (target == GL_TEXTURE_3D){
+ glTexImage3D(target,
+ i,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ mipDepth,
+ border,
+ format,
+ dataType,
+ data);
+ }else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT){
+ // prepare data for 2D array
+ // or upload slice
+ if (index == -1){
+ glTexImage3D(target,
+ 0,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ img.getData().size(), //# of slices
+ border,
+ format,
+ dataType,
+ data);
+ }else{
+ glTexSubImage3D(target,
+ i, // level
+ 0, // xoffset
+ 0, // yoffset
+ index, // zoffset
+ width, // width
+ height, // height
+ 1, // depth
+ format,
+ dataType,
+ data);
+ }
+ }else{
+ if (subtex){
+ if (samples > 1)
+ throw new IllegalStateException("Cannot update multisample textures");
+
+ glTexSubImage2D(target,
+ i,
+ 0, 0,
+ mipWidth, mipHeight,
+ format,
+ dataType,
+ data);
+ }else{
+ if (samples > 1){
+ ARBTextureMultisample.glTexImage2DMultisample(target,
+ samples,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ true);
+ }else{
+ glTexImage2D(target,
+ i,
+ internalFormat,
+ mipWidth,
+ mipHeight,
+ border,
+ format,
+ dataType,
+ data);
+ }
+ }
+ }
+ }
+
+ pos += mipSizes[i];
+ }
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
new file mode 100644
index 0000000..b1171cd
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.lwjgl.JInputJoyInput;
+import com.jme3.input.lwjgl.LwjglKeyInput;
+import com.jme3.input.lwjgl.LwjglMouseInput;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystem;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.Sys;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.OpenGLException;
+import org.lwjgl.opengl.Util;
+
+public abstract class LwjglAbstractDisplay extends LwjglContext implements Runnable {
+
+ private static final Logger logger = Logger.getLogger(LwjglAbstractDisplay.class.getName());
+
+ protected AtomicBoolean needClose = new AtomicBoolean(false);
+ protected boolean wasActive = false;
+ protected int frameRate = 0;
+ protected boolean autoFlush = true;
+
+ /**
+ * @return Type.Display or Type.Canvas
+ */
+ public abstract Type getType();
+
+ /**
+ * Set the title if its a windowed display
+ * @param title
+ */
+ public abstract void setTitle(String title);
+
+ /**
+ * Restart if its a windowed or full-screen display.
+ */
+ public abstract void restart();
+
+ /**
+ * Apply the settings, changing resolution, etc.
+ * @param settings
+ */
+ protected abstract void createContext(AppSettings settings) throws LWJGLException;
+
+ /**
+ * Destroy the context.
+ */
+ protected abstract void destroyContext();
+
+ /**
+ * Does LWJGL display initialization in the OpenGL thread
+ */
+ protected void initInThread(){
+ try{
+ if (!JmeSystem.isLowPermissions()){
+ // Enable uncaught exception handler only for current thread
+ Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+ if (needClose.get()){
+ // listener.handleError() has requested the
+ // context to close. Satisfy request.
+ deinitInThread();
+ }
+ }
+ });
+ }
+
+ // For canvas, this will create a pbuffer,
+ // allowing us to query information.
+ // When the canvas context becomes available, it will
+ // be replaced seamlessly.
+ createContext(settings);
+ printContextInitInfo();
+
+ created.set(true);
+ } catch (Exception ex){
+ try {
+ if (Display.isCreated())
+ Display.destroy();
+ } catch (Exception ex2){
+ logger.log(Level.WARNING, null, ex2);
+ }
+
+ listener.handleError("Failed to create display", ex);
+ return; // if we failed to create display, do not continue
+ }
+ super.internalCreate();
+ listener.initialize();
+ }
+
+ protected boolean checkGLError(){
+ try {
+ Util.checkGLError();
+ } catch (OpenGLException ex){
+ listener.handleError("An OpenGL error has occured!", ex);
+ }
+ // NOTE: Always return true since this is used in an "assert" statement
+ return true;
+ }
+
+ /**
+ * execute one iteration of the render loop in the OpenGL thread
+ */
+ protected void runLoop(){
+ if (!created.get())
+ throw new IllegalStateException();
+
+ listener.update();
+
+ // All this does is call swap buffers
+ // If the canvas is not active, there's no need to waste time
+ // doing that ..
+ if (renderable.get()){
+ assert checkGLError();
+
+ // calls swap buffers, etc.
+ try {
+ if (autoFlush){
+ Display.update(false);
+ }else{
+ Display.processMessages();
+ Thread.sleep(50);
+ // add a small wait
+ // to reduce CPU usage
+ }
+ } catch (Throwable ex){
+ listener.handleError("Error while swapping buffers", ex);
+ }
+ }
+
+ if (frameRate > 0)
+ Display.sync(frameRate);
+
+ if (renderable.get()){
+ if (autoFlush){
+ // check input after we synchronize with framerate.
+ // this reduces input lag.
+ Display.processMessages();
+ }
+ }
+
+ // Subclasses just call GLObjectManager clean up objects here
+ // it is safe .. for now.
+ renderer.onFrame();
+ }
+
+ /**
+ * De-initialize in the OpenGL thread.
+ */
+ protected void deinitInThread(){
+ destroyContext();
+
+ listener.destroy();
+ logger.info("Display destroyed.");
+ super.internalDestroy();
+ }
+
+ public void run(){
+ if (listener == null)
+ throw new IllegalStateException("SystemListener is not set on context!"
+ + "Must set with JmeContext.setSystemListner().");
+
+ logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion());
+ initInThread();
+ while (true){
+ if (renderable.get()){
+ if (Display.isCloseRequested())
+ listener.requestClose(false);
+
+ if (wasActive != Display.isActive()) {
+ if (!wasActive) {
+ listener.gainFocus();
+ timer.reset();
+ wasActive = true;
+ } else {
+ listener.loseFocus();
+ wasActive = false;
+ }
+ }
+ }
+
+ runLoop();
+
+ if (needClose.get())
+ break;
+ }
+ deinitInThread();
+ }
+
+ public JoyInput getJoyInput() {
+ if (joyInput == null){
+ joyInput = new JInputJoyInput();
+ }
+ return joyInput;
+ }
+
+ public MouseInput getMouseInput() {
+ if (mouseInput == null){
+ mouseInput = new LwjglMouseInput(this);
+ }
+ return mouseInput;
+ }
+
+ public KeyInput getKeyInput() {
+ if (keyInput == null){
+ keyInput = new LwjglKeyInput(this);
+ }
+ return keyInput;
+ }
+
+ public TouchInput getTouchInput() {
+ return null;
+ }
+
+ public void setAutoFlushFrames(boolean enabled){
+ this.autoFlush = enabled;
+ }
+
+ public void destroy(boolean waitFor){
+ needClose.set(true);
+ if (waitFor)
+ waitFor(false);
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglCanvas.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglCanvas.java
new file mode 100644
index 0000000..bb18543
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglCanvas.java
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystem;
+import com.jme3.system.Platform;
+import java.awt.Canvas;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.Pbuffer;
+import org.lwjgl.opengl.PixelFormat;
+
+public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
+
+ protected static final int TASK_NOTHING = 0,
+ TASK_DESTROY_DISPLAY = 1,
+ TASK_CREATE_DISPLAY = 2,
+ TASK_COMPLETE = 3;
+
+// protected static final boolean USE_SHARED_CONTEXT =
+// Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
+
+ protected static final boolean USE_SHARED_CONTEXT = false;
+
+ private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
+ private Canvas canvas;
+ private int width;
+ private int height;
+
+ private final Object taskLock = new Object();
+ private int desiredTask = TASK_NOTHING;
+
+ private Thread renderThread;
+ private boolean runningFirstTime = true;
+ private boolean mouseWasGrabbed = false;
+
+ private boolean mouseWasCreated = false;
+ private boolean keyboardWasCreated = false;
+
+ private Pbuffer pbuffer;
+ private PixelFormat pbufferFormat;
+ private PixelFormat canvasFormat;
+
+ private class GLCanvas extends Canvas {
+ @Override
+ public void addNotify(){
+ super.addNotify();
+
+ if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED)
+ return; // already destroyed.
+
+ if (renderThread == null){
+ logger.log(Level.INFO, "EDT: Creating OGL thread.");
+
+ // Also set some settings on the canvas here.
+ // So we don't do it outside the AWT thread.
+ canvas.setFocusable(true);
+ canvas.setIgnoreRepaint(true);
+
+ renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
+ renderThread.start();
+ }else if (needClose.get()){
+ return;
+ }
+
+ logger.log(Level.INFO, "EDT: Telling OGL to create display ..");
+ synchronized (taskLock){
+ desiredTask = TASK_CREATE_DISPLAY;
+// while (desiredTask != TASK_COMPLETE){
+// try {
+// taskLock.wait();
+// } catch (InterruptedException ex) {
+// return;
+// }
+// }
+// desiredTask = TASK_NOTHING;
+ }
+// logger.log(Level.INFO, "EDT: OGL has created the display");
+ }
+
+ @Override
+ public void removeNotify(){
+ if (needClose.get()){
+ logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas.");
+ super.removeNotify();
+ return;
+ }
+
+ // We must tell GL context to shutdown and wait for it to
+ // shutdown, otherwise, issues will occur.
+ logger.log(Level.INFO, "EDT: Telling OGL to destroy display ..");
+ synchronized (taskLock){
+ desiredTask = TASK_DESTROY_DISPLAY;
+ while (desiredTask != TASK_COMPLETE){
+ try {
+ taskLock.wait();
+ } catch (InterruptedException ex){
+ super.removeNotify();
+ return;
+ }
+ }
+ desiredTask = TASK_NOTHING;
+ }
+
+ logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death");
+ // GL context is dead at this point
+
+ super.removeNotify();
+ }
+ }
+
+ public LwjglCanvas(){
+ super();
+ canvas = new GLCanvas();
+ }
+
+ @Override
+ public Type getType() {
+ return Type.Canvas;
+ }
+
+ public void create(boolean waitFor){
+ if (renderThread == null){
+ logger.log(Level.INFO, "MAIN: Creating OGL thread.");
+
+ renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
+ renderThread.start();
+ }
+ // do not do anything.
+ // superclass's create() will be called at initInThread()
+ if (waitFor)
+ waitFor(true);
+ }
+
+ @Override
+ public void setTitle(String title) {
+ }
+
+ @Override
+ public void restart() {
+ frameRate = settings.getFrameRate();
+ // TODO: Handle other cases, like change of pixel format, etc.
+ }
+
+ public Canvas getCanvas(){
+ return canvas;
+ }
+
+ @Override
+ protected void runLoop(){
+ if (desiredTask != TASK_NOTHING){
+ synchronized (taskLock){
+ switch (desiredTask){
+ case TASK_CREATE_DISPLAY:
+ logger.log(Level.INFO, "OGL: Creating display ..");
+ restoreCanvas();
+ listener.gainFocus();
+ desiredTask = TASK_NOTHING;
+ break;
+ case TASK_DESTROY_DISPLAY:
+ logger.log(Level.INFO, "OGL: Destroying display ..");
+ listener.loseFocus();
+ pauseCanvas();
+ break;
+ }
+ desiredTask = TASK_COMPLETE;
+ taskLock.notifyAll();
+ }
+ }
+
+ if (renderable.get()){
+ int newWidth = Math.max(canvas.getWidth(), 1);
+ int newHeight = Math.max(canvas.getHeight(), 1);
+ if (width != newWidth || height != newHeight){
+ width = newWidth;
+ height = newHeight;
+ if (listener != null){
+ listener.reshape(width, height);
+ }
+ }
+ }else{
+ if (frameRate <= 0){
+ // NOTE: MUST be done otherwise
+ // Windows OS will freeze
+ Display.sync(30);
+ }
+ }
+
+ super.runLoop();
+ }
+
+ private void pauseCanvas(){
+ if (Mouse.isCreated()){
+ if (Mouse.isGrabbed()){
+ Mouse.setGrabbed(false);
+ mouseWasGrabbed = true;
+ }
+ mouseWasCreated = true;
+ Mouse.destroy();
+ }
+ if (Keyboard.isCreated()){
+ keyboardWasCreated = true;
+ Keyboard.destroy();
+ }
+
+ renderable.set(false);
+ destroyContext();
+ }
+
+ /**
+ * Called to restore the canvas.
+ */
+ private void restoreCanvas(){
+ logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable..");
+ while (!canvas.isDisplayable()){
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
+ }
+ }
+
+ logger.log(Level.INFO, "OGL: Creating display context ..");
+
+ // Set renderable to true, since canvas is now displayable.
+ renderable.set(true);
+ createContext(settings);
+
+ logger.log(Level.INFO, "OGL: Display is active!");
+
+ try {
+ if (mouseWasCreated){
+ Mouse.create();
+ if (mouseWasGrabbed){
+ Mouse.setGrabbed(true);
+ mouseWasGrabbed = false;
+ }
+ }
+ if (keyboardWasCreated){
+ Keyboard.create();
+ keyboardWasCreated = false;
+ }
+ } catch (LWJGLException ex){
+ logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);
+ }
+
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ canvas.requestFocus();
+ }
+ });
+ }
+
+ /**
+ * It seems it is best to use one pixel format for all shared contexts.
+ * @see <a href="http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
+ */
+ protected PixelFormat acquirePixelFormat(boolean forPbuffer){
+ if (forPbuffer){
+ // Use 0 samples for pbuffer format, prevents
+ // crashes on bad drivers
+ if (pbufferFormat == null){
+ pbufferFormat = new PixelFormat(settings.getBitsPerPixel(),
+ 0,
+ settings.getDepthBits(),
+ settings.getStencilBits(),
+ 0);
+ }
+ return pbufferFormat;
+ }else{
+ if (canvasFormat == null){
+ int samples = 0;
+ if (settings.getSamples() > 1){
+ samples = settings.getSamples();
+ }
+ canvasFormat = new PixelFormat(settings.getBitsPerPixel(),
+ 0,
+ settings.getDepthBits(),
+ settings.getStencilBits(),
+ samples);
+ }
+ return canvasFormat;
+ }
+ }
+
+ /**
+ * Makes sure the pbuffer is available and ready for use
+ */
+ protected void makePbufferAvailable() throws LWJGLException{
+ if (pbuffer != null && pbuffer.isBufferLost()){
+ logger.log(Level.WARNING, "PBuffer was lost!");
+ pbuffer.destroy();
+ pbuffer = null;
+ }
+
+ if (pbuffer == null) {
+ pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);
+ pbuffer.makeCurrent();
+ logger.log(Level.INFO, "OGL: Pbuffer has been created");
+
+ // Any created objects are no longer valid
+ if (!runningFirstTime){
+ renderer.resetGLObjects();
+ }
+ }
+
+ pbuffer.makeCurrent();
+ if (!pbuffer.isCurrent()){
+ throw new LWJGLException("Pbuffer cannot be made current");
+ }
+ }
+
+ protected void destroyPbuffer(){
+ if (pbuffer != null){
+ if (!pbuffer.isBufferLost()){
+ pbuffer.destroy();
+ }
+ pbuffer = null;
+ }
+ }
+
+ /**
+ * This is called:
+ * 1) When the context thread ends
+ * 2) Any time the canvas becomes non-displayable
+ */
+ protected void destroyContext(){
+ try {
+ // invalidate the state so renderer can resume operation
+ if (!USE_SHARED_CONTEXT){
+ renderer.cleanup();
+ }
+
+ if (Display.isCreated()){
+ /* FIXES:
+ * org.lwjgl.LWJGLException: X Error
+ * BadWindow (invalid Window parameter) request_code: 2 minor_code: 0
+ *
+ * Destroying keyboard early prevents the error above, triggered
+ * by destroying keyboard in by Display.destroy() or Display.setParent(null).
+ * Therefore Keyboard.destroy() should precede any of these calls.
+ */
+ if (Keyboard.isCreated()){
+ // Should only happen if called in
+ // LwjglAbstractDisplay.deinitInThread().
+ Keyboard.destroy();
+ }
+
+ //try {
+ // NOTE: On Windows XP, not calling setParent(null)
+ // freezes the application.
+ // On Mac it freezes the application.
+ // On Linux it fixes a crash with X Window System.
+ if (JmeSystem.getPlatform() == Platform.Windows32
+ || JmeSystem.getPlatform() == Platform.Windows64){
+ //Display.setParent(null);
+ }
+ //} catch (LWJGLException ex) {
+ // logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
+ //}
+
+ Display.destroy();
+ }
+
+ // The canvas is no longer visible,
+ // but the context thread is still running.
+ if (!needClose.get()){
+ // MUST make sure there's still a context current here ..
+ // Display is dead, make pbuffer available to the system
+ makePbufferAvailable();
+
+ renderer.invalidateState();
+ }else{
+ // The context thread is no longer running.
+ // Destroy pbuffer.
+ destroyPbuffer();
+ }
+ } catch (LWJGLException ex) {
+ listener.handleError("Failed make pbuffer available", ex);
+ }
+ }
+
+ /**
+ * This is called:
+ * 1) When the context thread starts
+ * 2) Any time the canvas becomes displayable again.
+ */
+ @Override
+ protected void createContext(AppSettings settings) {
+ // In case canvas is not visible, we still take framerate
+ // from settings to prevent "100% CPU usage"
+ frameRate = settings.getFrameRate();
+
+ try {
+ if (renderable.get()){
+ if (!runningFirstTime){
+ // because the display is a different opengl context
+ // must reset the context state.
+ if (!USE_SHARED_CONTEXT){
+ renderer.cleanup();
+ }
+ }
+
+ // if the pbuffer is currently active,
+ // make sure to deactivate it
+ destroyPbuffer();
+
+ if (Keyboard.isCreated()){
+ Keyboard.destroy();
+ }
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ex) {
+ }
+
+ Display.setVSyncEnabled(settings.isVSync());
+ Display.setParent(canvas);
+
+ if (USE_SHARED_CONTEXT){
+ Display.create(acquirePixelFormat(false), pbuffer);
+ }else{
+ Display.create(acquirePixelFormat(false));
+ }
+
+ renderer.invalidateState();
+ }else{
+ // First create the pbuffer, if it is needed.
+ makePbufferAvailable();
+ }
+
+ // At this point, the OpenGL context is active.
+ if (runningFirstTime){
+ // THIS is the part that creates the renderer.
+ // It must always be called, now that we have the pbuffer workaround.
+ initContextFirstTime();
+ runningFirstTime = false;
+ }
+ } catch (LWJGLException ex) {
+ listener.handleError("Failed to initialize OpenGL context", ex);
+ // TODO: Fix deadlock that happens after the error (throw runtime exception?)
+ }
+ }
+}
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglContext.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglContext.java
new file mode 100644
index 0000000..91b3694
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglContext.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.input.lwjgl.JInputJoyInput;
+import com.jme3.input.lwjgl.LwjglKeyInput;
+import com.jme3.input.lwjgl.LwjglMouseInput;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.lwjgl.LwjglGL1Renderer;
+import com.jme3.renderer.lwjgl.LwjglRenderer;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.SystemListener;
+import com.jme3.system.Timer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.opengl.*;
+
+/**
+ * A LWJGL implementation of a graphics context.
+ */
+public abstract class LwjglContext implements JmeContext {
+
+ private static final Logger logger = Logger.getLogger(LwjglContext.class.getName());
+
+ protected AtomicBoolean created = new AtomicBoolean(false);
+ protected AtomicBoolean renderable = new AtomicBoolean(false);
+ protected final Object createdLock = new Object();
+
+ protected AppSettings settings = new AppSettings(true);
+ protected Renderer renderer;
+ protected LwjglKeyInput keyInput;
+ protected LwjglMouseInput mouseInput;
+ protected JInputJoyInput joyInput;
+ protected Timer timer;
+ protected SystemListener listener;
+
+ public void setSystemListener(SystemListener listener){
+ this.listener = listener;
+ }
+
+ protected void printContextInitInfo(){
+ logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
+
+ logger.log(Level.INFO, "Adapter: {0}", Display.getAdapter());
+ logger.log(Level.INFO, "Driver Version: {0}", Display.getVersion());
+
+ String vendor = GL11.glGetString(GL11.GL_VENDOR);
+ logger.log(Level.INFO, "Vendor: {0}", vendor);
+
+ String version = GL11.glGetString(GL11.GL_VERSION);
+ logger.log(Level.INFO, "OpenGL Version: {0}", version);
+
+ String renderGl = GL11.glGetString(GL11.GL_RENDERER);
+ logger.log(Level.INFO, "Renderer: {0}", renderGl);
+
+ if (GLContext.getCapabilities().OpenGL20){
+ String shadingLang = GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION);
+ logger.log(Level.INFO, "GLSL Ver: {0}", shadingLang);
+ }
+ }
+
+ protected ContextAttribs createContextAttribs(){
+ if (settings.getBoolean("GraphicsDebug") || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
+ ContextAttribs attr;
+ if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
+ attr = new ContextAttribs(3, 3);
+ attr = attr.withProfileCore(true).withForwardCompatible(true).withProfileCompatibility(false);
+ }else{
+ attr = new ContextAttribs();
+ }
+ if (settings.getBoolean("GraphicsDebug")){
+ attr = attr.withDebug(true);
+ }
+ return attr;
+ }else{
+ return null;
+ }
+ }
+
+ protected void initContextFirstTime(){
+ if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2)
+ || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
+ renderer = new LwjglRenderer();
+ }else if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL1)){
+ renderer = new LwjglGL1Renderer();
+ }else if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL_ANY)){
+ // Choose an appropriate renderer based on capabilities
+ if (GLContext.getCapabilities().OpenGL20){
+ renderer = new LwjglRenderer();
+ }else{
+ renderer = new LwjglGL1Renderer();
+ }
+ }else{
+ throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());
+ }
+
+ // Init renderer
+ if (renderer instanceof LwjglRenderer){
+ ((LwjglRenderer)renderer).initialize();
+ }else if (renderer instanceof LwjglGL1Renderer){
+ ((LwjglGL1Renderer)renderer).initialize();
+ }else{
+ assert false;
+ }
+
+ // Init input
+ if (keyInput != null)
+ keyInput.initialize();
+
+ if (mouseInput != null)
+ mouseInput.initialize();
+
+ if (joyInput != null)
+ joyInput.initialize();
+ }
+
+ public void internalDestroy(){
+ renderer = null;
+ timer = null;
+ renderable.set(false);
+ synchronized (createdLock){
+ created.set(false);
+ createdLock.notifyAll();
+ }
+ }
+
+ public void internalCreate(){
+ timer = new LwjglTimer();
+
+ synchronized (createdLock){
+ created.set(true);
+ createdLock.notifyAll();
+ }
+
+ if (renderable.get()){
+ initContextFirstTime();
+ }else{
+ assert getType() == Type.Canvas;
+ }
+ }
+
+ public void create(){
+ create(false);
+ }
+
+ public void destroy(){
+ destroy(false);
+ }
+
+ protected void waitFor(boolean createdVal){
+ synchronized (createdLock){
+ while (created.get() != createdVal){
+ try {
+ createdLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+
+ public boolean isCreated(){
+ return created.get();
+ }
+
+ public boolean isRenderable(){
+ return renderable.get();
+ }
+
+ public void setSettings(AppSettings settings) {
+ this.settings.copyFrom(settings);
+ }
+
+ public AppSettings getSettings(){
+ return settings;
+ }
+
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ public Timer getTimer() {
+ return timer;
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglDisplay.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglDisplay.java
new file mode 100644
index 0000000..db106e7
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglDisplay.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.opengl.*;
+
+public class LwjglDisplay extends LwjglAbstractDisplay {
+
+ private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
+
+ private final AtomicBoolean needRestart = new AtomicBoolean(false);
+ private PixelFormat pixelFormat;
+
+ protected DisplayMode getFullscreenDisplayMode(int width, int height, int bpp, int freq){
+ try {
+ DisplayMode[] modes = Display.getAvailableDisplayModes();
+ for (DisplayMode mode : modes){
+ if (mode.getWidth() == width
+ && mode.getHeight() == height
+ && (mode.getBitsPerPixel() == bpp || (bpp==24&&mode.getBitsPerPixel()==32))
+ && mode.getFrequency() == freq){
+ return mode;
+ }
+ }
+ } catch (LWJGLException ex) {
+ listener.handleError("Failed to acquire fullscreen display mode!", ex);
+ }
+ return null;
+ }
+
+ protected void createContext(AppSettings settings) throws LWJGLException{
+ DisplayMode displayMode = null;
+ if (settings.getWidth() <= 0 || settings.getHeight() <= 0){
+ displayMode = Display.getDesktopDisplayMode();
+ settings.setResolution(displayMode.getWidth(), displayMode.getHeight());
+ }else if (settings.isFullscreen()){
+ displayMode = getFullscreenDisplayMode(settings.getWidth(), settings.getHeight(),
+ settings.getBitsPerPixel(), settings.getFrequency());
+ if (displayMode == null)
+ throw new RuntimeException("Unable to find fullscreen display mode matching settings");
+ }else{
+ displayMode = new DisplayMode(settings.getWidth(), settings.getHeight());
+ }
+
+ int samples = 0;
+ if (settings.getSamples() > 1){
+ samples = settings.getSamples();
+ }
+ PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(),
+ 0,
+ settings.getDepthBits(),
+ settings.getStencilBits(),
+ samples);
+
+ frameRate = settings.getFrameRate();
+ logger.log(Level.INFO, "Selected display mode: {0}", displayMode);
+
+ boolean pixelFormatChanged = false;
+ if (created.get() && (pixelFormat.getBitsPerPixel() != pf.getBitsPerPixel()
+ ||pixelFormat.getDepthBits() != pf.getDepthBits()
+ ||pixelFormat.getStencilBits() != pf.getStencilBits()
+ ||pixelFormat.getSamples() != pf.getSamples())){
+ renderer.resetGLObjects();
+ Display.destroy();
+ pixelFormatChanged = true;
+ }
+ pixelFormat = pf;
+
+ Display.setTitle(settings.getTitle());
+ if (displayMode != null){
+ if (settings.isFullscreen()){
+ Display.setDisplayModeAndFullscreen(displayMode);
+ }else{
+ Display.setFullscreen(false);
+ Display.setDisplayMode(displayMode);
+ }
+ }else{
+ Display.setFullscreen(settings.isFullscreen());
+ }
+
+ if (settings.getIcons() != null) {
+ Display.setIcon(imagesToByteBuffers(settings.getIcons()));
+ }
+
+ Display.setVSyncEnabled(settings.isVSync());
+
+ if (created.get() && !pixelFormatChanged){
+ Display.releaseContext();
+ Display.makeCurrent();
+ Display.update();
+ }
+
+ if (!created.get() || pixelFormatChanged){
+ ContextAttribs attr = createContextAttribs();
+ if (attr != null){
+ Display.create(pixelFormat, attr);
+ }else{
+ Display.create(pixelFormat);
+ }
+ renderable.set(true);
+
+ if (pixelFormatChanged && pixelFormat.getSamples() > 1
+ && GLContext.getCapabilities().GL_ARB_multisample){
+ GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
+ }
+ }
+ }
+
+ protected void destroyContext(){
+ try {
+ renderer.cleanup();
+ Display.releaseContext();
+ Display.destroy();
+ } catch (LWJGLException ex) {
+ listener.handleError("Failed to destroy context", ex);
+ }
+ }
+
+ public void create(boolean waitFor){
+ if (created.get()){
+ logger.warning("create() called when display is already created!");
+ return;
+ }
+
+ new Thread(this, "LWJGL Renderer Thread").start();
+ if (waitFor)
+ waitFor(true);
+ }
+
+ @Override
+ public void runLoop(){
+ // This method is overriden to do restart
+ if (needRestart.getAndSet(false)){
+ try{
+ createContext(settings);
+ }catch (LWJGLException ex){
+ logger.log(Level.SEVERE, "Failed to set display settings!", ex);
+ }
+ listener.reshape(settings.getWidth(), settings.getHeight());
+ logger.info("Display restarted.");
+ }
+
+ super.runLoop();
+ }
+
+ @Override
+ public void restart() {
+ if (created.get()){
+ needRestart.set(true);
+ }else{
+ logger.warning("Display is not created, cannot restart window.");
+ }
+ }
+
+ public Type getType() {
+ return Type.Display;
+ }
+
+ public void setTitle(String title){
+ if (created.get())
+ Display.setTitle(title);
+ }
+
+ private ByteBuffer[] imagesToByteBuffers(Object[] images) {
+ ByteBuffer[] out = new ByteBuffer[images.length];
+ for (int i = 0; i < images.length; i++) {
+ BufferedImage image = (BufferedImage) images[i];
+ out[i] = imageToByteBuffer(image);
+ }
+ return out;
+ }
+
+ private ByteBuffer imageToByteBuffer(BufferedImage image) {
+ if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
+ BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
+ Graphics2D g = convertedImage.createGraphics();
+ double width = image.getWidth() * (double) 1;
+ double height = image.getHeight() * (double) 1;
+ g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2),
+ (int) ((convertedImage.getHeight() - height) / 2),
+ (int) (width), (int) (height), null);
+ g.dispose();
+ image = convertedImage;
+ }
+
+ byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4];
+ int counter = 0;
+ for (int i = 0; i < image.getHeight(); i++) {
+ for (int j = 0; j < image.getWidth(); j++) {
+ int colorSpace = image.getRGB(j, i);
+ imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
+ imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
+ imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
+ imageBuffer[counter + 3] = (byte) (colorSpace >> 24);
+ counter += 4;
+ }
+ }
+ return ByteBuffer.wrap(imageBuffer);
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java
new file mode 100644
index 0000000..9124531
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.Sys;
+import org.lwjgl.opengl.*;
+
+public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
+
+ private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName());
+ private Pbuffer pbuffer;
+ protected AtomicBoolean needClose = new AtomicBoolean(false);
+ private int width;
+ private int height;
+ private PixelFormat pixelFormat;
+
+ protected void initInThread(){
+ if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){
+ logger.severe("Offscreen surfaces are not supported.");
+ return;
+ }
+
+ int samples = 0;
+ if (settings.getSamples() > 1){
+ samples = settings.getSamples();
+ }
+ pixelFormat = new PixelFormat(settings.getBitsPerPixel(),
+ 0,
+ settings.getDepthBits(),
+ settings.getStencilBits(),
+ settings.getSamples());
+
+ width = settings.getWidth();
+ height = settings.getHeight();
+ try{
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+ }
+ });
+
+ pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs());
+ pbuffer.makeCurrent();
+
+ renderable.set(true);
+
+ logger.info("Offscreen buffer created.");
+ printContextInitInfo();
+ } catch (LWJGLException ex){
+ listener.handleError("Failed to create display", ex);
+ } finally {
+ // TODO: It is possible to avoid "Failed to find pixel format"
+ // error here by creating a default display.
+ }
+ super.internalCreate();
+ listener.initialize();
+ }
+
+ protected boolean checkGLError(){
+ try {
+ Util.checkGLError();
+ } catch (OpenGLException ex){
+ listener.handleError("An OpenGL error has occured!", ex);
+ }
+ // NOTE: Always return true since this is used in an "assert" statement
+ return true;
+ }
+
+ protected void runLoop(){
+ if (!created.get())
+ throw new IllegalStateException();
+
+ if (pbuffer.isBufferLost()){
+ pbuffer.destroy();
+ try{
+ pbuffer = new Pbuffer(width, height, pixelFormat, null);
+ pbuffer.makeCurrent();
+ }catch (LWJGLException ex){
+ listener.handleError("Failed to restore pbuffer content", ex);
+ }
+ }
+
+ listener.update();
+ assert checkGLError();
+
+ renderer.onFrame();
+
+ int frameRate = settings.getFrameRate();
+ if (frameRate >= 1){
+ Display.sync(frameRate);
+ }
+ }
+
+ protected void deinitInThread(){
+ renderable.set(false);
+
+ listener.destroy();
+ renderer.cleanup();
+ pbuffer.destroy();
+ logger.info("Offscreen buffer destroyed.");
+ }
+
+ public void run(){
+ logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion());
+ initInThread();
+ while (!needClose.get()){
+ runLoop();
+ }
+ deinitInThread();
+ }
+
+ public void destroy(boolean waitFor){
+ needClose.set(true);
+ if (waitFor)
+ waitFor(false);
+ }
+
+ public void create(boolean waitFor){
+ if (created.get()){
+ logger.warning("create() called when pbuffer is already created!");
+ return;
+ }
+
+ new Thread(this, "LWJGL Renderer Thread").start();
+ if (waitFor)
+ waitFor(true);
+ }
+
+ public void restart() {
+ }
+
+ public void setAutoFlushFrames(boolean enabled){
+ }
+
+ public Type getType() {
+ return Type.OffscreenSurface;
+ }
+
+ public MouseInput getMouseInput() {
+ return new DummyMouseInput();
+ }
+
+ public KeyInput getKeyInput() {
+ return new DummyKeyInput();
+ }
+
+ public JoyInput getJoyInput() {
+ return null;
+ }
+
+ public TouchInput getTouchInput() {
+ return null;
+ }
+
+ public void setTitle(String title) {
+ }
+
+}
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglSmoothingTimer.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglSmoothingTimer.java
new file mode 100644
index 0000000..2c9b80a
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglSmoothingTimer.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.math.FastMath;
+import com.jme3.system.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.Sys;
+
+/**
+ * <code>Timer</code> handles the system's time related functionality. This
+ * allows the calculation of the framerate. To keep the framerate calculation
+ * accurate, a call to update each frame is required. <code>Timer</code> is a
+ * singleton object and must be created via the <code>getTimer</code> method.
+ *
+ * @author Mark Powell
+ * @version $Id: LWJGLTimer.java,v 1.21 2007/09/22 16:46:35 irrisor Exp $
+ */
+public class LwjglSmoothingTimer extends Timer {
+ private static final Logger logger = Logger.getLogger(LwjglSmoothingTimer.class
+ .getName());
+
+ private long lastFrameDiff;
+
+ //frame rate parameters.
+ private long oldTime;
+
+ private float lastTPF, lastFPS;
+
+ public static int TIMER_SMOOTHNESS = 32;
+
+ private long[] tpf;
+
+ private int smoothIndex;
+
+ private final static long LWJGL_TIMER_RES = Sys.getTimerResolution();
+ private final static float INV_LWJGL_TIMER_RES = ( 1f / LWJGL_TIMER_RES );
+ private static float invTimerRezSmooth;
+
+ public final static long LWJGL_TIME_TO_NANOS = (1000000000 / LWJGL_TIMER_RES);
+
+ private long startTime;
+
+ private boolean allSmooth = false;
+
+ /**
+ * Constructor builds a <code>Timer</code> object. All values will be
+ * initialized to it's default values.
+ */
+ public LwjglSmoothingTimer() {
+ reset();
+
+ //print timer resolution info
+ logger.log(Level.INFO, "Timer resolution: {0} ticks per second", LWJGL_TIMER_RES);
+ }
+
+ public void reset() {
+ lastFrameDiff = 0;
+ lastFPS = 0;
+ lastTPF = 0;
+
+ // init to -1 to indicate this is a new timer.
+ oldTime = -1;
+ //reset time
+ startTime = Sys.getTime();
+
+ tpf = new long[TIMER_SMOOTHNESS];
+ smoothIndex = TIMER_SMOOTHNESS - 1;
+ invTimerRezSmooth = ( 1f / (LWJGL_TIMER_RES * TIMER_SMOOTHNESS));
+
+ // set tpf... -1 values will not be used for calculating the average in update()
+ for ( int i = tpf.length; --i >= 0; ) {
+ tpf[i] = -1;
+ }
+ }
+
+ /**
+ * @see com.jme.util.Timer#getTime()
+ */
+ public long getTime() {
+ return Sys.getTime() - startTime;
+ }
+
+ /**
+ * @see com.jme.util.Timer#getResolution()
+ */
+ public long getResolution() {
+ return LWJGL_TIMER_RES;
+ }
+
+ /**
+ * <code>getFrameRate</code> returns the current frame rate since the last
+ * call to <code>update</code>.
+ *
+ * @return the current frame rate.
+ */
+ public float getFrameRate() {
+ return lastFPS;
+ }
+
+ public float getTimePerFrame() {
+ return lastTPF;
+ }
+
+ /**
+ * <code>update</code> recalulates the frame rate based on the previous
+ * call to update. It is assumed that update is called each frame.
+ */
+ public void update() {
+ long newTime = Sys.getTime();
+ long oldTime = this.oldTime;
+ this.oldTime = newTime;
+ if ( oldTime == -1 ) {
+ // For the first frame use 60 fps. This value will not be counted in further averages.
+ // This is done so initialization code between creating the timer and the first
+ // frame is not counted as a single frame on it's own.
+ lastTPF = 1 / 60f;
+ lastFPS = 1f / lastTPF;
+ return;
+ }
+
+ long frameDiff = newTime - oldTime;
+ long lastFrameDiff = this.lastFrameDiff;
+ if ( lastFrameDiff > 0 && frameDiff > lastFrameDiff *100 ) {
+ frameDiff = lastFrameDiff *100;
+ }
+ this.lastFrameDiff = frameDiff;
+ tpf[smoothIndex] = frameDiff;
+ smoothIndex--;
+ if ( smoothIndex < 0 ) {
+ smoothIndex = tpf.length - 1;
+ }
+
+ lastTPF = 0.0f;
+ if (!allSmooth) {
+ int smoothCount = 0;
+ for ( int i = tpf.length; --i >= 0; ) {
+ if ( tpf[i] != -1 ) {
+ lastTPF += tpf[i];
+ smoothCount++;
+ }
+ }
+ if (smoothCount == tpf.length)
+ allSmooth = true;
+ lastTPF *= ( INV_LWJGL_TIMER_RES / smoothCount );
+ } else {
+ for ( int i = tpf.length; --i >= 0; ) {
+ if ( tpf[i] != -1 ) {
+ lastTPF += tpf[i];
+ }
+ }
+ lastTPF *= invTimerRezSmooth;
+ }
+ if ( lastTPF < FastMath.FLT_EPSILON ) {
+ lastTPF = FastMath.FLT_EPSILON;
+ }
+
+ lastFPS = 1f / lastTPF;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this timer
+ * in the format: <br>
+ * <br>
+ * jme.utility.Timer@1db699b <br>
+ * Time: {LONG} <br>
+ * FPS: {LONG} <br>
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ String string = super.toString();
+ string += "\nTime: " + oldTime;
+ string += "\nFPS: " + getFrameRate();
+ return string;
+ }
+} \ No newline at end of file
diff --git a/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglTimer.java b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglTimer.java
new file mode 100644
index 0000000..681f9de
--- /dev/null
+++ b/engine/src/lwjgl/com/jme3/system/lwjgl/LwjglTimer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.system.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.Sys;
+
+/**
+ * <code>Timer</code> handles the system's time related functionality. This
+ * allows the calculation of the framerate. To keep the framerate calculation
+ * accurate, a call to update each frame is required. <code>Timer</code> is a
+ * singleton object and must be created via the <code>getTimer</code> method.
+ *
+ * @author Mark Powell
+ * @version $Id: LWJGLTimer.java,v 1.21 2007/09/22 16:46:35 irrisor Exp $
+ */
+public class LwjglTimer extends Timer {
+ private static final Logger logger = Logger.getLogger(LwjglTimer.class
+ .getName());
+
+ //frame rate parameters.
+ private long oldTime;
+ private long startTime;
+
+ private float lastTPF, lastFPS;
+
+ private final static long LWJGL_TIMER_RES = Sys.getTimerResolution();
+ private final static float INV_LWJGL_TIMER_RES = ( 1f / LWJGL_TIMER_RES );
+
+ public final static long LWJGL_TIME_TO_NANOS = (1000000000 / LWJGL_TIMER_RES);
+
+ /**
+ * Constructor builds a <code>Timer</code> object. All values will be
+ * initialized to it's default values.
+ */
+ public LwjglTimer() {
+ reset();
+ logger.log(Level.INFO, "Timer resolution: {0} ticks per second", LWJGL_TIMER_RES);
+ }
+
+ public void reset() {
+ startTime = Sys.getTime();
+ oldTime = getTime();
+ }
+
+ @Override
+ public float getTimeInSeconds() {
+ return getTime() * INV_LWJGL_TIMER_RES;
+ }
+
+ /**
+ * @see com.jme.util.Timer#getTime()
+ */
+ public long getTime() {
+ return Sys.getTime() - startTime;
+ }
+
+ /**
+ * @see com.jme.util.Timer#getResolution()
+ */
+ public long getResolution() {
+ return LWJGL_TIMER_RES;
+ }
+
+ /**
+ * <code>getFrameRate</code> returns the current frame rate since the last
+ * call to <code>update</code>.
+ *
+ * @return the current frame rate.
+ */
+ public float getFrameRate() {
+ return lastFPS;
+ }
+
+ public float getTimePerFrame() {
+ return lastTPF;
+ }
+
+ /**
+ * <code>update</code> recalulates the frame rate based on the previous
+ * call to update. It is assumed that update is called each frame.
+ */
+ public void update() {
+ long curTime = getTime();
+ lastTPF = (curTime - oldTime) * (1.0f / LWJGL_TIMER_RES);
+ lastFPS = 1.0f / lastTPF;
+ oldTime = curTime;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this timer
+ * in the format: <br>
+ * <br>
+ * jme.utility.Timer@1db699b <br>
+ * Time: {LONG} <br>
+ * FPS: {LONG} <br>
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ String string = super.toString();
+ string += "\nTime: " + oldTime;
+ string += "\nFPS: " + getFrameRate();
+ return string;
+ }
+} \ No newline at end of file
diff --git a/engine/src/networking/com/jme3/network/AbstractMessage.java b/engine/src/networking/com/jme3/network/AbstractMessage.java
new file mode 100644
index 0000000..7b04fa3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/AbstractMessage.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Interface implemented by all network messages.
+ *
+ * @version $Revision: 7070 $
+ * @author Paul Speed
+ */
+@Serializable()
+public abstract class AbstractMessage implements Message
+{
+ private transient boolean reliable = true;
+
+ protected AbstractMessage()
+ {
+ }
+
+ protected AbstractMessage( boolean reliable )
+ {
+ this.reliable = reliable;
+ }
+
+ /**
+ * Sets this message to 'reliable' or not and returns this
+ * message.
+ */
+ public Message setReliable(boolean f)
+ {
+ this.reliable = f;
+ return this;
+ }
+
+ /**
+ * Indicates which way an outgoing message should be sent
+ * or which way an incoming message was sent.
+ */
+ public boolean isReliable()
+ {
+ return reliable;
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/Client.java b/engine/src/networking/com/jme3/network/Client.java
new file mode 100644
index 0000000..86ee8c6
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/Client.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * Represents a remote connection to a server that can be used
+ * for sending and receiving messages.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public interface Client extends MessageConnection
+{
+ /**
+ * Starts the client allowing it to begin processing incoming
+ * messages and delivering them to listeners.
+ */
+ public void start();
+
+ /**
+ * Returns true if this client is fully connected to the
+ * host.
+ */
+ public boolean isConnected();
+
+ /**
+ * Returns a unique ID for this client within the remote
+ * server or -1 if this client isn't fully connected to the
+ * server.
+ */
+ public int getId();
+
+ /**
+ * Returns the 'game name' for servers to which this client should be able
+ * to connect. This should match the 'game name' set on the server or this
+ * client will be turned away.
+ */
+ public String getGameName();
+
+ /**
+ * Returns the game-specific version of the server this client should
+ * be able to connect to.
+ */
+ public int getVersion();
+
+ /**
+ * Sends a message to the server.
+ */
+ public void send( Message message );
+
+ /**
+ * Sends a message to the other end of the connection using
+ * the specified alternate channel.
+ */
+ public void send( int channel, Message message );
+
+ /**
+ * Closes this connection to the server.
+ */
+ public void close();
+
+ /**
+ * Adds a listener that will be notified about connection
+ * state changes.
+ */
+ public void addClientStateListener( ClientStateListener listener );
+
+ /**
+ * Removes a previously registered connection listener.
+ */
+ public void removeClientStateListener( ClientStateListener listener );
+
+ /**
+ * Adds a listener that will be notified when any message or object
+ * is received from the server.
+ */
+ public void addMessageListener( MessageListener<? super Client> listener );
+
+ /**
+ * Adds a listener that will be notified when messages of the specified
+ * types are received.
+ */
+ public void addMessageListener( MessageListener<? super Client> listener, Class... classes );
+
+ /**
+ * Removes a previously registered wildcard listener. This does
+ * not remove this listener from any type-specific registrations.
+ */
+ public void removeMessageListener( MessageListener<? super Client> listener );
+
+ /**
+ * Removes a previously registered type-specific listener from
+ * the specified types.
+ */
+ public void removeMessageListener( MessageListener<? super Client> listener, Class... classes );
+
+ /**
+ * Adds a listener that will be notified when any connection errors
+ * occur. If a client has no error listeners then the default behavior
+ * is to close the connection and provide an appropriate DisconnectInfo
+ * to any ClientStateListeners. If the application adds its own error
+ * listeners then it must take care of closing the connection itself.
+ */
+ public void addErrorListener( ErrorListener<? super Client> listener );
+
+ /**
+ * Removes a previously registered error listener.
+ */
+ public void removeErrorListener( ErrorListener<? super Client> listener );
+}
+
+
diff --git a/engine/src/networking/com/jme3/network/ClientStateListener.java b/engine/src/networking/com/jme3/network/ClientStateListener.java
new file mode 100644
index 0000000..65ebe11
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/ClientStateListener.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * Listener that is notified about the connection state of
+ * a Client.
+ *
+ * @version $Revision: 7451 $
+ * @author Paul Speed
+ */
+public interface ClientStateListener
+{
+ /**
+ * Called when the specified client is fully connected to
+ * the remote server.
+ */
+ public void clientConnected( Client c );
+
+ /**
+ * Called when the client has disconnected from the remote
+ * server. If info is null then the client shut down the
+ * connection normally, otherwise the info object contains
+ * additional information about the disconnect.
+ */
+ public void clientDisconnected( Client c, DisconnectInfo info );
+
+ /**
+ * Provided with the clientDisconnected() notification to
+ * include additional information about the disconnect.
+ */
+ public class DisconnectInfo
+ {
+ public String reason;
+ public Throwable error;
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/ConnectionListener.java b/engine/src/networking/com/jme3/network/ConnectionListener.java
new file mode 100644
index 0000000..bcaf1e3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/ConnectionListener.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * Listener that is notified about connection arrivals and
+ * removals within a server.
+ *
+ * @version $Revision: 7010 $
+ * @author Paul Speed
+ */
+public interface ConnectionListener
+{
+ /**
+ * Called when a connection has been added to the specified server and
+ * is fully setup.
+ */
+ public void connectionAdded( Server server, HostedConnection conn );
+
+ /**
+ * Called when a connection has been removed from the specified
+ * server.
+ */
+ public void connectionRemoved( Server server, HostedConnection conn );
+}
diff --git a/engine/src/networking/com/jme3/network/ErrorListener.java b/engine/src/networking/com/jme3/network/ErrorListener.java
new file mode 100644
index 0000000..af4beae
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/ErrorListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * Notified when errors happen on a connection.
+ *
+ * @version $Revision: 7451 $
+ * @author Paul Speed
+ */
+public interface ErrorListener<S>
+{
+ public void handleError( S source, Throwable t );
+}
diff --git a/engine/src/networking/com/jme3/network/Filter.java b/engine/src/networking/com/jme3/network/Filter.java
new file mode 100644
index 0000000..dff3e94
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/Filter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * Determines a true or false value for a given input.
+ *
+ * @version $Revision: 7029 $
+ * @author Paul Speed
+ */
+public interface Filter<T>
+{
+ /**
+ * Returns true if the specified input is accepted by this
+ * filter.
+ */
+ public boolean apply( T input );
+}
+
+
diff --git a/engine/src/networking/com/jme3/network/Filters.java b/engine/src/networking/com/jme3/network/Filters.java
new file mode 100644
index 0000000..c255201
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/Filters.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * Static utility methods pertaining to Filter instances.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class Filters
+{
+ /**
+ * Creates a filter that returns true for any value in the specified
+ * list of values and false for all other cases.
+ */
+ public static <T> Filter<T> in( T... values )
+ {
+ return in( new HashSet<T>(Arrays.asList(values)) );
+ }
+
+ /**
+ * Creates a filter that returns true for any value in the specified
+ * collection and false for all other cases.
+ */
+ public static <T> Filter<T> in( Collection<? extends T> collection )
+ {
+ return new InFilter<T>(collection);
+ }
+
+ /**
+ * Creates a filter that returns true for any value NOT in the specified
+ * list of values and false for all other cases. This is the equivalent
+ * of calling not(in(values)).
+ */
+ public static <T> Filter<T> notIn( T... values )
+ {
+ return not( in( values ) );
+ }
+
+ /**
+ * Creates a filter that returns true for any value NOT in the specified
+ * collection and false for all other cases. This is the equivalent
+ * of calling not(in(collection)).
+ */
+ public static <T> Filter<T> notIn( Collection<? extends T> collection )
+ {
+ return not( in( collection ) );
+ }
+
+ /**
+ * Creates a filter that returns true for inputs that are .equals()
+ * equivalent to the specified value.
+ */
+ public static <T> Filter<T> equalTo( T value )
+ {
+ return new EqualToFilter<T>(value);
+ }
+
+ /**
+ * Creates a filter that returns true for inputs that are NOT .equals()
+ * equivalent to the specified value. This is the equivalent of calling
+ * not(equalTo(value)).
+ */
+ public static <T> Filter<T> notEqualTo( T value )
+ {
+ return not(equalTo(value));
+ }
+
+ /**
+ * Creates a filter that returns true when the specified delegate filter
+ * returns false, and vice versa.
+ */
+ public static <T> Filter<T> not( Filter<T> f )
+ {
+ return new NotFilter<T>(f);
+ }
+
+ private static class EqualToFilter<T> implements Filter<T>
+ {
+ private T value;
+
+ public EqualToFilter( T value )
+ {
+ this.value = value;
+ }
+
+ public boolean apply( T input )
+ {
+ return value == input || (value != null && value.equals(input));
+ }
+ }
+
+ private static class InFilter<T> implements Filter<T>
+ {
+ private Collection<? extends T> collection;
+
+ public InFilter( Collection<? extends T> collection )
+ {
+ this.collection = collection;
+ }
+
+ public boolean apply( T input )
+ {
+ return collection.contains(input);
+ }
+ }
+
+ private static class NotFilter<T> implements Filter<T>
+ {
+ private Filter<T> delegate;
+
+ public NotFilter( Filter<T> delegate )
+ {
+ this.delegate = delegate;
+ }
+
+ public boolean apply( T input )
+ {
+ return !delegate.apply(input);
+ }
+ }
+}
+
+
diff --git a/engine/src/networking/com/jme3/network/HostedConnection.java b/engine/src/networking/com/jme3/network/HostedConnection.java
new file mode 100644
index 0000000..b6f06cd
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/HostedConnection.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import java.util.Set;
+
+/**
+ * This is the connection back to a client that is being
+ * hosted in a server instance.
+ *
+ * @version $Revision: 7485 $
+ * @author Paul Speed
+ */
+public interface HostedConnection extends MessageConnection
+{
+ /**
+ * Returns the Server instance that is hosting this connection.
+ */
+ public Server getServer();
+
+ /**
+ * Returns the server-unique ID for this client.
+ */
+ public int getId();
+
+ /**
+ * Returns the transport specific remote address of this connection
+ * as a string. This may or may not be unique per connection depending
+ * on the type of transport. It is provided for information and filtering
+ * purposes.
+ */
+ public String getAddress();
+
+ /**
+ * Closes and removes this connection from the server
+ * sending the optional reason to the remote client.
+ */
+ public void close( String reason );
+
+ /**
+ * Sets a session attribute specific to this connection. If the value
+ * is set to null then the attribute is removed.
+ *
+ * @return The previous session value for this key or null
+ * if there was no previous value.
+ */
+ public Object setAttribute( String name, Object value );
+
+ /**
+ * Retrieves a previosly stored session attribute or
+ * null if no such attribute exists.
+ */
+ public <T> T getAttribute( String name );
+
+ /**
+ * Returns a read-only set of attribute names currently stored
+ * for this client session.
+ */
+ public Set<String> attributeNames();
+}
diff --git a/engine/src/networking/com/jme3/network/Message.java b/engine/src/networking/com/jme3/network/Message.java
new file mode 100644
index 0000000..4c3734f
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/Message.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Interface implemented by all network messages.
+ *
+ * @version $Revision: 7068 $
+ * @author Paul Speed
+ */
+@Serializable()
+public interface Message
+{
+ /**
+ * Sets this message to 'reliable' or not and returns this
+ * message.
+ */
+ public Message setReliable(boolean f);
+
+ /**
+ * Indicates which way an outgoing message should be sent
+ * or which way an incoming message was sent.
+ */
+ public boolean isReliable();
+}
diff --git a/engine/src/networking/com/jme3/network/MessageConnection.java b/engine/src/networking/com/jme3/network/MessageConnection.java
new file mode 100644
index 0000000..3a25f77
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/MessageConnection.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * The source of a received message and the common abstract interface
+ * of client->server and server->client objects.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public interface MessageConnection
+{
+ /**
+ * Sends a message to the other end of the connection.
+ */
+ public void send( Message message );
+
+ /**
+ * Sends a message to the other end of the connection using
+ * the specified alternate channel.
+ */
+ public void send( int channel, Message message );
+}
+
diff --git a/engine/src/networking/com/jme3/network/MessageListener.java b/engine/src/networking/com/jme3/network/MessageListener.java
new file mode 100644
index 0000000..36c6c59
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/MessageListener.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ * Listener notified about new messages.
+ *
+ * <p>Note about multithreading: on the server, these messages may
+ * be delivered by more than one thread depending on the server
+ * implementation used. Listener implementations should treat their
+ * shared data structures accordingly and set them up for multithreaded
+ * access. The only threading guarantee is that for a single
+ * HostedConnection, there will only ever be one thread at a time
+ * and the messages will always be delivered to that connection in the
+ * order that they were delivered.</p>
+ *
+ * @version $Revision: 7046 $
+ * @author Paul Speed
+ */
+public interface MessageListener<S>
+{
+ public void messageReceived( S source, Message m );
+}
diff --git a/engine/src/networking/com/jme3/network/Network.java b/engine/src/networking/com/jme3/network/Network.java
new file mode 100644
index 0000000..08d90d5
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/Network.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import com.jme3.network.base.DefaultClient;
+import com.jme3.network.base.DefaultServer;
+import com.jme3.network.base.TcpConnectorFactory;
+import com.jme3.network.kernel.tcp.SelectorKernel;
+import com.jme3.network.kernel.tcp.SocketConnector;
+import com.jme3.network.kernel.udp.UdpConnector;
+import com.jme3.network.kernel.udp.UdpKernel;
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * The main service provider for conveniently creating
+ * server and client instances.
+ *
+ * @version $Revision: 8979 $
+ * @author Paul Speed
+ */
+public class Network
+{
+ public static final String DEFAULT_GAME_NAME = "Unnamed jME3 Game";
+ public static final int DEFAULT_VERSION = 42;
+
+ /**
+ * Creates a Server that will utilize both reliable and fast
+ * transports to communicate with clients. The specified port
+ * will be used for both TCP and UDP communication.
+ */
+ public static Server createServer( int port ) throws IOException
+ {
+ return createServer( DEFAULT_GAME_NAME, DEFAULT_VERSION, port, port );
+ }
+
+ /**
+ * Creates a Server that will utilize both reliable and fast
+ * transports to communicate with clients. The specified port
+ * will be used for both TCP and UDP communication.
+ */
+ public static Server createServer( int tcpPort, int udpPort ) throws IOException
+ {
+ return createServer( DEFAULT_GAME_NAME, DEFAULT_VERSION, tcpPort, udpPort );
+ }
+
+ /**
+ * Creates a named and versioned Server that will utilize both reliable and fast
+ * transports to communicate with clients. The specified port
+ * will be used for both TCP and UDP communication.
+ *
+ * @param gameName This is the name that identifies the game. Connecting clients
+ * must use this name or be turned away.
+ * @param version This is a game-specific verison that helps detect when out-of-date
+ * clients have connected to an incompatible server.
+ * @param tcpPort The port upon which the TCP hosting will listen for new connections.
+ * @param udpPort The port upon which the UDP hosting will listen for new 'fast' UDP
+ * messages. Set to -1 if 'fast' traffic should go over TCP. This will
+ * completely disable UDP traffic for this server.
+ */
+ public static Server createServer( String gameName, int version, int tcpPort, int udpPort ) throws IOException
+ {
+ UdpKernel fast = udpPort == -1 ? null : new UdpKernel(udpPort);
+ SelectorKernel reliable = new SelectorKernel(tcpPort);
+
+ return new DefaultServer( gameName, version, reliable, fast );
+ }
+
+ /**
+ * Creates a client that can be connected at a later time.
+ */
+ public static NetworkClient createClient()
+ {
+ return createClient( DEFAULT_GAME_NAME, DEFAULT_VERSION );
+ }
+
+ /**
+ * Creates a client that can be connected at a later time. The specified
+ * game name and version must match the server or the client will be turned
+ * away.
+ */
+ public static NetworkClient createClient( String gameName, int version )
+ {
+ return new NetworkClientImpl(gameName, version);
+ }
+
+ /**
+ * Creates a Client that communicates with the specified host and port
+ * using both reliable and fast transports.
+ */
+ public static Client connectToServer( String host, int hostPort ) throws IOException
+ {
+ return connectToServer( DEFAULT_GAME_NAME, DEFAULT_VERSION, host, hostPort, hostPort );
+ }
+
+ /**
+ * Creates a Client that communicates with the specified host and separate TCP and UDP ports
+ * using both reliable and fast transports.
+ */
+ public static Client connectToServer( String host, int hostPort, int remoteUdpPort ) throws IOException
+ {
+ return connectToServer( DEFAULT_GAME_NAME, DEFAULT_VERSION, host, hostPort, remoteUdpPort );
+ }
+
+ /**
+ * Creates a Client that communicates with the specified host and port
+ * using both reliable and fast transports.
+ */
+ public static Client connectToServer( String gameName, int version,
+ String host, int hostPort ) throws IOException
+ {
+ return connectToServer( gameName, version, host, hostPort, hostPort );
+ }
+
+ /**
+ * Creates a Client that communicates with the specified host and and separate TCP and UDP ports
+ * using both reliable and fast transports.
+ *
+ * @param gameName This is the name that identifies the game. This must match
+ * the target server's name or this client will be turned away.
+ * @param version This is a game-specific verison that helps detect when out-of-date
+ * clients have connected to an incompatible server. This must match
+ * the server's version of this client will be turned away.
+ * @param hostPort The remote TCP port on the server to which this client should
+ * send reliable messages.
+ * @param remoteUdpPort The remote UDP port on the server to which this client should
+ * send 'fast'/unreliable messages. Set to -1 if 'fast' traffic should
+ * go over TCP. This will completely disable UDP traffic for this
+ * client.
+ */
+ public static Client connectToServer( String gameName, int version,
+ String host, int hostPort, int remoteUdpPort ) throws IOException
+ {
+ InetAddress remoteAddress = InetAddress.getByName(host);
+ UdpConnector fast = remoteUdpPort == -1 ? null : new UdpConnector( remoteAddress, remoteUdpPort );
+ SocketConnector reliable = new SocketConnector( remoteAddress, hostPort );
+
+ return new DefaultClient( gameName, version, reliable, fast, new TcpConnectorFactory(remoteAddress) );
+ }
+
+
+ protected static class NetworkClientImpl extends DefaultClient implements NetworkClient
+ {
+ public NetworkClientImpl(String gameName, int version)
+ {
+ super( gameName, version );
+ }
+
+ public void connectToServer( String host, int port, int remoteUdpPort ) throws IOException
+ {
+ connectToServer( InetAddress.getByName(host), port, remoteUdpPort );
+ }
+
+ public void connectToServer( InetAddress address, int port, int remoteUdpPort ) throws IOException
+ {
+ UdpConnector fast = new UdpConnector( address, remoteUdpPort );
+ SocketConnector reliable = new SocketConnector( address, port );
+
+ setPrimaryConnectors( reliable, fast, new TcpConnectorFactory(address) );
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/NetworkClient.java b/engine/src/networking/com/jme3/network/NetworkClient.java
new file mode 100644
index 0000000..2cd2cd7
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/NetworkClient.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * A Client whose network connection information can
+ * be provided post-creation. The actual connection stack
+ * will be setup the same as if Network.connectToServer
+ * had been called.
+ *
+ * @version $Revision: 8979 $
+ * @author Paul Speed
+ */
+public interface NetworkClient extends Client
+{
+ /**
+ * Connects this client to the specified remote server and ports.
+ */
+ public void connectToServer( String host, int port, int remoteUdpPort ) throws IOException;
+
+ /**
+ * Connects this client to the specified remote server and ports.
+ *
+ * @param address The hosts internet address.
+ * @param port The remote TCP port on the server to which this client should
+ * send reliable messages.
+ * @param remoteUdpPort The remote UDP port on the server to which this client should
+ * send 'fast'/unreliable messages. Set to -1 if 'fast' traffic should
+ * go over TCP. This will completely disable UDP traffic for this
+ * client.
+ */
+ public void connectToServer( InetAddress address, int port, int remoteUdpPort ) throws IOException;
+
+}
diff --git a/engine/src/networking/com/jme3/network/Server.java b/engine/src/networking/com/jme3/network/Server.java
new file mode 100644
index 0000000..850cf29
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/Server.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import java.util.Collection;
+
+/**
+ * Represents a host that can send and receive messages to
+ * a set of remote client connections.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public interface Server
+{
+ /**
+ * Returns the 'game name' for this server. This should match the
+ * 'game name' set on connecting clients or they will be turned away.
+ */
+ public String getGameName();
+
+ /**
+ * Returns the game-specific version of this server used for detecting
+ * mismatched clients.
+ */
+ public int getVersion();
+
+ /**
+ * Sends the specified message to all connected clients.
+ */
+ public void broadcast( Message message );
+
+ /**
+ * Sends the specified message to all connected clients that match
+ * the filter. If no filter is specified then this is the same as
+ * calling broadcast(message) and the message will be delivered to
+ * all connections.
+ * <p>Examples:</p>
+ * <pre>
+ * // Broadcast to connections: conn1, conn2, and conn3
+ * server.broadcast( Filters.in( conn1, conn2, conn3 ), message );
+ *
+ * // Broadcast to all connections exception source
+ * server.broadcast( Filters.notEqualTo( source ), message );
+ * </pre>
+ */
+ public void broadcast( Filter<? super HostedConnection> filter, Message message );
+
+ /**
+ * Sends the specified message over the specified alternate channel to all connected
+ * clients that match the filter. If no filter is specified then this is the same as
+ * calling broadcast(message) and the message will be delivered to
+ * all connections.
+ * <p>Examples:</p>
+ * <pre>
+ * // Broadcast to connections: conn1, conn2, and conn3
+ * server.broadcast( Filters.in( conn1, conn2, conn3 ), message );
+ *
+ * // Broadcast to all connections exception source
+ * server.broadcast( Filters.notEqualTo( source ), message );
+ * </pre>
+ */
+ public void broadcast( int channel, Filter<? super HostedConnection> filter, Message message );
+
+ /**
+ * Start the server so that it will began accepting new connections
+ * and processing messages.
+ */
+ public void start();
+
+ /**
+ * Adds an alternate channel to the server, using the specified port. This is an
+ * entirely separate connection where messages are sent and received in parallel
+ * to the two primary default channels. All channels must be added before the connection
+ * is started.
+ * Returns the ID of the created channel for use when specifying the channel in send or
+ * broadcast calls. The ID is returned entirely out of convenience since the IDs
+ * are predictably incremented. The first channel is 0, second is 1, and so on.
+ */
+ public int addChannel( int port );
+
+ /**
+ * Returns true if the server has been started.
+ */
+ public boolean isRunning();
+
+ /**
+ * Closes all client connections, stops and running processing threads, and
+ * closes the host connection.
+ */
+ public void close();
+
+ /**
+ * Retrieves a hosted connection by ID.
+ */
+ public HostedConnection getConnection( int id );
+
+ /**
+ * Retrieves a read-only collection of all currently connected connections.
+ */
+ public Collection<HostedConnection> getConnections();
+
+ /**
+ * Returns true if the server has active connections at the time of this
+ * call.
+ */
+ public boolean hasConnections();
+
+ /**
+ * Adds a listener that will be notified when new hosted connections
+ * arrive.
+ */
+ public void addConnectionListener( ConnectionListener listener );
+
+ /**
+ * Removes a previously registered connection listener.
+ */
+ public void removeConnectionListener( ConnectionListener listener );
+
+ /**
+ * Adds a listener that will be notified when any message or object
+ * is received from one of the clients.
+ *
+ * <p>Note about MessageListener multithreading: on the server, message events may
+ * be delivered by more than one thread depending on the server
+ * implementation used. Listener implementations should treat their
+ * shared data structures accordingly and set them up for multithreaded
+ * access. The only threading guarantee is that for a single
+ * HostedConnection, there will only ever be one thread at a time
+ * and the messages will always be delivered to that connection in the
+ * order that they were delivered. This is the only restriction placed
+ * upon server message dispatch pool implementations.</p>
+ */
+ public void addMessageListener( MessageListener<? super HostedConnection> listener );
+
+ /**
+ * Adds a listener that will be notified when messages of the specified
+ * types are received from one of the clients.
+ */
+ public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes );
+
+ /**
+ * Removes a previously registered wildcard listener. This does
+ * not remove this listener from any type-specific registrations.
+ */
+ public void removeMessageListener( MessageListener<? super HostedConnection> listener );
+
+ /**
+ * Removes a previously registered type-specific listener from
+ * the specified types.
+ */
+ public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes );
+
+
+}
+
diff --git a/engine/src/networking/com/jme3/network/base/ConnectorAdapter.java b/engine/src/networking/com/jme3/network/base/ConnectorAdapter.java
new file mode 100644
index 0000000..3827786
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/ConnectorAdapter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.ErrorListener;
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.kernel.ConnectorException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Wraps a single Connector and forwards new messages
+ * to the supplied message dispatcher. This is used
+ * by DefaultClient to manage its connector objects.
+ * This is only responsible for message reading and provides
+ * no support for buffering writes.
+ *
+ * <p>This adapter assumes a simple protocol where two
+ * bytes define a (short) object size with the object data
+ * to follow. Note: this limits the size of serialized
+ * objects to 32676 bytes... even though, for example,
+ * datagram packets can hold twice that. :P</p>
+ *
+ * @version $Revision: 8944 $
+ * @author Paul Speed
+ */
+public class ConnectorAdapter extends Thread
+{
+ private static final int OUTBOUND_BACKLOG = 16000;
+
+ private Connector connector;
+ private MessageListener<Object> dispatcher;
+ private ErrorListener<Object> errorHandler;
+ private AtomicBoolean go = new AtomicBoolean(true);
+
+ private BlockingQueue<ByteBuffer> outbound;
+
+ // Writes messages out on a background thread
+ private WriterThread writer;
+
+ // Marks the messages as reliable or not if they came
+ // through this connector.
+ private boolean reliable;
+
+ public ConnectorAdapter( Connector connector, MessageListener<Object> dispatcher,
+ ErrorListener<Object> errorHandler, boolean reliable )
+ {
+ super( String.valueOf(connector) );
+ this.connector = connector;
+ this.dispatcher = dispatcher;
+ this.errorHandler = errorHandler;
+ this.reliable = reliable;
+ setDaemon(true);
+
+ // The backlog makes sure that the outbound channel blocks once
+ // a certain backlog level is reached. It is set high so that it
+ // is only reached in the worst cases... which are usually things like
+ // raw throughput tests. Technically, a saturated TCP channel could
+ // back up quite a bit if the buffers are full and the socket has
+ // stalled but 16,000 messages is still a big backlog.
+ outbound = new ArrayBlockingQueue<ByteBuffer>(OUTBOUND_BACKLOG);
+
+ // Note: this technically adds a potential deadlock case
+ // with the above code where there wasn't one before. For example,
+ // if a TCP outbound queue fills to capacity and a client sends
+ // in such a way that they block TCP message handling then if the HostedConnection
+ // on the server is similarly blocked then the TCP network buffers may
+ // all get full and no outbound messages move and we forever block
+ // on the queue.
+ // However, in practice this can't really happen... or at least it's
+ // the sign of other really bad things.
+ // First, currently the server-side outbound queues are all unbounded and
+ // so won't ever block the handling of messages if the outbound channel is full.
+ // Second, there would have to be a huge amount of data backlog for this
+ // to ever occur anyway.
+ // Third, it's a sign of a really poor architecture if 16,000 messages
+ // can go out in a way that blocks reads.
+
+ writer = new WriterThread();
+ writer.start();
+ }
+
+ public void close()
+ {
+ go.set(false);
+
+ // Kill the writer service
+ writer.shutdown();
+
+ if( connector.isConnected() )
+ {
+ // Kill the connector
+ connector.close();
+ }
+ }
+
+ protected void dispatch( Message m )
+ {
+ dispatcher.messageReceived( null, m );
+ }
+
+ public void write( ByteBuffer data )
+ {
+ try {
+ outbound.put( data );
+ } catch( InterruptedException e ) {
+ throw new RuntimeException( "Interrupted while waiting for queue to drain", e );
+ }
+ }
+
+ protected void handleError( Exception e )
+ {
+ if( !go.get() )
+ return;
+
+ errorHandler.handleError( this, e );
+ }
+
+ public void run()
+ {
+ MessageProtocol protocol = new MessageProtocol();
+
+ try {
+ while( go.get() ) {
+ ByteBuffer buffer = connector.read();
+ if( buffer == null ) {
+ if( go.get() ) {
+ throw new ConnectorException( "Connector closed." );
+ } else {
+ // Just dump out because a null buffer is expected
+ // from a closed/closing connector
+ break;
+ }
+ }
+
+ protocol.addBuffer( buffer );
+
+ Message m = null;
+ while( (m = protocol.getMessage()) != null ) {
+ m.setReliable( reliable );
+ dispatch( m );
+ }
+ }
+ } catch( Exception e ) {
+ handleError( e );
+ }
+ }
+
+ protected class WriterThread extends Thread
+ {
+ public WriterThread()
+ {
+ super( String.valueOf(connector) + "-writer" );
+ }
+
+ public void shutdown()
+ {
+ interrupt();
+ }
+
+ private void write( ByteBuffer data )
+ {
+ try {
+ connector.write(data);
+ } catch( Exception e ) {
+ handleError( e );
+ }
+ }
+
+ public void run()
+ {
+ while( go.get() ) {
+ try {
+ ByteBuffer data = outbound.take();
+ write(data);
+ } catch( InterruptedException e ) {
+ if( !go.get() )
+ return;
+ throw new RuntimeException( "Interrupted waiting for data", e );
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/base/ConnectorFactory.java b/engine/src/networking/com/jme3/network/base/ConnectorFactory.java
new file mode 100644
index 0000000..d9bb700
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/ConnectorFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.kernel.Connector;
+import java.io.IOException;
+
+
+/**
+ * Creates Connectors for a specific host.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public interface ConnectorFactory
+{
+ public Connector createConnector( int channel, int port ) throws IOException;
+}
diff --git a/engine/src/networking/com/jme3/network/base/DefaultClient.java b/engine/src/networking/com/jme3/network/base/DefaultClient.java
new file mode 100644
index 0000000..e03c3e4
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/DefaultClient.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.ClientStateListener.DisconnectInfo;
+import com.jme3.network.*;
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.message.ChannelInfoMessage;
+import com.jme3.network.message.ClientRegistrationMessage;
+import com.jme3.network.message.DisconnectMessage;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A default implementation of the Client interface that delegates
+ * its network connectivity to a kernel.Connector.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public class DefaultClient implements Client
+{
+ static Logger log = Logger.getLogger(DefaultClient.class.getName());
+
+ // First two channels are reserved for reliable and
+ // unreliable. Note: channels are endpoint specific so these
+ // constants and the handling need not have anything to do with
+ // the same constants in DefaultServer... which is why they are
+ // separate.
+ private static final int CH_RELIABLE = 0;
+ private static final int CH_UNRELIABLE = 1;
+ private static final int CH_FIRST = 2;
+
+ private ThreadLocal<ByteBuffer> dataBuffer = new ThreadLocal<ByteBuffer>();
+
+ private int id = -1;
+ private boolean isRunning = false;
+ private CountDownLatch connecting = new CountDownLatch(1);
+ private String gameName;
+ private int version;
+ private MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
+ private List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
+ private List<ErrorListener<? super Client>> errorListeners = new CopyOnWriteArrayList<ErrorListener<? super Client>>();
+ private Redispatch dispatcher = new Redispatch();
+ private List<ConnectorAdapter> channels = new ArrayList<ConnectorAdapter>();
+
+ private ConnectorFactory connectorFactory;
+
+ public DefaultClient( String gameName, int version )
+ {
+ this.gameName = gameName;
+ this.version = version;
+ }
+
+ public DefaultClient( String gameName, int version, Connector reliable, Connector fast,
+ ConnectorFactory connectorFactory )
+ {
+ this( gameName, version );
+ setPrimaryConnectors( reliable, fast, connectorFactory );
+ }
+
+ protected void setPrimaryConnectors( Connector reliable, Connector fast, ConnectorFactory connectorFactory )
+ {
+ if( reliable == null )
+ throw new IllegalArgumentException( "The reliable connector cannot be null." );
+ if( isRunning )
+ throw new IllegalStateException( "Client is already started." );
+ if( !channels.isEmpty() )
+ throw new IllegalStateException( "Channels already exist." );
+
+ this.connectorFactory = connectorFactory;
+ channels.add(new ConnectorAdapter(reliable, dispatcher, dispatcher, true));
+ if( fast != null ) {
+ channels.add(new ConnectorAdapter(fast, dispatcher, dispatcher, false));
+ } else {
+ // Add the null adapter to keep the indexes right
+ channels.add(null);
+ }
+ }
+
+ protected void checkRunning()
+ {
+ if( !isRunning )
+ throw new IllegalStateException( "Client is not started." );
+ }
+
+ public void start()
+ {
+ if( isRunning )
+ throw new IllegalStateException( "Client is already started." );
+
+ // Start up the threads and stuff for the
+ // connectors that we have
+ for( ConnectorAdapter ca : channels ) {
+ if( ca == null )
+ continue;
+ ca.start();
+ }
+
+ // Send our connection message with a generated ID until
+ // we get one back from the server. We'll hash time in
+ // millis and time in nanos.
+ // This is used to match the TCP and UDP endpoints up on the
+ // other end since they may take different routes to get there.
+ // Behind NAT, many game clients may be coming over the same
+ // IP address from the server's perspective and they may have
+ // their UDP ports mapped all over the place.
+ //
+ // Since currentTimeMillis() is absolute time and nano time
+ // is roughtly related to system start time, adding these two
+ // together should be plenty unique for our purposes. It wouldn't
+ // hurt to reconcile with IP on the server side, though.
+ long tempId = System.currentTimeMillis() + System.nanoTime();
+
+ // Set it true here so we can send some messages.
+ isRunning = true;
+
+ ClientRegistrationMessage reg;
+ reg = new ClientRegistrationMessage();
+ reg.setId(tempId);
+ reg.setGameName(getGameName());
+ reg.setVersion(getVersion());
+ reg.setReliable(true);
+ send(CH_RELIABLE, reg, false);
+
+ // Send registration messages to any other configured
+ // connectors
+ reg = new ClientRegistrationMessage();
+ reg.setId(tempId);
+ reg.setReliable(false);
+ for( int ch = CH_UNRELIABLE; ch < channels.size(); ch++ ) {
+ if( channels.get(ch) == null )
+ continue;
+ send(ch, reg, false);
+ }
+ }
+
+ protected void waitForConnected()
+ {
+ if( isConnected() )
+ return;
+
+ try {
+ connecting.await();
+ } catch( InterruptedException e ) {
+ throw new RuntimeException( "Interrupted waiting for connect", e );
+ }
+ }
+
+ public boolean isConnected()
+ {
+ return id != -1 && isRunning;
+ }
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public String getGameName()
+ {
+ return gameName;
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public void send( Message message )
+ {
+ if( message.isReliable() || channels.get(CH_UNRELIABLE) == null ) {
+ send(CH_RELIABLE, message, true);
+ } else {
+ send(CH_UNRELIABLE, message, true);
+ }
+ }
+
+ public void send( int channel, Message message )
+ {
+ if( channel < 0 || channel + CH_FIRST >= channels.size() )
+ throw new IllegalArgumentException( "Channel is undefined:" + channel );
+ send( channel + CH_FIRST, message, true );
+ }
+
+ protected void send( int channel, Message message, boolean waitForConnected )
+ {
+ checkRunning();
+
+ if( waitForConnected ) {
+ // Make sure we aren't still connecting
+ waitForConnected();
+ }
+
+ ByteBuffer buffer = dataBuffer.get();
+ if( buffer == null ) {
+ buffer = ByteBuffer.allocate( 65536 + 2 );
+ dataBuffer.set(buffer);
+ }
+ buffer.clear();
+
+ // Convert the message to bytes
+ buffer = MessageProtocol.messageToBuffer(message, buffer);
+
+ // Since we share the buffer between invocations, we will need to
+ // copy this message's part out of it. This is because we actually
+ // do the send on a background thread.
+ byte[] temp = new byte[buffer.remaining()];
+ System.arraycopy(buffer.array(), buffer.position(), temp, 0, buffer.remaining());
+ buffer = ByteBuffer.wrap(temp);
+
+ channels.get(channel).write(buffer);
+ }
+
+ public void close()
+ {
+ checkRunning();
+
+ closeConnections( null );
+ }
+
+ protected void closeConnections( DisconnectInfo info )
+ {
+ if( !isRunning )
+ return;
+
+ // Send a close message
+
+ // Tell the thread it's ok to die
+ for( ConnectorAdapter ca : channels ) {
+ if( ca == null )
+ continue;
+ ca.close();
+ }
+
+ // Wait for the threads?
+
+ // Just in case we never fully connected
+ connecting.countDown();
+
+ fireDisconnected(info);
+
+ isRunning = false;
+ }
+
+ public void addClientStateListener( ClientStateListener listener )
+ {
+ stateListeners.add( listener );
+ }
+
+ public void removeClientStateListener( ClientStateListener listener )
+ {
+ stateListeners.remove( listener );
+ }
+
+ public void addMessageListener( MessageListener<? super Client> listener )
+ {
+ messageListeners.addMessageListener( listener );
+ }
+
+ public void addMessageListener( MessageListener<? super Client> listener, Class... classes )
+ {
+ messageListeners.addMessageListener( listener, classes );
+ }
+
+ public void removeMessageListener( MessageListener<? super Client> listener )
+ {
+ messageListeners.removeMessageListener( listener );
+ }
+
+ public void removeMessageListener( MessageListener<? super Client> listener, Class... classes )
+ {
+ messageListeners.removeMessageListener( listener, classes );
+ }
+
+ public void addErrorListener( ErrorListener<? super Client> listener )
+ {
+ errorListeners.add( listener );
+ }
+
+ public void removeErrorListener( ErrorListener<? super Client> listener )
+ {
+ errorListeners.remove( listener );
+ }
+
+ protected void fireConnected()
+ {
+ for( ClientStateListener l : stateListeners ) {
+ l.clientConnected( this );
+ }
+ }
+
+ protected void fireDisconnected( DisconnectInfo info )
+ {
+ for( ClientStateListener l : stateListeners ) {
+ l.clientDisconnected( this, info );
+ }
+ }
+
+ /**
+ * Either calls the ErrorListener or closes the connection
+ * if there are no listeners.
+ */
+ protected void handleError( Throwable t )
+ {
+ // If there are no listeners then close the connection with
+ // a reason
+ if( errorListeners.isEmpty() ) {
+ log.log( Level.SEVERE, "Termining connection due to unhandled error", t );
+ DisconnectInfo info = new DisconnectInfo();
+ info.reason = "Connection Error";
+ info.error = t;
+ closeConnections(info);
+ return;
+ }
+
+ for( ErrorListener l : errorListeners ) {
+ l.handleError( this, t );
+ }
+ }
+
+ protected void configureChannels( long tempId, int[] ports ) {
+
+ try {
+ for( int i = 0; i < ports.length; i++ ) {
+ Connector c = connectorFactory.createConnector( i, ports[i] );
+ ConnectorAdapter ca = new ConnectorAdapter(c, dispatcher, dispatcher, true);
+ int ch = channels.size();
+ channels.add( ca );
+
+ // Need to send the connection its hook-up registration
+ // and start it.
+ ca.start();
+ ClientRegistrationMessage reg;
+ reg = new ClientRegistrationMessage();
+ reg.setId(tempId);
+ reg.setReliable(true);
+ send( ch, reg, false );
+ }
+ } catch( IOException e ) {
+ throw new RuntimeException( "Error configuring channels", e );
+ }
+ }
+
+ protected void dispatch( Message m )
+ {
+ // Pull off the connection management messages we're
+ // interested in and then pass on the rest.
+ if( m instanceof ClientRegistrationMessage ) {
+ // Then we've gotten our real id
+ this.id = (int)((ClientRegistrationMessage)m).getId();
+ log.log( Level.INFO, "Connection established, id:{0}.", this.id );
+ connecting.countDown();
+ fireConnected();
+ return;
+ } else if( m instanceof ChannelInfoMessage ) {
+ // This is an interum step in the connection process and
+ // now we need to add a bunch of connections
+ configureChannels( ((ChannelInfoMessage)m).getId(), ((ChannelInfoMessage)m).getPorts() );
+ return;
+ } else if( m instanceof DisconnectMessage ) {
+ // Can't do too much else yet
+ String reason = ((DisconnectMessage)m).getReason();
+ log.log( Level.SEVERE, "Connection terminated, reason:{0}.", reason );
+ DisconnectInfo info = new DisconnectInfo();
+ info.reason = reason;
+ closeConnections(info);
+ }
+
+ // Make sure client MessageListeners are called single-threaded
+ // since it could receive messages from the TCP and UDP
+ // thread simultaneously.
+ synchronized( this ) {
+ messageListeners.messageReceived( this, m );
+ }
+ }
+
+ protected class Redispatch implements MessageListener<Object>, ErrorListener<Object>
+ {
+ public void messageReceived( Object source, Message m )
+ {
+ dispatch( m );
+ }
+
+ public void handleError( Object source, Throwable t )
+ {
+ // Only doing the DefaultClient.this to make the code
+ // checker happy... it compiles fine without it but I
+ // don't like red lines in my editor. :P
+ DefaultClient.this.handleError( t );
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/base/DefaultServer.java b/engine/src/networking/com/jme3/network/base/DefaultServer.java
new file mode 100644
index 0000000..28c4c80
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/DefaultServer.java
@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.*;
+import com.jme3.network.kernel.Endpoint;
+import com.jme3.network.kernel.Kernel;
+import com.jme3.network.message.ChannelInfoMessage;
+import com.jme3.network.message.ClientRegistrationMessage;
+import com.jme3.network.message.DisconnectMessage;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A default implementation of the Server interface that delegates
+ * its network connectivity to kernel.Kernel.
+ *
+ * @version $Revision: 9114 $
+ * @author Paul Speed
+ */
+public class DefaultServer implements Server
+{
+ static Logger log = Logger.getLogger(DefaultServer.class.getName());
+
+ // First two channels are reserved for reliable and
+ // unreliable
+ private static final int CH_RELIABLE = 0;
+ private static final int CH_UNRELIABLE = 1;
+ private static final int CH_FIRST = 2;
+
+ private boolean isRunning = false;
+ private AtomicInteger nextId = new AtomicInteger(0);
+ private String gameName;
+ private int version;
+ private KernelFactory kernelFactory = KernelFactory.DEFAULT;
+ private KernelAdapter reliableAdapter;
+ private KernelAdapter fastAdapter;
+ private List<KernelAdapter> channels = new ArrayList<KernelAdapter>();
+ private List<Integer> alternatePorts = new ArrayList<Integer>();
+ private Redispatch dispatcher = new Redispatch();
+ private Map<Integer,HostedConnection> connections = new ConcurrentHashMap<Integer,HostedConnection>();
+ private Map<Endpoint,HostedConnection> endpointConnections
+ = new ConcurrentHashMap<Endpoint,HostedConnection>();
+
+ // Keeps track of clients for whom we've only received the UDP
+ // registration message
+ private Map<Long,Connection> connecting = new ConcurrentHashMap<Long,Connection>();
+
+ private MessageListenerRegistry<HostedConnection> messageListeners
+ = new MessageListenerRegistry<HostedConnection>();
+ private List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>();
+
+ public DefaultServer( String gameName, int version, Kernel reliable, Kernel fast )
+ {
+ if( reliable == null )
+ throw new IllegalArgumentException( "Default server reqiures a reliable kernel instance." );
+
+ this.gameName = gameName;
+ this.version = version;
+
+ reliableAdapter = new KernelAdapter( this, reliable, dispatcher, true );
+ channels.add( reliableAdapter );
+ if( fast != null ) {
+ fastAdapter = new KernelAdapter( this, fast, dispatcher, false );
+ channels.add( fastAdapter );
+ }
+ }
+
+ public String getGameName()
+ {
+ return gameName;
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public int addChannel( int port )
+ {
+ if( isRunning )
+ throw new IllegalStateException( "Channels cannot be added once server is started." );
+
+ // Note: it does bug me that channels aren't 100% universal and
+ // setup externally but it requires a more invasive set of changes
+ // for "connection types" and some kind of registry of kernel and
+ // connector factories. This really would be the best approach and
+ // would allow all kinds of channel customization maybe... but for
+ // now, we hard-code the standard connections and treat the +2 extras
+ // differently.
+
+ // Check for consistency with the channels list
+ if( channels.size() - CH_FIRST != alternatePorts.size() )
+ throw new IllegalStateException( "Channel and port lists do not match." );
+
+ try {
+ int result = alternatePorts.size();
+ alternatePorts.add(port);
+
+ Kernel kernel = kernelFactory.createKernel(result, port);
+ channels.add( new KernelAdapter(this, kernel, dispatcher, true) );
+
+ return result;
+ } catch( IOException e ) {
+ throw new RuntimeException( "Error adding channel for port:" + port, e );
+ }
+ }
+
+ protected void checkChannel( int channel )
+ {
+ if( channel < 0 || channel >= alternatePorts.size() )
+ throw new IllegalArgumentException( "Channel is undefined:" + channel );
+ }
+
+ public void start()
+ {
+ if( isRunning )
+ throw new IllegalStateException( "Server is already started." );
+
+ // Initialize the kernels
+ for( KernelAdapter ka : channels ) {
+ ka.initialize();
+ }
+
+ // Start em up
+ for( KernelAdapter ka : channels ) {
+ ka.start();
+ }
+
+ isRunning = true;
+ }
+
+ public boolean isRunning()
+ {
+ return isRunning;
+ }
+
+ public void close()
+ {
+ if( !isRunning )
+ throw new IllegalStateException( "Server is not started." );
+
+ try {
+ // Kill the adpaters, they will kill the kernels
+ for( KernelAdapter ka : channels ) {
+ ka.close();
+ }
+
+ isRunning = false;
+ } catch( InterruptedException e ) {
+ throw new RuntimeException( "Interrupted while closing", e );
+ }
+ }
+
+ public void broadcast( Message message )
+ {
+ broadcast( null, message );
+ }
+
+ public void broadcast( Filter<? super HostedConnection> filter, Message message )
+ {
+ if( connections.isEmpty() )
+ return;
+
+ ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
+
+ FilterAdapter adapter = filter == null ? null : new FilterAdapter(filter);
+
+ if( message.isReliable() || fastAdapter == null ) {
+ // Don't need to copy the data because message protocol is already
+ // giving us a fresh buffer
+ reliableAdapter.broadcast( adapter, buffer, true, false );
+ } else {
+ fastAdapter.broadcast( adapter, buffer, false, false );
+ }
+ }
+
+ public void broadcast( int channel, Filter<? super HostedConnection> filter, Message message )
+ {
+ if( connections.isEmpty() )
+ return;
+
+ checkChannel(channel);
+
+ ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
+
+ FilterAdapter adapter = filter == null ? null : new FilterAdapter(filter);
+
+ channels.get(channel+CH_FIRST).broadcast( adapter, buffer, true, false );
+ }
+
+ public HostedConnection getConnection( int id )
+ {
+ return connections.get(id);
+ }
+
+ public boolean hasConnections()
+ {
+ return !connections.isEmpty();
+ }
+
+ public Collection<HostedConnection> getConnections()
+ {
+ return Collections.unmodifiableCollection((Collection<HostedConnection>)connections.values());
+ }
+
+ public void addConnectionListener( ConnectionListener listener )
+ {
+ connectionListeners.add(listener);
+ }
+
+ public void removeConnectionListener( ConnectionListener listener )
+ {
+ connectionListeners.remove(listener);
+ }
+
+ public void addMessageListener( MessageListener<? super HostedConnection> listener )
+ {
+ messageListeners.addMessageListener( listener );
+ }
+
+ public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
+ {
+ messageListeners.addMessageListener( listener, classes );
+ }
+
+ public void removeMessageListener( MessageListener<? super HostedConnection> listener )
+ {
+ messageListeners.removeMessageListener( listener );
+ }
+
+ public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
+ {
+ messageListeners.removeMessageListener( listener, classes );
+ }
+
+ protected void dispatch( HostedConnection source, Message m )
+ {
+ if( source == null ) {
+ messageListeners.messageReceived( source, m );
+ } else {
+
+ // A semi-heavy handed way to make sure the listener
+ // doesn't get called at the same time from two different
+ // threads for the same hosted connection.
+ synchronized( source ) {
+ messageListeners.messageReceived( source, m );
+ }
+ }
+ }
+
+ protected void fireConnectionAdded( HostedConnection conn )
+ {
+ for( ConnectionListener l : connectionListeners ) {
+ l.connectionAdded( this, conn );
+ }
+ }
+
+ protected void fireConnectionRemoved( HostedConnection conn )
+ {
+ for( ConnectionListener l : connectionListeners ) {
+ l.connectionRemoved( this, conn );
+ }
+ }
+
+ protected int getChannel( KernelAdapter ka )
+ {
+ return channels.indexOf(ka);
+ }
+
+ protected void registerClient( KernelAdapter ka, Endpoint p, ClientRegistrationMessage m )
+ {
+ Connection addedConnection = null;
+
+ // generally this will only be called by one thread but it's
+ // important enough I won't take chances
+ synchronized( this ) {
+ // Grab the random ID that the client created when creating
+ // its two registration messages
+ long tempId = m.getId();
+
+ // See if we already have one
+ Connection c = connecting.remove(tempId);
+ if( c == null ) {
+ c = new Connection(channels.size());
+ log.log( Level.FINE, "Registering client for endpoint, pass 1:{0}.", p );
+ } else {
+ log.log( Level.FINE, "Refining client registration for endpoint:{0}.", p );
+ }
+
+ // Fill in what we now know
+ int channel = getChannel(ka);
+ c.setChannel(channel, p);
+ log.log( Level.FINE, "Setting up channel:{0}", channel );
+
+ // If it's channel 0 then this is the initial connection
+ // and we will send the connection information
+ if( channel == CH_RELIABLE ) {
+ // Validate the name and version which is only sent
+ // over the reliable connection at this point.
+ if( !getGameName().equals(m.getGameName())
+ || getVersion() != m.getVersion() ) {
+
+ log.log( Level.INFO, "Kicking client due to name/version mismatch:{0}.", c );
+
+ // Need to kick them off... I may regret doing this from within
+ // the sync block but the alternative is more code
+ c.close( "Server client mismatch, server:" + getGameName() + " v" + getVersion()
+ + " client:" + m.getGameName() + " v" + m.getVersion() );
+ return;
+ }
+
+ // Else send the extra channel information to the client
+ if( !alternatePorts.isEmpty() ) {
+ ChannelInfoMessage cim = new ChannelInfoMessage( m.getId(), alternatePorts );
+ c.send(cim);
+ }
+ }
+
+ if( c.isComplete() ) {
+ // Then we are fully connected
+ if( connections.put( c.getId(), c ) == null ) {
+
+ for( Endpoint cp : c.channels ) {
+ if( cp == null )
+ continue;
+ endpointConnections.put( cp, c );
+ }
+
+ addedConnection = c;
+ }
+ } else {
+ // Need to keep getting channels so we'll keep it in
+ // the map
+ connecting.put(tempId, c);
+ }
+ }
+
+ // Best to do this outside of the synch block to avoid
+ // over synchronizing which is the path to deadlocks
+ if( addedConnection != null ) {
+ log.log( Level.INFO, "Client registered:{0}.", addedConnection );
+
+ // Send the ID back to the client letting it know it's
+ // fully connected.
+ m = new ClientRegistrationMessage();
+ m.setId( addedConnection.getId() );
+ m.setReliable(true);
+ addedConnection.send(m);
+
+ // Now we can notify the listeners about the
+ // new connection.
+ fireConnectionAdded( addedConnection );
+ }
+ }
+
+ protected HostedConnection getConnection( Endpoint endpoint )
+ {
+ return endpointConnections.get(endpoint);
+ }
+
+ protected void connectionClosed( Endpoint p )
+ {
+ if( p.isConnected() ) {
+ log.log( Level.INFO, "Connection closed:{0}.", p );
+ } else {
+ log.log( Level.FINE, "Connection closed:{0}.", p );
+ }
+
+ // Try to find the endpoint in all ways that it might
+ // exist. Note: by this point the raw network channel is
+ // closed already.
+
+ // Also note: this method will be called multiple times per
+ // HostedConnection if it has multiple endpoints.
+
+ Connection removed = null;
+ synchronized( this ) {
+ // Just in case the endpoint was still connecting
+ connecting.values().remove(p);
+
+ // And the regular management
+ removed = (Connection)endpointConnections.remove(p);
+ if( removed != null ) {
+ connections.remove( removed.getId() );
+ }
+
+ log.log( Level.FINE, "Connections size:{0}", connections.size() );
+ log.log( Level.FINE, "Endpoint mappings size:{0}", endpointConnections.size() );
+ }
+
+ // Better not to fire events while we hold a lock
+ // so always do this outside the synch block.
+ // Note: checking removed.closed just to avoid spurious log messages
+ // since in general we are called back for every endpoint closing.
+ if( removed != null && !removed.closed ) {
+
+ log.log( Level.INFO, "Client closed:{0}.", removed );
+
+ removed.closeConnection();
+ }
+ }
+
+ protected class Connection implements HostedConnection
+ {
+ private int id;
+ private boolean closed;
+ private Endpoint[] channels;
+ private int setChannelCount = 0;
+
+ private Map<String,Object> sessionData = new ConcurrentHashMap<String,Object>();
+
+ public Connection( int channelCount )
+ {
+ id = nextId.getAndIncrement();
+ channels = new Endpoint[channelCount];
+ }
+
+ void setChannel( int channel, Endpoint p )
+ {
+ if( channels[channel] != null && channels[channel] != p ) {
+ throw new RuntimeException( "Channel has already been set:" + channel
+ + " = " + channels[channel] + ", cannot be set to:" + p );
+ }
+ channels[channel] = p;
+ if( p != null )
+ setChannelCount++;
+ }
+
+ boolean isComplete()
+ {
+ return setChannelCount == channels.length;
+ }
+
+ public Server getServer()
+ {
+ return DefaultServer.this;
+ }
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public String getAddress()
+ {
+ return channels[CH_RELIABLE] == null ? null : channels[CH_RELIABLE].getAddress();
+ }
+
+ public void send( Message message )
+ {
+ ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
+ if( message.isReliable() || channels[CH_UNRELIABLE] == null ) {
+ channels[CH_RELIABLE].send( buffer );
+ } else {
+ channels[CH_UNRELIABLE].send( buffer );
+ }
+ }
+
+ public void send( int channel, Message message )
+ {
+ checkChannel(channel);
+ ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
+ channels[channel+CH_FIRST].send(buffer);
+ }
+
+ protected void closeConnection()
+ {
+ if( closed )
+ return;
+ closed = true;
+
+ // Make sure all endpoints are closed. Note: reliable
+ // should always already be closed through all paths that I
+ // can conceive... but it doesn't hurt to be sure.
+ for( Endpoint p : channels ) {
+ if( p == null )
+ continue;
+ p.close();
+ }
+
+ fireConnectionRemoved( this );
+ }
+
+ public void close( String reason )
+ {
+ // Send a reason
+ DisconnectMessage m = new DisconnectMessage();
+ m.setType( DisconnectMessage.KICK );
+ m.setReason( reason );
+ m.setReliable( true );
+ send( m );
+
+ // Just close the reliable endpoint
+ // fast will be cleaned up as a side-effect
+ // when closeConnection() is called by the
+ // connectionClosed() endpoint callback.
+ if( channels[CH_RELIABLE] != null ) {
+ // Close with flush so we make sure our
+ // message gets out
+ channels[CH_RELIABLE].close(true);
+ }
+ }
+
+ public Object setAttribute( String name, Object value )
+ {
+ if( value == null )
+ return sessionData.remove(name);
+ return sessionData.put(name, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getAttribute( String name )
+ {
+ return (T)sessionData.get(name);
+ }
+
+ public Set<String> attributeNames()
+ {
+ return Collections.unmodifiableSet(sessionData.keySet());
+ }
+
+ public String toString()
+ {
+ return "Connection[ id=" + id + ", reliable=" + channels[CH_RELIABLE]
+ + ", fast=" + channels[CH_UNRELIABLE] + " ]";
+ }
+ }
+
+ protected class Redispatch implements MessageListener<HostedConnection>
+ {
+ public void messageReceived( HostedConnection source, Message m )
+ {
+ dispatch( source, m );
+ }
+ }
+
+ protected class FilterAdapter implements Filter<Endpoint>
+ {
+ private Filter<? super HostedConnection> delegate;
+
+ public FilterAdapter( Filter<? super HostedConnection> delegate )
+ {
+ this.delegate = delegate;
+ }
+
+ public boolean apply( Endpoint input )
+ {
+ HostedConnection conn = getConnection( input );
+ if( conn == null )
+ return false;
+ return delegate.apply(conn);
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/base/KernelAdapter.java b/engine/src/networking/com/jme3/network/base/KernelAdapter.java
new file mode 100644
index 0000000..8477aad
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/KernelAdapter.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.Filter;
+import com.jme3.network.HostedConnection;
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+import com.jme3.network.kernel.Endpoint;
+import com.jme3.network.kernel.EndpointEvent;
+import com.jme3.network.kernel.Envelope;
+import com.jme3.network.kernel.Kernel;
+import com.jme3.network.message.ClientRegistrationMessage;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Wraps a single Kernel and forwards new messages
+ * to the supplied message dispatcher and new endpoint
+ * events to the connection dispatcher. This is used
+ * by DefaultServer to manage its kernel objects.
+ *
+ * <p>This adapter assumes a simple protocol where two
+ * bytes define a (short) object size with the object data
+ * to follow. Note: this limits the size of serialized
+ * objects to 32676 bytes... even though, for example,
+ * datagram packets can hold twice that. :P</p>
+ *
+ * @version $Revision: 8944 $
+ * @author Paul Speed
+ */
+public class KernelAdapter extends Thread
+{
+ static Logger log = Logger.getLogger(KernelAdapter.class.getName());
+
+ private DefaultServer server; // this is unfortunate
+ private Kernel kernel;
+ private MessageListener<HostedConnection> messageDispatcher;
+ private AtomicBoolean go = new AtomicBoolean(true);
+
+ // Keeps track of the in-progress messages that are received
+ // on reliable connections
+ private Map<Endpoint, MessageProtocol> messageBuffers = new ConcurrentHashMap<Endpoint,MessageProtocol>();
+
+ // Marks the messages as reliable or not if they came
+ // through this connector.
+ private boolean reliable;
+
+ public KernelAdapter( DefaultServer server, Kernel kernel, MessageListener<HostedConnection> messageDispatcher,
+ boolean reliable )
+ {
+ super( String.valueOf(kernel) );
+ this.server = server;
+ this.kernel = kernel;
+ this.messageDispatcher = messageDispatcher;
+ this.reliable = reliable;
+ setDaemon(true);
+ }
+
+ public Kernel getKernel()
+ {
+ return kernel;
+ }
+
+ public void initialize()
+ {
+ kernel.initialize();
+ }
+
+ public void broadcast( Filter<? super Endpoint> filter, ByteBuffer data, boolean reliable,
+ boolean copy )
+ {
+ kernel.broadcast( filter, data, reliable, copy );
+ }
+
+ public void close() throws InterruptedException
+ {
+ go.set(false);
+
+ // Kill the kernel
+ kernel.terminate();
+ }
+
+ protected void reportError( Endpoint p, Object context, Exception e )
+ {
+ // Should really be queued up so the outer thread can
+ // retrieve them. For now we'll just log it. FIXME
+ log.log( Level.SEVERE, "Unhandled error, endpoint:" + p + ", context:" + context, e );
+
+ // In lieu of other options, at least close the endpoint
+ p.close();
+ }
+
+ protected HostedConnection getConnection( Endpoint p )
+ {
+ return server.getConnection(p);
+ }
+
+ protected void connectionClosed( Endpoint p )
+ {
+ // Remove any message buffer we've been accumulating
+ // on behalf of this endpoing
+ messageBuffers.remove(p);
+
+ log.log( Level.FINE, "Buffers size:{0}", messageBuffers.size() );
+
+ server.connectionClosed(p);
+ }
+
+ /**
+ * Note on threading for those writing their own server
+ * or adapter implementations. The rule that a single connection be
+ * processed by only one thread at a time is more about ensuring that
+ * the messages are delivered in the order that they are received
+ * than for any user-code safety. 99% of the time the user code should
+ * be writing for multithreaded access anyway.
+ *
+ * <p>The issue with the messages is that if a an implementation is
+ * using a general thread pool then it would be possible for a
+ * naive implementation to have one thread grab an Envelope from
+ * connection 1's and another grab the next Envelope. Since an Envelope
+ * may contain several messages, delivering the second thread's messages
+ * before or during the first's would be really confusing and hard
+ * to code for in user code.</p>
+ *
+ * <p>And that's why this note is here. DefaultServer does a rudimentary
+ * per-connection locking but it couldn't possibly guard against
+ * out of order Envelope processing.</p>
+ */
+ protected void dispatch( Endpoint p, Message m )
+ {
+ // Because this class is the only one with the information
+ // to do it... we need to pull of the registration message
+ // here.
+ if( m instanceof ClientRegistrationMessage ) {
+ server.registerClient( this, p, (ClientRegistrationMessage)m );
+ return;
+ }
+
+ try {
+ HostedConnection source = getConnection(p);
+ if( source == null ) {
+ if( reliable ) {
+ // If it's a reliable connection then it's slightly more
+ // concerning but this can happen all the time for a UDP endpoint.
+ log.log( Level.WARNING, "Recieved message from unconnected endpoint:" + p + " message:" + m );
+ }
+ return;
+ }
+ messageDispatcher.messageReceived( source, m );
+ } catch( Exception e ) {
+ reportError(p, m, e);
+ }
+ }
+
+ protected MessageProtocol getMessageBuffer( Endpoint p )
+ {
+ if( !reliable ) {
+ // Since UDP comes in packets and they aren't split
+ // up, there is no reason to buffer. In fact, there would
+ // be a down side because there is no way for us to reliably
+ // clean these up later since we'd create another one for
+ // any random UDP packet that comes to the port.
+ return new MessageProtocol();
+ } else {
+ // See if we already have one
+ MessageProtocol result = messageBuffers.get(p);
+ if( result == null ) {
+ result = new MessageProtocol();
+ messageBuffers.put(p, result);
+ }
+ return result;
+ }
+ }
+
+ protected void createAndDispatch( Envelope env )
+ {
+ MessageProtocol protocol = getMessageBuffer(env.getSource());
+
+ byte[] data = env.getData();
+ ByteBuffer buffer = ByteBuffer.wrap(data);
+
+ int count = protocol.addBuffer( buffer );
+ if( count == 0 ) {
+ // This can happen if there was only a partial message
+ // received. However, this should never happen for unreliable
+ // connections.
+ if( !reliable ) {
+ // Log some additional information about the packet.
+ int len = Math.min( 10, data.length );
+ StringBuilder sb = new StringBuilder();
+ for( int i = 0; i < len; i++ ) {
+ sb.append( "[" + Integer.toHexString(data[i]) + "]" );
+ }
+ log.log( Level.INFO, "First 10 bytes of incomplete nessage:" + sb );
+ throw new RuntimeException( "Envelope contained incomplete data:" + env );
+ }
+ }
+
+ // Should be complete... and maybe we should check but we don't
+ Message m = null;
+ while( (m = protocol.getMessage()) != null ) {
+ m.setReliable(reliable);
+ dispatch( env.getSource(), m );
+ }
+ }
+
+ protected void createAndDispatch( EndpointEvent event )
+ {
+ // Only need to tell the server about disconnects
+ if( event.getType() == EndpointEvent.Type.REMOVE ) {
+ connectionClosed( event.getEndpoint() );
+ }
+ }
+
+ protected void flushEvents()
+ {
+ EndpointEvent event;
+ while( (event = kernel.nextEvent()) != null ) {
+ try {
+ createAndDispatch( event );
+ } catch( Exception e ) {
+ reportError(event.getEndpoint(), event, e);
+ }
+ }
+ }
+
+ public void run()
+ {
+ while( go.get() ) {
+
+ try {
+ // Check for pending events
+ flushEvents();
+
+ // Grab the next envelope
+ Envelope e = kernel.read();
+ if( e == Kernel.EVENTS_PENDING )
+ continue; // We'll catch it up above
+
+ // Check for pending events that might have
+ // come in while we were blocking. This is usually
+ // when the connection add events come through
+ flushEvents();
+
+ try {
+ createAndDispatch( e );
+ } catch( Exception ex ) {
+ reportError(e.getSource(), e, ex);
+ }
+
+ } catch( InterruptedException ex ) {
+ if( !go.get() )
+ return;
+ throw new RuntimeException( "Unexpected interruption", ex );
+ }
+ }
+ }
+
+}
+
+
diff --git a/engine/src/networking/com/jme3/network/base/KernelFactory.java b/engine/src/networking/com/jme3/network/base/KernelFactory.java
new file mode 100644
index 0000000..0074d39
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/KernelFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.kernel.Kernel;
+import java.io.IOException;
+
+
+/**
+ * Supplied to the DefaultServer to create any additional
+ * channel kernels that might be required.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public interface KernelFactory
+{
+ public static final KernelFactory DEFAULT = new NioKernelFactory();
+
+ public Kernel createKernel( int channel, int port ) throws IOException;
+}
diff --git a/engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java b/engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java
new file mode 100644
index 0000000..c861786
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Keeps track of message listeners registered to specific
+ * types or to any type.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class MessageListenerRegistry<S> implements MessageListener<S>
+{
+ static Logger log = Logger.getLogger(MessageListenerRegistry.class.getName());
+
+ private List<MessageListener<? super S>> listeners = new CopyOnWriteArrayList<MessageListener<? super S>>();
+ private Map<Class,List<MessageListener<? super S>>> typeListeners
+ = new ConcurrentHashMap<Class,List<MessageListener<? super S>>>();
+
+ public MessageListenerRegistry()
+ {
+ }
+
+ public void messageReceived( S source, Message m )
+ {
+ boolean delivered = false;
+
+ for( MessageListener<? super S> l : listeners ) {
+ l.messageReceived( source, m );
+ delivered = true;
+ }
+
+ for( MessageListener<? super S> l : getListeners(m.getClass(),false) ) {
+ l.messageReceived( source, m );
+ delivered = true;
+ }
+
+ if( !delivered ) {
+ log.log( Level.INFO, "Received message had no registered listeners: {0}", m );
+ }
+ }
+
+ protected List<MessageListener<? super S>> getListeners( Class c, boolean create )
+ {
+ List<MessageListener<? super S>> result = typeListeners.get(c);
+ if( result == null && create ) {
+ result = new CopyOnWriteArrayList<MessageListener<? super S>>();
+ typeListeners.put( c, result );
+ }
+
+ if( result == null ) {
+ result = Collections.emptyList();
+ }
+ return result;
+ }
+
+ public void addMessageListener( MessageListener<? super S> listener )
+ {
+ listeners.add(listener);
+ }
+
+ public void removeMessageListener( MessageListener<? super S> listener )
+ {
+ listeners.remove(listener);
+ }
+
+ public void addMessageListener( MessageListener<? super S> listener, Class... classes )
+ {
+ for( Class c : classes ) {
+ getListeners(c, true).add(listener);
+ }
+ }
+
+ public void removeMessageListener( MessageListener<? super S> listener, Class... classes )
+ {
+ for( Class c : classes ) {
+ getListeners(c, false).remove(listener);
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/base/MessageProtocol.java b/engine/src/networking/com/jme3/network/base/MessageProtocol.java
new file mode 100644
index 0000000..eb22302
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/MessageProtocol.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.Message;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+
+/**
+ * Consolidates the conversion of messages to/from byte buffers
+ * and provides a rolling message buffer. ByteBuffers can be
+ * pushed in and messages will be extracted, accumulated, and
+ * available for retrieval. This is not thread safe and is meant
+ * to be used within a single message processing thread.
+ *
+ * <p>The protocol is based on a simple length + data format
+ * where two bytes represent the (short) length of the data
+ * and the rest is the raw data for the Serializers class.</p>
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class MessageProtocol
+{
+ private LinkedList<Message> messages = new LinkedList<Message>();
+ private ByteBuffer current;
+ private int size;
+ private Byte carry;
+
+ /**
+ * Converts a message to a ByteBuffer using the Serializer
+ * and the (short length) + data protocol. If target is null
+ * then a 32k byte buffer will be created and filled.
+ */
+ public static ByteBuffer messageToBuffer( Message message, ByteBuffer target )
+ {
+ // Could let the caller pass their own in
+ ByteBuffer buffer = target == null ? ByteBuffer.allocate( 32767 + 2 ) : target;
+
+ try {
+ buffer.position( 2 );
+ Serializer.writeClassAndObject( buffer, message );
+ buffer.flip();
+ short dataLength = (short)(buffer.remaining() - 2);
+ buffer.putShort( dataLength );
+ buffer.position( 0 );
+
+ return buffer;
+ } catch( IOException e ) {
+ throw new RuntimeException( "Error serializing message", e );
+ }
+ }
+
+ /**
+ * Retrieves and removes an extracted message from the accumulated buffer
+ * or returns null if there are no more messages.
+ */
+ public Message getMessage()
+ {
+ if( messages.isEmpty() ) {
+ return null;
+ }
+
+ return messages.removeFirst();
+ }
+
+ /**
+ * Adds the specified buffer, extracting the contained messages
+ * and making them available to getMessage(). The left over
+ * data is buffered to be combined with future data.
+ &
+ * @return The total number of queued messages after this call.
+ */
+ public int addBuffer( ByteBuffer buffer )
+ {
+ // push the data from the buffer into as
+ // many messages as we can
+ while( buffer.remaining() > 0 ) {
+
+ if( current == null ) {
+
+ // If we have a left over carry then we need to
+ // do manual processing to get the short value
+ if( carry != null ) {
+ byte high = carry;
+ byte low = buffer.get();
+
+ size = (high & 0xff) << 8 | (low & 0xff);
+ carry = null;
+ }
+ else if( buffer.remaining() < 2 ) {
+ // It's possible that the supplied buffer only has one
+ // byte in it... and in that case we will get an underflow
+ // when attempting to read the short below.
+
+ // It has to be 1 or we'd never get here... but one
+ // isn't enough so we stash it away.
+ carry = buffer.get();
+ break;
+ } else {
+ // We are not currently reading an object so
+ // grab the size.
+ // Note: this is somewhat limiting... int would
+ // be better.
+ size = buffer.getShort();
+ }
+
+ // Allocate the buffer into which we'll feed the
+ // data as we get it
+ current = ByteBuffer.allocate(size);
+ }
+
+ if( current.remaining() <= buffer.remaining() ) {
+ // We have at least one complete object so
+ // copy what we can into current, create a message,
+ // and then continue pulling from buffer.
+
+ // Artificially set the limit so we don't overflow
+ int extra = buffer.remaining() - current.remaining();
+ buffer.limit( buffer.position() + current.remaining() );
+
+ // Now copy the data
+ current.put( buffer );
+ current.flip();
+
+ // Now set the limit back to a good value
+ buffer.limit( buffer.position() + extra );
+
+ createMessage( current );
+
+ current = null;
+ } else {
+
+ // Not yet a complete object so just copy what we have
+ current.put( buffer );
+ }
+ }
+
+ return messages.size();
+ }
+
+ /**
+ * Creates a message from the properly sized byte buffer
+ * and adds it to the messages queue.
+ */
+ protected void createMessage( ByteBuffer buffer )
+ {
+ try {
+ Object obj = Serializer.readClassAndObject( buffer );
+ Message m = (Message)obj;
+ messages.add(m);
+ } catch( IOException e ) {
+ throw new RuntimeException( "Error deserializing object", e );
+ }
+ }
+}
+
+
+
diff --git a/engine/src/networking/com/jme3/network/base/NioKernelFactory.java b/engine/src/networking/com/jme3/network/base/NioKernelFactory.java
new file mode 100644
index 0000000..c9fab08
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/NioKernelFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.kernel.Kernel;
+import com.jme3.network.kernel.tcp.SelectorKernel;
+import java.io.IOException;
+
+
+/**
+ * KernelFactory implemention for creating TCP kernels
+ * using the NIO selector model.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public class NioKernelFactory implements KernelFactory
+{
+ public Kernel createKernel( int channel, int port ) throws IOException
+ {
+ return new SelectorKernel(port);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/base/TcpConnectorFactory.java b/engine/src/networking/com/jme3/network/base/TcpConnectorFactory.java
new file mode 100644
index 0000000..e53e62d
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/TcpConnectorFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.kernel.tcp.SocketConnector;
+import java.io.IOException;
+import java.net.InetAddress;
+
+
+/**
+ * Creates TCP connectors to a specific remote address.
+ *
+ * @version $Revision: 8938 $
+ * @author Paul Speed
+ */
+public class TcpConnectorFactory implements ConnectorFactory
+{
+ private InetAddress remoteAddress;
+
+ public TcpConnectorFactory( InetAddress remoteAddress )
+ {
+ this.remoteAddress = remoteAddress;
+ }
+
+ public Connector createConnector( int channel, int port ) throws IOException
+ {
+ return new SocketConnector( remoteAddress, port );
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/base/package.html b/engine/src/networking/com/jme3/network/base/package.html
new file mode 100644
index 0000000..a00cb16
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/base/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+The base package contains the default implementations for the
+{@link com.jme3.network.Client} and {@link com.jme3.network.Server}
+interfaces from the public API.
+</body>
+</html>
diff --git a/engine/src/networking/com/jme3/network/kernel/AbstractKernel.java b/engine/src/networking/com/jme3/network/kernel/AbstractKernel.java
new file mode 100644
index 0000000..9881bfa
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/AbstractKernel.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Base implementation of the Kernel interface providing several
+ * useful default implementations of some methods. This implementation
+ * assumes that the kernel will be managing its own internal threads
+ * and queuing any results for the caller to retrieve on their own
+ * thread.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public abstract class AbstractKernel implements Kernel
+{
+ static Logger log = Logger.getLogger(AbstractKernel.class.getName());
+
+ private AtomicLong nextId = new AtomicLong(1);
+
+ /**
+ * Contains the pending endpoint events waiting for the caller
+ * to retrieve them.
+ */
+ private ConcurrentLinkedQueue<EndpointEvent> endpointEvents = new ConcurrentLinkedQueue<EndpointEvent>();
+
+ /**
+ * Contains the pending envelopes waiting for the caller to
+ * retrieve them.
+ */
+ private LinkedBlockingQueue<Envelope> envelopes = new LinkedBlockingQueue<Envelope>();
+
+ protected AbstractKernel()
+ {
+ }
+
+ protected void reportError( Exception e )
+ {
+ // Should really be queued up so the outer thread can
+ // retrieve them. For now we'll just log it. FIXME
+ log.log( Level.SEVERE, "Unhanddled kernel error", e );
+ }
+
+ protected long nextEndpointId()
+ {
+ return nextId.getAndIncrement();
+ }
+
+ /**
+ * Returns true if there are waiting envelopes.
+ */
+ public boolean hasEnvelopes()
+ {
+ return !envelopes.isEmpty();
+ }
+
+ /**
+ * Removes one envelope from the received messages queue or
+ * blocks until one is available.
+ */
+ public Envelope read() throws InterruptedException
+ {
+ return envelopes.take();
+ }
+
+ /**
+ * Removes and returnsn one endpoint event from the event queue or
+ * null if there are no endpoint events.
+ */
+ public EndpointEvent nextEvent()
+ {
+ return endpointEvents.poll();
+ }
+
+ protected void addEvent( EndpointEvent e )
+ {
+ endpointEvents.add( e );
+ }
+
+ protected void addEnvelope( Envelope env )
+ {
+ if( !envelopes.offer( env ) ) {
+ throw new KernelException( "Critical error, could not enqueue envelope." );
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/Connector.java b/engine/src/networking/com/jme3/network/kernel/Connector.java
new file mode 100644
index 0000000..f86c17b
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/Connector.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A single channel remote connection allowing the sending
+ * and receiving of data. As opposed to the Kernel, this will
+ * only ever receive data from one Endpoint and so bypasses
+ * the envelope wrapping.
+ *
+ * @version $Revision: 7012 $
+ * @author Paul Speed
+ */
+public interface Connector
+{
+ /**
+ * Returns true if this connector is currently connected.
+ */
+ public boolean isConnected();
+
+ /**
+ * Closes the connection. Any subsequent attempts to read
+ * or write will fail with an exception.
+ */
+ public void close();
+
+ /**
+ * Returns true if there is currently data available for
+ * reading. Some connector implementations may not be able
+ * to answer this question accurately and will always return
+ * false.
+ */
+ public boolean available();
+
+ /**
+ * Reads a chunk of data from the connection, blocking if
+ * there is no data available. The buffer may only be valid
+ * until the next read() call is made. Callers should copy
+ * the data if they need it for longer than that.
+ *
+ * @return The data read or null if there is no more data
+ * because the connection is closed.
+ */
+ public ByteBuffer read();
+
+ /**
+ * Writes a chunk of data to the connection from data.position()
+ * to data.limit().
+ */
+ public void write( ByteBuffer data );
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/ConnectorException.java b/engine/src/networking/com/jme3/network/kernel/ConnectorException.java
new file mode 100644
index 0000000..31d02b0
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/ConnectorException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+
+/**
+ * Represents a client-side connection error, usually encapsulating
+ * an IOException as its cause.
+ *
+ * @version $Revision: 7009 $
+ * @author Paul Speed
+ */
+public class ConnectorException extends RuntimeException
+{
+ public ConnectorException( String message, Throwable cause )
+ {
+ super( message, cause );
+ }
+
+ public ConnectorException( String message )
+ {
+ super( message );
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/Endpoint.java b/engine/src/networking/com/jme3/network/kernel/Endpoint.java
new file mode 100644
index 0000000..d63fdae
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/Endpoint.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+import java.nio.ByteBuffer;
+
+/**
+ * An abstract endpoint in a Kernel that can be used for
+ * sending/receiving messages within the kernel space.
+ *
+ * @version $Revision: 7049 $
+ * @author Paul Speed
+ */
+public interface Endpoint
+{
+ /**
+ * Returns an ID that is unique for this endpoint within its
+ * Kernel instance.
+ */
+ public long getId();
+
+ /**
+ * Returns the transport specific remote address of this endpoint
+ * as a string. This may or may not be unique per endpoint depending
+ * on the type of transport.
+ */
+ public String getAddress();
+
+ /**
+ * Returns the kernel to which this endpoint belongs.
+ */
+ public Kernel getKernel();
+
+ /**
+ * Returns true if this endpoint is currently connected.
+ */
+ public boolean isConnected();
+
+ /**
+ * Sends data to the other end of the connection represented
+ * by this endpoint.
+ */
+ public void send( ByteBuffer data );
+
+ /**
+ * Closes this endpoint without flushing any of its
+ * currently enqueued outbound data.
+ */
+ public void close();
+
+ /**
+ * Closes this endpoint, optionally flushing any queued
+ * data before closing. As soon as this method is called,
+ * ne send() calls will fail with an exception... even while
+ * close() is still flushing the earlier queued messages.
+ */
+ public void close(boolean flushData);
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/EndpointEvent.java b/engine/src/networking/com/jme3/network/kernel/EndpointEvent.java
new file mode 100644
index 0000000..feb48a6
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/EndpointEvent.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+
+/**
+ * Provides information about an added or
+ * removed connection.
+ *
+ * @version $Revision: 7009 $
+ * @author Paul Speed
+ */
+public class EndpointEvent
+{
+ public enum Type { ADD, REMOVE };
+
+ private Kernel source;
+ private Endpoint endpoint;
+ private Type type;
+
+ public EndpointEvent( Kernel source, Endpoint p, Type type )
+ {
+ this.source = source;
+ this.endpoint = p;
+ this.type = type;
+ }
+
+ public static EndpointEvent createAdd( Kernel source, Endpoint p )
+ {
+ return new EndpointEvent( source, p, Type.ADD );
+ }
+
+ public static EndpointEvent createRemove( Kernel source, Endpoint p )
+ {
+ return new EndpointEvent( source, p, Type.REMOVE );
+ }
+
+ public Kernel getSource()
+ {
+ return source;
+ }
+
+ public Endpoint getEndpoint()
+ {
+ return endpoint;
+ }
+
+ public Type getType()
+ {
+ return type;
+ }
+
+ public String toString()
+ {
+ return "EndpointEvent[" + type + ", " + endpoint + "]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/Envelope.java b/engine/src/networking/com/jme3/network/kernel/Envelope.java
new file mode 100644
index 0000000..3c0028b
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/Envelope.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+/**
+ * Encapsulates a received piece of data. This is used by the Kernel
+ * to track incoming chunks of data.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class Envelope
+{
+ private Endpoint source;
+ private byte[] data;
+ private boolean reliable;
+
+ /**
+ * Creates an incoming envelope holding the data from the specified
+ * source. The 'reliable' flag further indicates on which mode of
+ * transport the data arrrived.
+ */
+ public Envelope( Endpoint source, byte[] data, boolean reliable )
+ {
+ this.source = source;
+ this.data = data;
+ this.reliable = reliable;
+ }
+
+ public Endpoint getSource()
+ {
+ return source;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ public boolean isReliable()
+ {
+ return reliable;
+ }
+
+ public String toString()
+ {
+ return "Envelope[" + source + ", " + (reliable?"reliable":"unreliable") + ", " + data.length + "]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/Kernel.java b/engine/src/networking/com/jme3/network/kernel/Kernel.java
new file mode 100644
index 0000000..c13b736
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/Kernel.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+import com.jme3.network.Filter;
+import java.nio.ByteBuffer;
+
+/**
+ * Defines the basic byte[] passing messaging
+ * kernel.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public interface Kernel
+{
+ /**
+ * A marker envelope returned from read() that indicates that
+ * there are events pending. This allows a single thread to
+ * more easily process the envelopes and endpoint events.
+ */
+ public static final Envelope EVENTS_PENDING = new Envelope( null, new byte[0], false );
+
+ /**
+ * Initializes the kernel and starts any internal processing.
+ */
+ public void initialize();
+
+ /**
+ * Gracefully terminates the kernel and stops any internal
+ * daemon processing. This method will not return until all
+ * internal threads have been shut down.
+ */
+ public void terminate() throws InterruptedException;
+
+ /**
+ * Dispatches the data to all endpoints managed by the
+ * kernel that match the specified endpoint filter..
+ * If 'copy' is true then the implementation will copy the byte buffer
+ * before delivering it to endpoints. This allows the caller to reuse
+ * the data buffer. Though it is important that the buffer not be changed
+ * by another thread while this call is running.
+ * Only the bytes from data.position() to data.remaining() are sent.
+ */
+ public void broadcast( Filter<? super Endpoint> filter, ByteBuffer data, boolean reliable,
+ boolean copy );
+
+ /**
+ * Returns true if there are waiting envelopes.
+ */
+ public boolean hasEnvelopes();
+
+ /**
+ * Removes one envelope from the received messages queue or
+ * blocks until one is available.
+ */
+ public Envelope read() throws InterruptedException;
+
+ /**
+ * Removes and returnsn one endpoint event from the event queue or
+ * null if there are no endpoint events.
+ */
+ public EndpointEvent nextEvent();
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/KernelException.java b/engine/src/networking/com/jme3/network/kernel/KernelException.java
new file mode 100644
index 0000000..62a57e4
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/KernelException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+
+/**
+ * Represents a kernel-level error, usually encapsulating
+ * an IOException as its cause.
+ *
+ * @version $Revision: 7009 $
+ * @author Paul Speed
+ */
+public class KernelException extends RuntimeException
+{
+ public KernelException( String message, Throwable cause )
+ {
+ super( message, cause );
+ }
+
+ public KernelException( String message )
+ {
+ super( message );
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/NamedThreadFactory.java b/engine/src/networking/com/jme3/network/kernel/NamedThreadFactory.java
new file mode 100644
index 0000000..f56c23a
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/NamedThreadFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * A simple factory that delegates to java.util.concurrent's
+ * default thread factory but adds a prefix to the beginning
+ * of the thread name.
+ *
+ * @version $Revision: 7314 $
+ * @author Paul Speed
+ */
+public class NamedThreadFactory implements ThreadFactory
+{
+ private String name;
+ private boolean daemon;
+ private ThreadFactory delegate;
+
+ public NamedThreadFactory( String name )
+ {
+ this( name, Executors.defaultThreadFactory() );
+ }
+
+ public NamedThreadFactory( String name, boolean daemon )
+ {
+ this( name, daemon, Executors.defaultThreadFactory() );
+ }
+
+ public NamedThreadFactory( String name, ThreadFactory delegate )
+ {
+ this( name, false, delegate );
+ }
+
+ public NamedThreadFactory( String name, boolean daemon, ThreadFactory delegate )
+ {
+ this.name = name;
+ this.daemon = daemon;
+ this.delegate = delegate;
+ }
+
+ public Thread newThread( Runnable r )
+ {
+ Thread result = delegate.newThread(r);
+ String s = result.getName();
+ result.setName( name + "[" + s + "]" );
+ result.setDaemon(daemon);
+ return result;
+ }
+}
+
diff --git a/engine/src/networking/com/jme3/network/kernel/package.html b/engine/src/networking/com/jme3/network/kernel/package.html
new file mode 100644
index 0000000..7029c95
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/package.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+
+<body>
+The kernel package is the heart of the JME networking module
+and controls the routing and dispatch of message data over
+different transport implementations. Most users will never
+have to deal with these classes unless they are writing their own
+client and server implementations that diverge from the standard
+classes that are provided.
+
+<p>{@link com.jme3.network.kernel.Kernel} defines the core of a server-side message
+broker that abstracts away the specific transport and underlying
+threading model used. For example, it might use NIO selectors
+in a single threaded model or straight multithreaded socket
+model. Or it might implement SSL connections. Once created,
+{@link com.jme3.network.kernel.Kernel} users don't need to care about the details.</p>
+
+<p>{@link com.jme3.network.kernel.Endpoint} is a managed connection within a
+{@link com.jme3.network.kernel.Kernel} providing kernel to client connectivity.</p>
+
+<p>{@link com.jme3.network.kernel.Connector} defines the basic client-side message sender
+and these objects are typically used to connect to a {@link com.jme3.network.kernel.Kernel}
+though they can connect to any network port that supports the implementation's
+protocol. Implementations are provided for straight TCP and UDP communication
+and could be extended to support SSL or different threading models.</p>
+
+</body>
+</html>
diff --git a/engine/src/networking/com/jme3/network/kernel/tcp/NioEndpoint.java b/engine/src/networking/com/jme3/network/kernel/tcp/NioEndpoint.java
new file mode 100644
index 0000000..87b721f
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/tcp/NioEndpoint.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel.tcp;
+
+import com.jme3.network.kernel.Endpoint;
+import com.jme3.network.kernel.Kernel;
+import com.jme3.network.kernel.KernelException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+
+/**
+ * Endpoint implementation that encapsulates the
+ * channel IO based connection information and keeps
+ * track of the outbound data queue for the channel.
+ *
+ * @version $Revision: 8944 $
+ * @author Paul Speed
+ */
+public class NioEndpoint implements Endpoint
+{
+ protected static final ByteBuffer CLOSE_MARKER = ByteBuffer.allocate(0);
+
+ private long id;
+ private SocketChannel socket;
+ private SelectorKernel kernel;
+ private ConcurrentLinkedQueue<ByteBuffer> outbound = new ConcurrentLinkedQueue<ByteBuffer>();
+ private boolean closing = false;
+
+ public NioEndpoint( SelectorKernel kernel, long id, SocketChannel socket )
+ {
+ this.id = id;
+ this.socket = socket;
+ this.kernel = kernel;
+ }
+
+ public Kernel getKernel()
+ {
+ return kernel;
+ }
+
+ public void close()
+ {
+ close(false);
+ }
+
+ public void close( boolean flushData )
+ {
+ if( flushData ) {
+ closing = true;
+
+ // Enqueue a close marker message to let the server
+ // know we should close
+ send( CLOSE_MARKER, false, true );
+
+ return;
+ }
+
+ try {
+ // Note: even though we may be disconnected from the socket.isConnected()
+ // standpoint, it's still safest to tell the kernel so that it can be sure
+ // to stop managing us gracefully.
+ kernel.closeEndpoint(this);
+ } catch( IOException e ) {
+ throw new KernelException( "Error closing endpoint for socket:" + socket, e );
+ }
+ }
+
+ public long getId()
+ {
+ return id;
+ }
+
+ public String getAddress()
+ {
+ return String.valueOf(socket.socket().getRemoteSocketAddress());
+ }
+
+ public boolean isConnected()
+ {
+ return socket.isConnected();
+ }
+
+ /**
+ * The wakeup option is used internally when the kernel is
+ * broadcasting out to a bunch of endpoints and doesn't want to
+ * necessarily wakeup right away.
+ */
+ protected void send( ByteBuffer data, boolean copy, boolean wakeup )
+ {
+ // We create a ByteBuffer per endpoint since we
+ // use it to track the data sent to each endpoint
+ // separately.
+ ByteBuffer buffer;
+ if( !copy ) {
+ buffer = data;
+ } else {
+ // Copy the buffer
+ buffer = ByteBuffer.allocate(data.remaining());
+ buffer.put(data);
+ buffer.flip();
+ }
+
+ // Queue it up
+ outbound.add(buffer);
+
+ if( wakeup )
+ kernel.wakeupSelector();
+ }
+
+ /**
+ * Called by the SelectorKernel to get the current top
+ * buffer for writing.
+ */
+ protected ByteBuffer peekPending()
+ {
+ return outbound.peek();
+ }
+
+ /**
+ * Called by the SelectorKernel when the top buffer
+ * has been exhausted.
+ */
+ protected ByteBuffer removePending()
+ {
+ return outbound.poll();
+ }
+
+ protected boolean hasPending()
+ {
+ return !outbound.isEmpty();
+ }
+
+ public void send( ByteBuffer data )
+ {
+ if( data == null ) {
+ throw new IllegalArgumentException( "Data cannot be null." );
+ }
+ if( closing ) {
+ throw new KernelException( "Endpoint has been closed:" + socket );
+ }
+ send( data, true, true );
+ }
+
+ public String toString()
+ {
+ return "NioEndpoint[" + id + ", " + socket + "]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/tcp/SelectorKernel.java b/engine/src/networking/com/jme3/network/kernel/tcp/SelectorKernel.java
new file mode 100644
index 0000000..5fa75f3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/tcp/SelectorKernel.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel.tcp;
+
+import com.jme3.network.Filter;
+import com.jme3.network.kernel.*;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * A Kernel implementation based on NIO selectors.
+ *
+ * @version $Revision: 8944 $
+ * @author Paul Speed
+ */
+public class SelectorKernel extends AbstractKernel
+{
+ static Logger log = Logger.getLogger(SelectorKernel.class.getName());
+
+ private InetSocketAddress address;
+ private SelectorThread thread;
+
+ private Map<Long,NioEndpoint> endpoints = new ConcurrentHashMap<Long,NioEndpoint>();
+
+ public SelectorKernel( InetAddress host, int port )
+ {
+ this( new InetSocketAddress(host, port) );
+ }
+
+ public SelectorKernel( int port ) throws IOException
+ {
+ this( new InetSocketAddress(port) );
+ }
+
+ public SelectorKernel( InetSocketAddress address )
+ {
+ this.address = address;
+ }
+
+ protected SelectorThread createSelectorThread()
+ {
+ return new SelectorThread();
+ }
+
+ public void initialize()
+ {
+ if( thread != null )
+ throw new IllegalStateException( "Kernel already initialized." );
+
+ thread = createSelectorThread();
+
+ try {
+ thread.connect();
+ thread.start();
+ } catch( IOException e ) {
+ throw new KernelException( "Error hosting:" + address, e );
+ }
+ }
+
+ public void terminate() throws InterruptedException
+ {
+ if( thread == null )
+ throw new IllegalStateException( "Kernel not initialized." );
+
+ try {
+ thread.close();
+ thread = null;
+ } catch( IOException e ) {
+ throw new KernelException( "Error closing host connection:" + address, e );
+ }
+ }
+
+ public void broadcast( Filter<? super Endpoint> filter, ByteBuffer data, boolean reliable,
+ boolean copy )
+ {
+ if( !reliable )
+ throw new UnsupportedOperationException( "Unreliable send not supported by this kernel." );
+
+ if( copy ) {
+ // Copy the data just once
+ byte[] temp = new byte[data.remaining()];
+ System.arraycopy(data.array(), data.position(), temp, 0, data.remaining());
+ data = ByteBuffer.wrap(temp);
+ }
+
+ // Hand it to all of the endpoints that match our routing
+ for( NioEndpoint p : endpoints.values() ) {
+ // Does it match the filter?
+ if( filter != null && !filter.apply(p) )
+ continue;
+
+ // Give it the data... but let each endpoint track their
+ // own completion over the shared array of bytes by
+ // duplicating it
+ p.send( data.duplicate(), false, false );
+ }
+
+ // Wake up the selector so it can reinitialize its
+ // state accordingly.
+ wakeupSelector();
+ }
+
+ protected NioEndpoint addEndpoint( SocketChannel c )
+ {
+ // Note: we purposely do NOT put the key in the endpoint.
+ // SelectionKeys are dangerous outside the selector thread
+ // and this is safer.
+ NioEndpoint p = new NioEndpoint( this, nextEndpointId(), c );
+
+ endpoints.put( p.getId(), p );
+
+ // Enqueue an endpoint event for the listeners
+ addEvent( EndpointEvent.createAdd( this, p ) );
+
+ return p;
+ }
+
+ protected void removeEndpoint( NioEndpoint p, SocketChannel c )
+ {
+ endpoints.remove( p.getId() );
+ log.log( Level.FINE, "Endpoints size:{0}", endpoints.size() );
+
+ // Enqueue an endpoint event for the listeners
+ addEvent( EndpointEvent.createRemove( this, p ) );
+
+ // If there are no pending messages then add one so that the
+ // kernel-user knows to wake up if it is only listening for
+ // envelopes.
+ if( !hasEnvelopes() ) {
+ // Note: this is not really a race condition. At worst, our
+ // event has already been handled by now and it does no harm
+ // to check again.
+ addEnvelope( EVENTS_PENDING );
+ }
+ }
+
+ /**
+ * Called by the endpoints when they need to be closed.
+ */
+ protected void closeEndpoint( NioEndpoint p ) throws IOException
+ {
+ //log.log( Level.INFO, "Closing endpoint:{0}.", p );
+
+ thread.cancel(p);
+ }
+
+ /**
+ * Used internally by the endpoints to wakeup the selector
+ * when they have data to send.
+ */
+ protected void wakeupSelector()
+ {
+ thread.wakeupSelector();
+ }
+
+ protected void newData( NioEndpoint p, SocketChannel c, ByteBuffer shared, int size )
+ {
+ // Note: if ever desirable, it would be possible to accumulate
+ // data per source channel and only 'finalize' it when
+ // asked for more envelopes then were ready. I just don't
+ // think it will be an issue in practice. The busier the
+ // server, the more the buffers will fill before we get to them.
+ // And if the server isn't busy, who cares if we chop things up
+ // smaller... the network is still likely to deliver things in
+ // bulk anyway.
+
+ // Must copy the shared data before we use it
+ byte[] dataCopy = new byte[size];
+ System.arraycopy(shared.array(), 0, dataCopy, 0, size);
+
+ Envelope env = new Envelope( p, dataCopy, true );
+ addEnvelope( env );
+ }
+
+ /**
+ * This class is purposely tucked neatly away because
+ * messing with the selector from other threads for any
+ * reason is very bad. This is the safest architecture.
+ */
+ protected class SelectorThread extends Thread
+ {
+ private ServerSocketChannel serverChannel;
+ private Selector selector;
+ private AtomicBoolean go = new AtomicBoolean(true);
+ private ByteBuffer working = ByteBuffer.allocate( 8192 );
+
+ /**
+ * Because we want to keep the keys to ourselves, we'll do
+ * the endpoint -> key mapping internally.
+ */
+ private Map<NioEndpoint,SelectionKey> endpointKeys = new ConcurrentHashMap<NioEndpoint,SelectionKey>();
+
+ public SelectorThread()
+ {
+ setName( "Selector@" + address );
+ setDaemon(true);
+ }
+
+ public void connect() throws IOException
+ {
+ // Create a new selector
+ this.selector = SelectorProvider.provider().openSelector();
+
+ // Create a new non-blocking server socket channel
+ this.serverChannel = ServerSocketChannel.open();
+ serverChannel.configureBlocking(false);
+
+ // Bind the server socket to the specified address and port
+ serverChannel.socket().bind(address);
+
+ // Register the server socket channel, indicating an interest in
+ // accepting new connections
+ serverChannel.register(selector, SelectionKey.OP_ACCEPT);
+
+ log.log( Level.INFO, "Hosting TCP connection:{0}.", address );
+ }
+
+ public void close() throws IOException, InterruptedException
+ {
+ // Set the thread to stop
+ go.set(false);
+
+ // Make sure the channel is closed
+ serverChannel.close();
+
+ // Force the selector to stop blocking
+ wakeupSelector();
+
+ // And wait for it
+ join();
+ }
+
+ protected void wakeupSelector()
+ {
+ selector.wakeup();
+ }
+
+ protected void setupSelectorOptions()
+ {
+ // For now, selection keys will either be in OP_READ
+ // or OP_WRITE. So while we are writing a buffer, we
+ // will not be reading. This is way simpler and less
+ // error prone... it can always be changed when everything
+ // else works if we are looking to micro-optimize.
+
+ // Setup options based on the current state of
+ // the endpoints. This could potentially be more
+ // efficiently done as change requests... or simply
+ // keeping a thread-safe set of endpoints with pending
+ // writes. For most cases, it shouldn't matter.
+ for( Map.Entry<NioEndpoint,SelectionKey> e : endpointKeys.entrySet() ) {
+ if( e.getKey().hasPending() ) {
+ e.getValue().interestOps(SelectionKey.OP_WRITE);
+ }
+ }
+ }
+
+ protected void accept( SelectionKey key ) throws IOException
+ {
+ // Would only get accepts on a server channel
+ ServerSocketChannel serverChan = (ServerSocketChannel)key.channel();
+
+ // Setup the connection to be non-blocking
+ SocketChannel remoteChan = serverChan.accept();
+ remoteChan.configureBlocking(false);
+
+ // And disable Nagle's buffering algorithm... we want
+ // data to go when we put it there.
+ Socket sock = remoteChan.socket();
+ sock.setTcpNoDelay(true);
+
+ // Let the selector know we're interested in reading
+ // data from the channel
+ SelectionKey endKey = remoteChan.register( selector, SelectionKey.OP_READ );
+
+ // And now create a new endpoint
+ NioEndpoint p = addEndpoint( remoteChan );
+ endKey.attach(p);
+ endpointKeys.put(p, endKey);
+ }
+
+ protected void cancel( NioEndpoint p ) throws IOException
+ {
+ SelectionKey key = endpointKeys.remove(p);
+ if( key == null ) {
+ //log.log( Level.INFO, "Endpoint already closed:{0}.", p );
+ return; // already closed it
+ }
+ log.log( Level.FINE, "Endpoint keys size:{0}", endpointKeys.size() );
+
+ log.log( Level.INFO, "Closing endpoint:{0}.", p );
+ SocketChannel c = (SocketChannel)key.channel();
+
+ // Note: key.cancel() is specifically thread safe. One of
+ // the few things one can do with a key from another
+ // thread.
+ key.cancel();
+ c.close();
+ removeEndpoint( p, c );
+ }
+
+ protected void cancel( SelectionKey key, SocketChannel c ) throws IOException
+ {
+ NioEndpoint p = (NioEndpoint)key.attachment();
+ log.log( Level.INFO, "Closing channel endpoint:{0}.", p );
+ Object o = endpointKeys.remove(p);
+
+ log.log( Level.FINE, "Endpoint keys size:{0}", endpointKeys.size() );
+
+ key.cancel();
+ c.close();
+ removeEndpoint( p, c );
+ }
+
+ protected void read( SelectionKey key ) throws IOException
+ {
+ NioEndpoint p = (NioEndpoint)key.attachment();
+ SocketChannel c = (SocketChannel)key.channel();
+ working.clear();
+
+ int size;
+ try {
+ size = c.read(working);
+ } catch( IOException e ) {
+ // The remove end forcibly closed the connection...
+ // close out our end and cancel the key
+ cancel( key, c );
+ return;
+ }
+
+ if( size == -1 ) {
+ // The remote end shut down cleanly...
+ // close out our end and cancel the key
+ cancel( key, c );
+ return;
+ }
+
+ newData( p, c, working, size );
+ }
+
+ protected void write( SelectionKey key ) throws IOException
+ {
+ NioEndpoint p = (NioEndpoint)key.attachment();
+ SocketChannel c = (SocketChannel)key.channel();
+
+ // We will send what we can and move on.
+ ByteBuffer current = p.peekPending();
+ if( current == NioEndpoint.CLOSE_MARKER ) {
+ // This connection wants to be closed now
+ closeEndpoint(p);
+
+ // Nothing more to do
+ return;
+ }
+
+ c.write( current );
+
+ // If we wrote all of that packet then we need to remove it
+ if( current.remaining() == 0 ) {
+ p.removePending();
+ }
+
+ // If we happened to empty the pending queue then let's read
+ // again.
+ if( !p.hasPending() ) {
+ key.interestOps( SelectionKey.OP_READ );
+ }
+ }
+
+ protected void select() throws IOException
+ {
+ selector.select();
+
+ for( Iterator<SelectionKey> i = selector.selectedKeys().iterator(); i.hasNext(); ) {
+ SelectionKey key = i.next();
+ i.remove();
+
+ if( !key.isValid() )
+ {
+ // When does this happen?
+ log.log( Level.INFO, "Key is not valid:{0}.", key );
+ continue;
+ }
+
+ try {
+ if( key.isAcceptable() )
+ accept(key);
+ else if( key.isWritable() )
+ write(key);
+ else if( key.isReadable() )
+ read(key);
+ } catch( IOException e ) {
+ if( !go.get() )
+ return; // error likely due to shutting down
+ reportError( e );
+
+ // And at this level, errors likely mean the key is now
+ // dead and it doesn't hurt to kick them anyway. If we
+ // find IOExceptions that are not fatal, this can be
+ // readdressed
+ cancel( key, (SocketChannel)key.channel() );
+ }
+ }
+ }
+
+ public void run()
+ {
+ log.log( Level.INFO, "Kernel started for connection:{0}.", address );
+
+ // An atomic is safest and costs almost nothing
+ while( go.get() ) {
+ // Setup any queued option changes
+ setupSelectorOptions();
+
+ // Check for available keys and process them
+ try {
+ select();
+ } catch( ClosedSelectorException e ) {
+ if( !go.get() )
+ return; // it's because we're shutting down
+ throw new KernelException( "Premature selector closing", e );
+ } catch( IOException e ) {
+ if( !go.get() )
+ return; // error likely due to shutting down
+ reportError( e );
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/tcp/SocketConnector.java b/engine/src/networking/com/jme3/network/kernel/tcp/SocketConnector.java
new file mode 100644
index 0000000..66d2040
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/tcp/SocketConnector.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel.tcp;
+
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.kernel.ConnectorException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+/**
+ * A straight forward socket-based connector implementation that
+ * does not use any separate threading. It relies completely on
+ * the buffering in the OS network layer.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class SocketConnector implements Connector
+{
+ private Socket sock;
+ private InputStream in;
+ private OutputStream out;
+ private SocketAddress remoteAddress;
+ private byte[] buffer = new byte[65535];
+ private AtomicBoolean connected = new AtomicBoolean(false);
+
+ public SocketConnector( InetAddress address, int port ) throws IOException
+ {
+ this.sock = new Socket(address, port);
+ remoteAddress = sock.getRemoteSocketAddress(); // for info purposes
+
+ // Disable Nagle's buffering so data goes out when we
+ // put it there.
+ sock.setTcpNoDelay(true);
+
+ in = sock.getInputStream();
+ out = sock.getOutputStream();
+
+ connected.set(true);
+ }
+
+ protected void checkClosed()
+ {
+ if( sock == null )
+ throw new ConnectorException( "Connection is closed:" + remoteAddress );
+ }
+
+ public boolean isConnected()
+ {
+ if( sock == null )
+ return false;
+ return sock.isConnected();
+ }
+
+ public void close()
+ {
+ checkClosed();
+ try {
+ Socket temp = sock;
+ sock = null;
+ connected.set(false);
+ temp.close();
+ } catch( IOException e ) {
+ throw new ConnectorException( "Error closing socket for:" + remoteAddress, e );
+ }
+ }
+
+ public boolean available()
+ {
+ checkClosed();
+ try {
+ return in.available() > 0;
+ } catch( IOException e ) {
+ throw new ConnectorException( "Error retrieving data availability for:" + remoteAddress, e );
+ }
+ }
+
+ public ByteBuffer read()
+ {
+ checkClosed();
+
+ try {
+ // Read what we can
+ int count = in.read(buffer);
+ if( count < 0 ) {
+ // Socket is closed
+ close();
+ return null;
+ }
+
+ // Wrap it in a ByteBuffer for the caller
+ return ByteBuffer.wrap( buffer, 0, count );
+ } catch( IOException e ) {
+ if( !connected.get() ) {
+ // Nothing to see here... just move along
+ return null;
+ }
+ throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e );
+ }
+ }
+
+ public void write( ByteBuffer data )
+ {
+ checkClosed();
+
+ try {
+ out.write(data.array(), data.position(), data.remaining());
+ } catch( IOException e ) {
+ throw new ConnectorException( "Error writing to connection:" + remoteAddress, e );
+ }
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/udp/UdpConnector.java b/engine/src/networking/com/jme3/network/kernel/udp/UdpConnector.java
new file mode 100644
index 0000000..0193e1c
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/udp/UdpConnector.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel.udp;
+
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.kernel.ConnectorException;
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+/**
+ * A straight forward datagram socket-based UDP connector
+ * implementation.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class UdpConnector implements Connector
+{
+ private DatagramSocket sock = new DatagramSocket();
+ private SocketAddress remoteAddress;
+ private byte[] buffer = new byte[65535];
+ private AtomicBoolean connected = new AtomicBoolean(false);
+
+ /**
+ * In order to provide proper available() checking, we
+ * potentially queue one datagram.
+ */
+ private DatagramPacket pending;
+
+ /**
+ * Creates a new UDP connection that send datagrams to the
+ * specified address and port.
+ */
+ public UdpConnector( InetAddress remote, int remotePort ) throws IOException
+ {
+ InetSocketAddress localSocketAddress = new InetSocketAddress(0);
+ this.sock = new DatagramSocket( localSocketAddress );
+ remoteAddress = new InetSocketAddress( remote, remotePort );
+
+ // Setup to receive only from the remote address
+ sock.connect( remoteAddress );
+
+ connected.set(true);
+ }
+
+ protected void checkClosed()
+ {
+ if( sock == null )
+ throw new ConnectorException( "Connection is closed:" + remoteAddress );
+ }
+
+ public boolean isConnected()
+ {
+ if( sock == null )
+ return false;
+ return sock.isConnected();
+ }
+
+ public void close()
+ {
+ checkClosed();
+ DatagramSocket temp = sock;
+ sock = null;
+ connected.set(false);
+ temp.close();
+ }
+
+ /**
+ * This always returns false since the simple DatagramSocket usage
+ * cannot be run in a non-blocking way.
+ */
+ public boolean available()
+ {
+ // It would take a separate thread or an NIO Selector based implementation to get this
+ // to work. If a polling strategy is never employed by callers then it doesn't
+ // seem worth it to implement all of that just for this method.
+ checkClosed();
+ return false;
+ }
+
+ public ByteBuffer read()
+ {
+ checkClosed();
+
+ try {
+ DatagramPacket packet = new DatagramPacket( buffer, buffer.length );
+ sock.receive(packet);
+
+ // Wrap it in a ByteBuffer for the caller
+ return ByteBuffer.wrap( buffer, 0, packet.getLength() );
+ } catch( IOException e ) {
+ if( !connected.get() ) {
+ // Nothing to see here... just move along
+ return null;
+ }
+ throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e );
+ }
+ }
+
+ public void write( ByteBuffer data )
+ {
+ checkClosed();
+
+ try {
+ DatagramPacket p = new DatagramPacket( data.array(), data.position(), data.remaining(),
+ remoteAddress );
+ sock.send(p);
+ } catch( IOException e ) {
+ throw new ConnectorException( "Error writing to connection:" + remoteAddress, e );
+ }
+ }
+}
+
diff --git a/engine/src/networking/com/jme3/network/kernel/udp/UdpEndpoint.java b/engine/src/networking/com/jme3/network/kernel/udp/UdpEndpoint.java
new file mode 100644
index 0000000..4a98387
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/udp/UdpEndpoint.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel.udp;
+
+import com.jme3.network.kernel.Endpoint;
+import com.jme3.network.kernel.Kernel;
+import com.jme3.network.kernel.KernelException;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+
+
+/**
+ * Endpoint implementation that encapsulates the
+ * UDP connection information for return messaging,
+ * identification of envelope sources, etc.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class UdpEndpoint implements Endpoint
+{
+ private long id;
+ private SocketAddress address;
+ private DatagramSocket socket;
+ private UdpKernel kernel;
+ private boolean connected = true; // it's connectionless but we track logical state
+
+ public UdpEndpoint( UdpKernel kernel, long id, SocketAddress address, DatagramSocket socket )
+ {
+ this.id = id;
+ this.address = address;
+ this.socket = socket;
+ this.kernel = kernel;
+ }
+
+ public Kernel getKernel()
+ {
+ return kernel;
+ }
+
+ protected SocketAddress getRemoteAddress()
+ {
+ return address;
+ }
+
+ public void close()
+ {
+ close( false );
+ }
+
+ public void close( boolean flush )
+ {
+ // No real reason to flush UDP traffic yet... especially
+ // when considering that the outbound UDP isn't even
+ // queued.
+
+ try {
+ kernel.closeEndpoint(this);
+ connected = false;
+ } catch( IOException e ) {
+ throw new KernelException( "Error closing endpoint for socket:" + socket, e );
+ }
+ }
+
+ public long getId()
+ {
+ return id;
+ }
+
+ public String getAddress()
+ {
+ return String.valueOf(address);
+ }
+
+ public boolean isConnected()
+ {
+ // The socket is always unconnected anyway so we track our
+ // own logical state for the kernel's benefit.
+ return connected;
+ }
+
+ public void send( ByteBuffer data )
+ {
+ if( !isConnected() ) {
+ throw new KernelException( "Endpoint is not connected:" + this );
+ }
+
+
+ try {
+ DatagramPacket p = new DatagramPacket( data.array(), data.position(),
+ data.remaining(), address );
+
+ // Just queue it up for the kernel threads to write
+ // out
+ kernel.enqueueWrite( this, p );
+
+ //socket.send(p);
+ } catch( IOException e ) {
+ throw new KernelException( "Error sending datagram to:" + address, e );
+ }
+ }
+
+ public String toString()
+ {
+ return "UdpEndpoint[" + id + ", " + address + "]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/kernel/udp/UdpKernel.java b/engine/src/networking/com/jme3/network/kernel/udp/UdpKernel.java
new file mode 100644
index 0000000..c0a0f88
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/kernel/udp/UdpKernel.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.kernel.udp;
+
+import com.jme3.network.Filter;
+import com.jme3.network.kernel.*;
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A Kernel implementation using UDP packets.
+ *
+ * @version $Revision: 8944 $
+ * @author Paul Speed
+ */
+public class UdpKernel extends AbstractKernel
+{
+ static Logger log = Logger.getLogger(UdpKernel.class.getName());
+
+ private InetSocketAddress address;
+ private HostThread thread;
+
+ private ExecutorService writer;
+
+ // The nature of UDP means that even through a firewall,
+ // a user would have to have a unique address+port since UDP
+ // can't really be NAT'ed.
+ private Map<SocketAddress,UdpEndpoint> socketEndpoints = new ConcurrentHashMap<SocketAddress,UdpEndpoint>();
+
+ public UdpKernel( InetAddress host, int port )
+ {
+ this( new InetSocketAddress(host, port) );
+ }
+
+ public UdpKernel( int port ) throws IOException
+ {
+ this( new InetSocketAddress(port) );
+ }
+
+ public UdpKernel( InetSocketAddress address )
+ {
+ this.address = address;
+ }
+
+ protected HostThread createHostThread()
+ {
+ return new HostThread();
+ }
+
+ public void initialize()
+ {
+ if( thread != null )
+ throw new IllegalStateException( "Kernel already initialized." );
+
+ writer = Executors.newFixedThreadPool(2, new NamedThreadFactory(toString() + "-writer"));
+
+ thread = createHostThread();
+
+ try {
+ thread.connect();
+ thread.start();
+ } catch( IOException e ) {
+ throw new KernelException( "Error hosting:" + address, e );
+ }
+ }
+
+ public void terminate() throws InterruptedException
+ {
+ if( thread == null )
+ throw new IllegalStateException( "Kernel not initialized." );
+
+ try {
+ thread.close();
+ writer.shutdown();
+ thread = null;
+ } catch( IOException e ) {
+ throw new KernelException( "Error closing host connection:" + address, e );
+ }
+ }
+
+ /**
+ * Dispatches the data to all endpoints managed by the
+ * kernel. 'routing' is currently ignored.
+ */
+ public void broadcast( Filter<? super Endpoint> filter, ByteBuffer data, boolean reliable,
+ boolean copy )
+ {
+ if( reliable )
+ throw new UnsupportedOperationException( "Reliable send not supported by this kernel." );
+
+ if( copy ) {
+ // Copy the data just once
+ byte[] temp = new byte[data.remaining()];
+ System.arraycopy(data.array(), data.position(), temp, 0, data.remaining());
+ data = ByteBuffer.wrap(temp);
+ }
+
+ // Hand it to all of the endpoints that match our routing
+ for( UdpEndpoint p : socketEndpoints.values() ) {
+ // Does it match the filter?
+ if( filter != null && !filter.apply(p) )
+ continue;
+
+ // Send the data
+ p.send( data );
+ }
+ }
+
+ protected Endpoint getEndpoint( SocketAddress address, boolean create )
+ {
+ UdpEndpoint p = socketEndpoints.get(address);
+ if( p == null && create ) {
+ p = new UdpEndpoint( this, nextEndpointId(), address, thread.getSocket() );
+ socketEndpoints.put( address, p );
+
+ // Add an event for it.
+ addEvent( EndpointEvent.createAdd( this, p ) );
+ }
+ return p;
+ }
+
+ /**
+ * Called by the endpoints when they need to be closed.
+ */
+ protected void closeEndpoint( UdpEndpoint p ) throws IOException
+ {
+ // Just book-keeping to do here.
+ if( socketEndpoints.remove( p.getRemoteAddress() ) == null )
+ return;
+
+ log.log( Level.INFO, "Closing endpoint:{0}.", p );
+ log.log( Level.FINE, "Socket endpoints size:{0}", socketEndpoints.size() );
+
+ addEvent( EndpointEvent.createRemove( this, p ) );
+
+ // If there are no pending messages then add one so that the
+ // kernel-user knows to wake up if it is only listening for
+ // envelopes.
+ if( !hasEnvelopes() ) {
+ // Note: this is not really a race condition. At worst, our
+ // event has already been handled by now and it does no harm
+ // to check again.
+ addEnvelope( EVENTS_PENDING );
+ }
+ }
+
+ protected void newData( DatagramPacket packet )
+ {
+ // So the tricky part here is figuring out the endpoint and
+ // whether it's new or not. In these UDP schemes, firewalls have
+ // to be ported back to a specific machine so we will consider
+ // the address + port (ie: SocketAddress) the defacto unique
+ // ID.
+ Endpoint p = getEndpoint( packet.getSocketAddress(), true );
+
+ // We'll copy the data to trim it.
+ byte[] data = new byte[packet.getLength()];
+ System.arraycopy(packet.getData(), 0, data, 0, data.length);
+
+ Envelope env = new Envelope( p, data, false );
+ addEnvelope( env );
+ }
+
+ protected void enqueueWrite( Endpoint endpoint, DatagramPacket packet )
+ {
+ writer.execute( new MessageWriter(endpoint, packet) );
+ }
+
+ protected class MessageWriter implements Runnable
+ {
+ private Endpoint endpoint;
+ private DatagramPacket packet;
+
+ public MessageWriter( Endpoint endpoint, DatagramPacket packet )
+ {
+ this.endpoint = endpoint;
+ this.packet = packet;
+ }
+
+ public void run()
+ {
+ // Not guaranteed to always work but an extra datagram
+ // to a dead connection isn't so big of a deal.
+ if( !endpoint.isConnected() ) {
+ return;
+ }
+
+ try {
+ thread.getSocket().send(packet);
+ } catch( Exception e ) {
+ KernelException exc = new KernelException( "Error sending datagram to:" + address, e );
+ exc.fillInStackTrace();
+ reportError(exc);
+ }
+ }
+ }
+
+ protected class HostThread extends Thread
+ {
+ private DatagramSocket socket;
+ private AtomicBoolean go = new AtomicBoolean(true);
+
+ private byte[] buffer = new byte[65535]; // slightly bigger than needed.
+
+ public HostThread()
+ {
+ setName( "UDP Host@" + address );
+ setDaemon(true);
+ }
+
+ protected DatagramSocket getSocket()
+ {
+ return socket;
+ }
+
+ public void connect() throws IOException
+ {
+ socket = new DatagramSocket( address );
+ log.log( Level.INFO, "Hosting UDP connection:{0}.", address );
+ }
+
+ public void close() throws IOException, InterruptedException
+ {
+ // Set the thread to stop
+ go.set(false);
+
+ // Make sure the channel is closed
+ socket.close();
+
+ // And wait for it
+ join();
+ }
+
+ public void run()
+ {
+ log.log( Level.INFO, "Kernel started for connection:{0}.", address );
+
+ // An atomic is safest and costs almost nothing
+ while( go.get() ) {
+ try {
+ // Could reuse the packet but I don't see the
+ // point and it may lead to subtle bugs if not properly
+ // reset.
+ DatagramPacket packet = new DatagramPacket( buffer, buffer.length );
+ socket.receive(packet);
+
+ newData( packet );
+ } catch( IOException e ) {
+ if( !go.get() )
+ return;
+ reportError( e );
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/message/ChannelInfoMessage.java b/engine/src/networking/com/jme3/network/message/ChannelInfoMessage.java
new file mode 100644
index 0000000..91e54bc
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/message/ChannelInfoMessage.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Contains information about any extra server channels (if they exist).
+ *
+ * @author Paul Speed
+ */
+@Serializable()
+public class ChannelInfoMessage extends AbstractMessage {
+ private long id;
+ private int[] ports;
+
+ public ChannelInfoMessage() {
+ super( true );
+ }
+
+ public ChannelInfoMessage( long id, List<Integer> ports ) {
+ super( true );
+ this.id = id;
+ this.ports = new int[ports.size()];
+ for( int i = 0; i < ports.size(); i++ ) {
+ this.ports[i] = ports.get(i);
+ }
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public int[] getPorts() {
+ return ports;
+ }
+
+ public String toString() {
+ return "ChannelInfoMessage[" + id + ", " + Arrays.asList(ports) + "]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/message/ClientRegistrationMessage.java b/engine/src/networking/com/jme3/network/message/ClientRegistrationMessage.java
new file mode 100644
index 0000000..fcbf36b
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/message/ClientRegistrationMessage.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Client registration is a message that contains a unique ID. This ID
+ * is simply the current time in milliseconds, providing multiple clients
+ * will not connect to the same server within one millisecond. This is used
+ * to couple the TCP and UDP connections together into one 'Client' on the
+ * server.
+ *
+ * @author Lars Wesselius
+ */
+@Serializable()
+public class ClientRegistrationMessage extends AbstractMessage {
+ private long id;
+ private String gameName;
+ private int version;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public void setGameName( String name ) {
+ this.gameName = name;
+ }
+
+ public String getGameName() {
+ return gameName;
+ }
+
+ public void setVersion( int version ) {
+ this.version = version;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/message/CompressedMessage.java b/engine/src/networking/com/jme3/network/message/CompressedMessage.java
new file mode 100644
index 0000000..80ac2d0
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/message/CompressedMessage.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.Message;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * CompressedMessage is a base class for all messages that
+ * compress others.
+ *
+ * @author Lars Wesselius
+ */
+@Serializable()
+public class CompressedMessage extends AbstractMessage {
+ private Message message;
+
+ public CompressedMessage() { }
+
+ public CompressedMessage(Message msg) {
+ this.message = msg;
+ }
+
+ public void setMessage(Message message) {
+ this.message = message;
+ }
+
+ public Message getMessage() {
+ return message;
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/message/DisconnectMessage.java b/engine/src/networking/com/jme3/network/message/DisconnectMessage.java
new file mode 100644
index 0000000..ab667c3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/message/DisconnectMessage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Represents a disconnect message.
+ *
+ * @author Lars Wesselius
+ */
+@Serializable()
+public class DisconnectMessage extends AbstractMessage {
+ public static final String KICK = "Kick";
+ public static final String USER_REQUESTED = "User requested";
+ public static final String ERROR = "Error";
+ public static final String FILTERED = "Filtered";
+
+ private String reason;
+ private String type;
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/message/GZIPCompressedMessage.java b/engine/src/networking/com/jme3/network/message/GZIPCompressedMessage.java
new file mode 100644
index 0000000..91af83d
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/message/GZIPCompressedMessage.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.Message;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * GZIPCompressedMessage is the class that you need to use should you want to
+ * compress a message using Gzip.
+ *
+ * @author Lars Wesselius
+ */
+@Serializable()
+public class GZIPCompressedMessage extends CompressedMessage {
+ public GZIPCompressedMessage() {
+ super();
+ }
+
+ public GZIPCompressedMessage(Message msg) {
+ super(msg);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/message/ZIPCompressedMessage.java b/engine/src/networking/com/jme3/network/message/ZIPCompressedMessage.java
new file mode 100644
index 0000000..5dff3e5
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/message/ZIPCompressedMessage.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.Message;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Compress a message using this ZIPCompressedMessage class
+ *
+ * @author Lars Wesselius
+ */
+@Serializable()
+public class ZIPCompressedMessage extends CompressedMessage {
+ private static int compressionLevel = 6;
+
+ public ZIPCompressedMessage() {
+ super();
+ }
+
+ public ZIPCompressedMessage(Message msg) {
+ super(msg);
+ }
+
+ public ZIPCompressedMessage(Message msg, int level) {
+ super(msg);
+ setLevel(level);
+ }
+
+ /**
+ * Set the compression level, where 1 is the best compression but slower and 9 is the weakest
+ * compression but the quickest. Default is 6.
+ *
+ * @param level The level.
+ */
+ public static void setLevel(int level) {
+ compressionLevel = level;
+ }
+
+ public int getLevel() { return compressionLevel; }
+}
diff --git a/engine/src/networking/com/jme3/network/package.html b/engine/src/networking/com/jme3/network/package.html
new file mode 100644
index 0000000..46dfa80
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/package.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+The network package contains the public API for the jME3
+SpiderMonkey networking module. The {@link com.jme3.network.Network}
+class is the entry point for creating default implementations
+of {@link com.jme3.network.Client} and {@link com.jme3.network.Server}
+implementations.
+</body>
+</html>
diff --git a/engine/src/networking/com/jme3/network/rmi/LocalObject.java b/engine/src/networking/com/jme3/network/rmi/LocalObject.java
new file mode 100644
index 0000000..a6477aa
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/LocalObject.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import java.lang.reflect.Method;
+
+/**
+ * Describes a RMI interface on the local machine.
+ *
+ * @author Kirill Vainer
+ */
+public class LocalObject {
+
+ /**
+ * Object name
+ */
+ String objectName;
+
+ /**
+ * The RMI interface implementation
+ */
+ Object theObject;
+
+ /**
+ * Shared Object ID
+ */
+ short objectId;
+
+ /**
+ * Methods exposed by the RMI interface. The "methodID" is used
+ * to look-up methods in this array.
+ */
+ Method[] methods;
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/MethodDef.java b/engine/src/networking/com/jme3/network/rmi/MethodDef.java
new file mode 100644
index 0000000..9164e06
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/MethodDef.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+
+/**
+ * Method definition is used to map methods on an RMI interface
+ * to an implementation on a remote machine.
+ *
+ * @author Kirill Vainer
+ */
+public class MethodDef {
+
+ /**
+ * Method name
+ */
+ public String name;
+
+ /**
+ * Return type
+ */
+ public Class<?> retType;
+
+ /**
+ * Parameter types
+ */
+ public Class<?>[] paramTypes;
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/ObjectDef.java b/engine/src/networking/com/jme3/network/rmi/ObjectDef.java
new file mode 100644
index 0000000..6a338ae
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/ObjectDef.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+
+import com.jme3.network.serializing.Serializable;
+import java.lang.reflect.Method;
+
+@Serializable
+public class ObjectDef {
+
+ /**
+ * The object name, can be null if undefined.
+ */
+ public String objectName;
+
+ /**
+ * Object ID
+ */
+ public int objectId;
+
+ /**
+ * Methods of the implementation on the local client. Set to null
+ * on remote clients.
+ */
+ public Method[] methods;
+
+ /**
+ * Method definitions of the implementation. Set to null on
+ * the local client.
+ */
+ public MethodDef[] methodDefs;
+
+ @Override
+ public String toString(){
+ return "ObjectDef[name=" + objectName + ", objectId=" + objectId+"]";
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/ObjectStore.java b/engine/src/networking/com/jme3/network/rmi/ObjectStore.java
new file mode 100644
index 0000000..137320d
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/ObjectStore.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.ClientStateListener.DisconnectInfo;
+import com.jme3.network.*;
+import com.jme3.network.serializing.Serializer;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ObjectStore {
+
+ private static final Logger logger = Logger.getLogger(ObjectStore.class.getName());
+
+ private static final class Invocation {
+
+ Object retVal;
+ boolean available = false;
+
+ @Override
+ public String toString(){
+ return "Invocation[" + retVal + "]";
+ }
+ }
+
+ private Client client;
+ private Server server;
+
+ private ClientEventHandler clientEventHandler = new ClientEventHandler();
+ private ServerEventHandler serverEventHandler = new ServerEventHandler();
+
+ // Local object ID counter
+ private volatile short objectIdCounter = 0;
+
+ // Local invocation ID counter
+ private volatile short invocationIdCounter = 0;
+
+ // Invocations waiting ..
+ private IntMap<Invocation> pendingInvocations = new IntMap<Invocation>();
+
+ // Objects I share with other people
+ private IntMap<LocalObject> localObjects = new IntMap<LocalObject>();
+
+ // Objects others share with me
+ private HashMap<String, RemoteObject> remoteObjects = new HashMap<String, RemoteObject>();
+ private IntMap<RemoteObject> remoteObjectsById = new IntMap<RemoteObject>();
+
+ private final Object receiveObjectLock = new Object();
+
+ public class ServerEventHandler implements MessageListener<HostedConnection>,
+ ConnectionListener {
+
+ public void messageReceived(HostedConnection source, Message m) {
+ onMessage(source, m);
+ }
+
+ public void connectionAdded(Server server, HostedConnection conn) {
+ onConnection(conn);
+ }
+
+ public void connectionRemoved(Server server, HostedConnection conn) {
+ }
+
+ }
+
+ public class ClientEventHandler implements MessageListener,
+ ClientStateListener {
+
+ public void messageReceived(Object source, Message m) {
+ onMessage(null, m);
+ }
+
+ public void clientConnected(Client c) {
+ onConnection(null);
+ }
+
+ public void clientDisconnected(Client c, DisconnectInfo info) {
+ }
+
+ }
+
+ static {
+ Serializer s = new RmiSerializer();
+ Serializer.registerClass(RemoteObjectDefMessage.class, s);
+ Serializer.registerClass(RemoteMethodCallMessage.class, s);
+ Serializer.registerClass(RemoteMethodReturnMessage.class, s);
+ }
+
+ public ObjectStore(Client client) {
+ this.client = client;
+ client.addMessageListener(clientEventHandler,
+ RemoteObjectDefMessage.class,
+ RemoteMethodCallMessage.class,
+ RemoteMethodReturnMessage.class);
+ client.addClientStateListener(clientEventHandler);
+ }
+
+ public ObjectStore(Server server) {
+ this.server = server;
+ server.addMessageListener(serverEventHandler,
+ RemoteObjectDefMessage.class,
+ RemoteMethodCallMessage.class,
+ RemoteMethodReturnMessage.class);
+ server.addConnectionListener(serverEventHandler);
+ }
+
+ private ObjectDef makeObjectDef(LocalObject localObj){
+ ObjectDef def = new ObjectDef();
+ def.objectName = localObj.objectName;
+ def.objectId = localObj.objectId;
+ def.methods = localObj.methods;
+ return def;
+ }
+
+ public void exposeObject(String name, Object obj) throws IOException{
+ // Create a local object
+ LocalObject localObj = new LocalObject();
+ localObj.objectName = name;
+ localObj.objectId = objectIdCounter++;
+ localObj.theObject = obj;
+ //localObj.methods = obj.getClass().getMethods();
+
+ ArrayList<Method> methodList = new ArrayList<Method>();
+ for (Method method : obj.getClass().getMethods()){
+ if (method.getDeclaringClass() == obj.getClass()){
+ methodList.add(method);
+ }
+ }
+ localObj.methods = methodList.toArray(new Method[methodList.size()]);
+
+ // Put it in the store
+ localObjects.put(localObj.objectId, localObj);
+
+ // Inform the others of its existence
+ RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
+ defMsg.objects = new ObjectDef[]{ makeObjectDef(localObj) };
+
+ if (client != null) {
+ client.send(defMsg);
+ logger.log(Level.INFO, "Client: Sending {0}", defMsg);
+ } else {
+ server.broadcast(defMsg);
+ logger.log(Level.INFO, "Server: Sending {0}", defMsg);
+ }
+ }
+
+ public <T> T getExposedObject(String name, Class<T> type, boolean waitFor) throws InterruptedException{
+ RemoteObject ro = remoteObjects.get(name);
+ if (ro == null){
+ if (!waitFor)
+ throw new RuntimeException("Cannot find remote object named: " + name);
+ else{
+ do {
+ synchronized (receiveObjectLock){
+ receiveObjectLock.wait();
+ }
+ } while ( (ro = remoteObjects.get(name)) == null );
+ }
+ }
+
+ Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ type }, ro);
+ ro.loadMethods(type);
+ return (T) proxy;
+ }
+
+ Object invokeRemoteMethod(RemoteObject remoteObj, Method method, Object[] args){
+ Integer methodIdInt = remoteObj.methodMap.get(method);
+ if (methodIdInt == null)
+ throw new RuntimeException("Method not implemented by remote object owner: "+method);
+
+ boolean needReturn = method.getReturnType() != void.class;
+ short objectId = remoteObj.objectId;
+ short methodId = methodIdInt.shortValue();
+ RemoteMethodCallMessage call = new RemoteMethodCallMessage();
+ call.methodId = methodId;
+ call.objectId = objectId;
+ call.args = args;
+
+ Invocation invoke = null;
+ if (needReturn){
+ call.invocationId = invocationIdCounter++;
+ invoke = new Invocation();
+ // Note: could cause threading issues if used from multiple threads
+ pendingInvocations.put(call.invocationId, invoke);
+ }
+
+ if (server != null){
+ remoteObj.client.send(call);
+ logger.log(Level.INFO, "Server: Sending {0}", call);
+ }else{
+ client.send(call);
+ logger.log(Level.INFO, "Client: Sending {0}", call);
+ }
+
+ if (invoke != null){
+ synchronized(invoke){
+ while (!invoke.available){
+ try {
+ invoke.wait();
+ } catch (InterruptedException ex){
+ ex.printStackTrace();
+ }
+ }
+ }
+ // Note: could cause threading issues if used from multiple threads
+ pendingInvocations.remove(call.invocationId);
+ return invoke.retVal;
+ }else{
+ return null;
+ }
+ }
+
+ private void onMessage(HostedConnection source, Message message) {
+ // Might want to do more strict validation of the data
+ // in the message to prevent crashes
+
+ if (message instanceof RemoteObjectDefMessage){
+ RemoteObjectDefMessage defMsg = (RemoteObjectDefMessage) message;
+
+ ObjectDef[] defs = defMsg.objects;
+ for (ObjectDef def : defs){
+ RemoteObject remoteObject = new RemoteObject(this, source);
+ remoteObject.objectId = (short)def.objectId;
+ remoteObject.methodDefs = def.methodDefs;
+ remoteObjects.put(def.objectName, remoteObject);
+ remoteObjectsById.put(def.objectId, remoteObject);
+ }
+
+ synchronized (receiveObjectLock){
+ receiveObjectLock.notifyAll();
+ }
+ }else if (message instanceof RemoteMethodCallMessage){
+ RemoteMethodCallMessage call = (RemoteMethodCallMessage) message;
+ LocalObject localObj = localObjects.get(call.objectId);
+ if (localObj == null)
+ return;
+
+ if (call.methodId < 0 || call.methodId >= localObj.methods.length)
+ return;
+
+ Object obj = localObj.theObject;
+ Method method = localObj.methods[call.methodId];
+ Object[] args = call.args;
+ Object ret = null;
+ try {
+ ret = method.invoke(obj, args);
+ } catch (IllegalAccessException ex){
+ logger.log(Level.WARNING, "RMI: Error accessing method", ex);
+ } catch (IllegalArgumentException ex){
+ logger.log(Level.WARNING, "RMI: Invalid arguments", ex);
+ } catch (InvocationTargetException ex){
+ logger.log(Level.WARNING, "RMI: Invocation exception", ex);
+ }
+
+ if (method.getReturnType() != void.class){
+ // send return value back
+ RemoteMethodReturnMessage retMsg = new RemoteMethodReturnMessage();
+ retMsg.invocationID = call.invocationId;
+ retMsg.retVal = ret;
+ if (server != null){
+ source.send(retMsg);
+ logger.log(Level.INFO, "Server: Sending {0}", retMsg);
+ } else{
+ client.send(retMsg);
+ logger.log(Level.INFO, "Client: Sending {0}", retMsg);
+ }
+ }
+ }else if (message instanceof RemoteMethodReturnMessage){
+ RemoteMethodReturnMessage retMsg = (RemoteMethodReturnMessage) message;
+ Invocation invoke = pendingInvocations.get(retMsg.invocationID);
+ if (invoke == null){
+ logger.log(Level.WARNING, "Cannot find invocation ID: {0}", retMsg.invocationID);
+ return;
+ }
+
+ synchronized (invoke){
+ invoke.retVal = retMsg.retVal;
+ invoke.available = true;
+ invoke.notifyAll();
+ }
+ }
+ }
+
+ private void onConnection(HostedConnection conn) {
+ if (localObjects.size() > 0){
+ // send a object definition message
+ ObjectDef[] defs = new ObjectDef[localObjects.size()];
+ int i = 0;
+ for (Entry<LocalObject> entry : localObjects){
+ defs[i] = makeObjectDef(entry.getValue());
+ i++;
+ }
+
+ RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
+ defMsg.objects = defs;
+ if (this.client != null){
+ this.client.send(defMsg);
+ logger.log(Level.INFO, "Client: Sending {0}", defMsg);
+ } else{
+ conn.send(defMsg);
+ logger.log(Level.INFO, "Server: Sending {0}", defMsg);
+ }
+ }
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/RemoteMethodCallMessage.java b/engine/src/networking/com/jme3/network/rmi/RemoteMethodCallMessage.java
new file mode 100644
index 0000000..6870ec4
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/RemoteMethodCallMessage.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Sent to a remote client to make a remote method invocation.
+ *
+ * @author Kirill Vainer
+ */
+@Serializable
+public class RemoteMethodCallMessage extends AbstractMessage {
+
+ public RemoteMethodCallMessage(){
+ super(true);
+ }
+
+ /**
+ * The object ID on which the call is being made.
+ */
+ public int objectId;
+
+ /**
+ * The method ID used for look-up in the LocalObject.methods array.
+ */
+ public short methodId;
+
+ /**
+ * Invocation ID is used to identify a particular call if the calling
+ * client needs the return value of the called RMI method.
+ * This is set to zero if the method does not return a value.
+ */
+ public short invocationId;
+
+ /**
+ * Arguments of the remote method invocation.
+ */
+ public Object[] args;
+
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ sb.append("RemoteMethodCallMessage[objectID=").append(objectId).append(", methodID=")
+ .append(methodId);
+ if (args != null && args.length > 0){
+ sb.append(", args={");
+ for (Object arg : args){
+ sb.append(arg.toString()).append(", ");
+ }
+ sb.setLength(sb.length()-2);
+ sb.append("}");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/RemoteMethodReturnMessage.java b/engine/src/networking/com/jme3/network/rmi/RemoteMethodReturnMessage.java
new file mode 100644
index 0000000..b95f5c8
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/RemoteMethodReturnMessage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Contains the return value for a remote method invocation, sent as a response
+ * to a {@link RemoteMethodCallMessage} with a non-zero invocationID.
+ *
+ * @author Kirill Vainer.
+ */
+@Serializable
+public class RemoteMethodReturnMessage extends AbstractMessage {
+
+ public RemoteMethodReturnMessage(){
+ super(true);
+ }
+
+ /**
+ * Invocation ID that was set in the {@link RemoteMethodCallMessage}.
+ */
+ public short invocationID;
+
+ /**
+ * The return value, could be null.
+ */
+ public Object retVal;
+
+
+ @Override
+ public String toString(){
+ return "RemoteMethodReturnMessage[ID="+invocationID+", Value="+retVal.toString()+"]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/RemoteObject.java b/engine/src/networking/com/jme3/network/rmi/RemoteObject.java
new file mode 100644
index 0000000..24a9463
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/RemoteObject.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.HostedConnection;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Contains various meta-data about an RMI interface.
+ *
+ * @author Kirill Vainer
+ */
+public class RemoteObject implements InvocationHandler {
+
+ /**
+ * Object ID
+ */
+ short objectId;
+
+ /**
+ * Contains {@link MethodDef method definitions} for all exposed
+ * RMI methods in the remote RMI interface.
+ */
+ MethodDef[] methodDefs;
+
+ /**
+ * Maps from methods locally retrieved from the RMI interface to
+ * a method ID.
+ */
+ HashMap<Method, Integer> methodMap = new HashMap<Method, Integer>();
+
+ /**
+ * The {@link ObjectStore} which stores this RMI interface.
+ */
+ ObjectStore store;
+
+ /**
+ * The client who exposed the RMI interface, or null if the server
+ * exposed it.
+ */
+ HostedConnection client;
+
+ public RemoteObject(ObjectStore store, HostedConnection client){
+ this.store = store;
+ this.client = client;
+ }
+
+ private boolean methodEquals(MethodDef methodDef, Method method){
+ Class<?>[] interfaceTypes = method.getParameterTypes();
+ Class<?>[] defTypes = methodDef.paramTypes;
+
+ if (interfaceTypes.length == defTypes.length){
+ for (int i = 0; i < interfaceTypes.length; i++){
+ if (!defTypes[i].isAssignableFrom(interfaceTypes[i])){
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Generates mappings from the given interface into the remote RMI
+ * interface's implementation.
+ *
+ * @param interfaceClass The interface class to use.
+ */
+ public void loadMethods(Class<?> interfaceClass){
+ HashMap<String, ArrayList<Method>> nameToMethods
+ = new HashMap<String, ArrayList<Method>>();
+
+ for (Method method : interfaceClass.getDeclaredMethods()){
+ ArrayList<Method> list = nameToMethods.get(method.getName());
+ if (list == null){
+ list = new ArrayList<Method>();
+ nameToMethods.put(method.getName(), list);
+ }
+ list.add(method);
+ }
+
+ mapping_search: for (int i = 0; i < methodDefs.length; i++){
+ MethodDef methodDef = methodDefs[i];
+ ArrayList<Method> methods = nameToMethods.get(methodDef.name);
+ if (methods == null)
+ continue;
+
+ for (Method method : methods){
+ if (methodEquals(methodDef, method)){
+ methodMap.put(method, i);
+ continue mapping_search;
+ }
+ }
+ }
+ }
+
+ /**
+ * Callback from InvocationHandler.
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return store.invokeRemoteMethod(this, method, args);
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/RemoteObjectDefMessage.java b/engine/src/networking/com/jme3/network/rmi/RemoteObjectDefMessage.java
new file mode 100644
index 0000000..b4639ae
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/RemoteObjectDefMessage.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+
+/**
+ * Sent to expose RMI interfaces on the local client to other clients.
+ * @author Kirill Vainer
+ */
+@Serializable
+public class RemoteObjectDefMessage extends AbstractMessage {
+
+ public ObjectDef[] objects;
+
+ public RemoteObjectDefMessage(){
+ super(true);
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ sb.append("RemoteObjectDefMessage[\n");
+ for (ObjectDef def : objects){
+ sb.append("\t").append(def).append("\n");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/rmi/RmiSerializer.java b/engine/src/networking/com/jme3/network/rmi/RmiSerializer.java
new file mode 100644
index 0000000..76709dc
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/RmiSerializer.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerRegistration;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * {@link RmiSerializer} is responsible for serializing RMI messages
+ * like define object, call, and return.
+ *
+ * @author Kirill Vainer
+ */
+public class RmiSerializer extends Serializer {
+
+ private static final Logger logger = Logger.getLogger(RmiSerializer.class.getName());
+
+ // not good for multithread applications
+ private char[] chrBuf = new char[256];
+
+ private void writeString(ByteBuffer buffer, String string) throws IOException{
+ int length = string.length();
+ if (length > 255){
+ logger.log(Level.WARNING, "The string length exceeds the limit! {0} > 255", length);
+ buffer.put( (byte) 0 );
+ return;
+ }
+
+ buffer.put( (byte) length );
+ for (int i = 0; i < length; i++){
+ buffer.put( (byte) string.charAt(i) );
+ }
+ }
+
+ private String readString(ByteBuffer buffer){
+ int length = buffer.get() & 0xff;
+ for (int i = 0; i < length; i++){
+ chrBuf[i] = (char) (buffer.get() & 0xff);
+ }
+ return String.valueOf(chrBuf, 0, length);
+ }
+
+ private void writeType(ByteBuffer buffer, Class<?> clazz) throws IOException{
+ if (clazz == void.class){
+ buffer.putShort((short)0);
+ } else {
+ SerializerRegistration reg = Serializer.getSerializerRegistration(clazz);
+ if (reg == null){
+ logger.log(Level.WARNING, "Unknown class: {0}", clazz);
+ throw new IOException(); // prevents message from being serialized
+ }
+ buffer.putShort(reg.getId());
+ }
+ }
+
+ private Class<?> readType(ByteBuffer buffer) throws IOException{
+ SerializerRegistration reg = Serializer.readClass(buffer);
+ if (reg == null){
+ // either "void" or unknown val
+ short id = buffer.getShort(buffer.position()-2);
+ if (id == 0){
+ return void.class;
+ } else{
+ logger.log(Level.WARNING, "Undefined class ID: {0}", id);
+ throw new IOException(); // prevents message from being serialized
+ }
+ }
+ return reg.getType();
+ }
+
+ private void writeMethod(ByteBuffer buffer, Method method) throws IOException{
+ String name = method.getName();
+ Class<?>[] paramTypes = method.getParameterTypes();
+ Class<?> returnType = method.getReturnType();
+
+ writeString(buffer, name);
+ writeType(buffer, returnType);
+ buffer.put((byte)paramTypes.length);
+ for (Class<?> paramType : paramTypes)
+ writeType(buffer, paramType);
+ }
+
+ private MethodDef readMethod(ByteBuffer buffer) throws IOException{
+ String name = readString(buffer);
+ Class<?> retType = readType(buffer);
+
+ int numParams = buffer.get() & 0xff;
+ Class<?>[] paramTypes = new Class<?>[numParams];
+ for (int i = 0; i < numParams; i++){
+ paramTypes[i] = readType(buffer);
+ }
+
+ MethodDef def = new MethodDef();
+ def.name = name;
+ def.paramTypes = paramTypes;
+ def.retType = retType;
+ return def;
+ }
+
+ private void writeObjectDef(ByteBuffer buffer, ObjectDef def) throws IOException{
+ buffer.putShort((short)def.objectId);
+ writeString(buffer, def.objectName);
+ Method[] methods = def.methods;
+ buffer.put( (byte) methods.length );
+ for (Method method : methods){
+ writeMethod(buffer, method);
+ }
+ }
+
+ private ObjectDef readObjectDef(ByteBuffer buffer) throws IOException{
+ ObjectDef def = new ObjectDef();
+
+ def.objectId = buffer.getShort();
+ def.objectName = readString(buffer);
+
+ int numMethods = buffer.get() & 0xff;
+ MethodDef[] methodDefs = new MethodDef[numMethods];
+ for (int i = 0; i < numMethods; i++){
+ methodDefs[i] = readMethod(buffer);
+ }
+ def.methodDefs = methodDefs;
+ return def;
+ }
+
+ private void writeObjectDefs(ByteBuffer buffer, RemoteObjectDefMessage defMsg) throws IOException{
+ ObjectDef[] defs = defMsg.objects;
+ buffer.put( (byte) defs.length );
+ for (ObjectDef def : defs)
+ writeObjectDef(buffer, def);
+ }
+
+ private RemoteObjectDefMessage readObjectDefs(ByteBuffer buffer) throws IOException{
+ RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
+ int numObjs = buffer.get() & 0xff;
+ ObjectDef[] defs = new ObjectDef[numObjs];
+ for (int i = 0; i < numObjs; i++){
+ defs[i] = readObjectDef(buffer);
+ }
+ defMsg.objects = defs;
+ return defMsg;
+ }
+
+ private void writeMethodCall(ByteBuffer buffer, RemoteMethodCallMessage call) throws IOException{
+ buffer.putShort((short)call.objectId);
+ buffer.putShort(call.methodId);
+ buffer.putShort(call.invocationId);
+ if (call.args == null){
+ buffer.put((byte)0);
+ }else{
+ buffer.put((byte)call.args.length);
+
+ // Right now it writes 0 for every null argument
+ // and 1 for every non-null argument followed by the serialized
+ // argument. For the future, using a bit set should be considered.
+ for (Object obj : call.args){
+ if (obj != null){
+ buffer.put((byte)0x01);
+ Serializer.writeClassAndObject(buffer, obj);
+ }else{
+ buffer.put((byte)0x00);
+ }
+ }
+ }
+ }
+
+ private RemoteMethodCallMessage readMethodCall(ByteBuffer buffer) throws IOException{
+ RemoteMethodCallMessage call = new RemoteMethodCallMessage();
+ call.objectId = buffer.getShort();
+ call.methodId = buffer.getShort();
+ call.invocationId = buffer.getShort();
+ int numArgs = buffer.get() & 0xff;
+ if (numArgs > 0){
+ Object[] args = new Object[numArgs];
+ for (int i = 0; i < numArgs; i++){
+ if (buffer.get() == (byte)0x01){
+ args[i] = Serializer.readClassAndObject(buffer);
+ }
+ }
+ call.args = args;
+ }
+ return call;
+ }
+
+ private void writeMethodReturn(ByteBuffer buffer, RemoteMethodReturnMessage ret) throws IOException{
+ buffer.putShort(ret.invocationID);
+ if (ret.retVal != null){
+ buffer.put((byte)0x01);
+ Serializer.writeClassAndObject(buffer, ret.retVal);
+ }else{
+ buffer.put((byte)0x00);
+ }
+ }
+
+ private RemoteMethodReturnMessage readMethodReturn(ByteBuffer buffer) throws IOException{
+ RemoteMethodReturnMessage ret = new RemoteMethodReturnMessage();
+ ret.invocationID = buffer.getShort();
+ if (buffer.get() == (byte)0x01){
+ ret.retVal = Serializer.readClassAndObject(buffer);
+ }
+ return ret;
+ }
+
+ @Override
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ if (c == RemoteObjectDefMessage.class){
+ return (T) readObjectDefs(data);
+ }else if (c == RemoteMethodCallMessage.class){
+ return (T) readMethodCall(data);
+ }else if (c == RemoteMethodReturnMessage.class){
+ return (T) readMethodReturn(data);
+ }
+ return null;
+ }
+
+ @Override
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+// int p = buffer.position();
+ if (object instanceof RemoteObjectDefMessage){
+ RemoteObjectDefMessage def = (RemoteObjectDefMessage) object;
+ writeObjectDefs(buffer, def);
+ }else if (object instanceof RemoteMethodCallMessage){
+ RemoteMethodCallMessage call = (RemoteMethodCallMessage) object;
+ writeMethodCall(buffer, call);
+ }else if (object instanceof RemoteMethodReturnMessage){
+ RemoteMethodReturnMessage ret = (RemoteMethodReturnMessage) object;
+ writeMethodReturn(buffer, ret);
+ }
+// p = buffer.position() - p;
+// System.out.println(object+": uses " + p + " bytes");
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/Serializable.java b/engine/src/networking/com/jme3/network/serializing/Serializable.java
new file mode 100644
index 0000000..751084e
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/Serializable.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing;
+
+import com.jme3.network.serializing.serializers.FieldSerializer;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Use this annotation when a class is going to be transferred
+ * over the network.
+ *
+ * @author Lars Wesselius
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Serializable {
+ Class serializer() default FieldSerializer.class;
+ short id() default 0;
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/Serializer.java b/engine/src/networking/com/jme3/network/serializing/Serializer.java
new file mode 100644
index 0000000..db87b54
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/Serializer.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing;
+
+import com.jme3.math.Vector3f;
+import com.jme3.network.message.ChannelInfoMessage;
+import com.jme3.network.message.ClientRegistrationMessage;
+import com.jme3.network.message.DisconnectMessage;
+import com.jme3.network.message.GZIPCompressedMessage;
+import com.jme3.network.message.ZIPCompressedMessage;
+import com.jme3.network.serializing.serializers.*;
+import java.beans.beancontext.BeanContextServicesSupport;
+import java.beans.beancontext.BeanContextSupport;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The main serializer class, which will serialize objects such that
+ * they can be sent across the network. Serializing classes should extend
+ * this to provide their own serialization.
+ *
+ * @author Lars Wesselius
+ */
+public abstract class Serializer {
+ protected static final Logger log = Logger.getLogger(Serializer.class.getName());
+
+ private static final SerializerRegistration NULL_CLASS = new SerializerRegistration( null, Void.class, (short)-1 );
+
+ private static final Map<Short, SerializerRegistration> idRegistrations = new HashMap<Short, SerializerRegistration>();
+ private static final Map<Class, SerializerRegistration> classRegistrations = new HashMap<Class, SerializerRegistration>();
+
+ private static final Serializer fieldSerializer = new FieldSerializer();
+ private static final Serializer serializableSerializer = new SerializableSerializer();
+ private static final Serializer arraySerializer = new ArraySerializer();
+
+ private static short nextId = -1;
+
+ private static boolean strictRegistration = true;
+
+ /****************************************************************
+ ****************************************************************
+ ****************************************************************
+
+ READ THIS BEFORE CHANGING ANYTHING BELOW
+
+ If a registration is moved or removed before the
+ ClientRegistrationMessage then it screws up the application's
+ ability to gracefully warn users about bad versions.
+
+ There really needs to be a version rolled into the protocol
+ and I intend to do that very soon. In the mean time, don't
+ edit the static registrations without decrementing nextId
+ appropriately.
+
+ Yes, that's how fragile this is. Live and learn.
+
+ ****************************************************************
+ ****************************************************************
+ ****************************************************************/
+
+
+ // Registers the classes we already have serializers for.
+ static {
+ registerClass(boolean.class, new BooleanSerializer());
+ registerClass(byte.class, new ByteSerializer());
+ registerClass(char.class, new CharSerializer());
+ registerClass(short.class, new ShortSerializer());
+ registerClass(int.class, new IntSerializer());
+ registerClass(long.class, new LongSerializer());
+ registerClass(float.class, new FloatSerializer());
+ registerClass(double.class, new DoubleSerializer());
+
+ registerClass(Boolean.class, new BooleanSerializer());
+ registerClass(Byte.class, new ByteSerializer());
+ registerClass(Character.class, new CharSerializer());
+ registerClass(Short.class, new ShortSerializer());
+ registerClass(Integer.class, new IntSerializer());
+ registerClass(Long.class, new LongSerializer());
+ registerClass(Float.class, new FloatSerializer());
+ registerClass(Double.class, new DoubleSerializer());
+ registerClass(String.class, new StringSerializer());
+
+ registerClass(Vector3f.class, new Vector3Serializer());
+
+ registerClass(Date.class, new DateSerializer());
+
+ // all the Collection classes go here
+ registerClass(AbstractCollection.class, new CollectionSerializer());
+ registerClass(AbstractList.class, new CollectionSerializer());
+ registerClass(AbstractSet.class, new CollectionSerializer());
+ registerClass(ArrayList.class, new CollectionSerializer());
+ registerClass(BeanContextServicesSupport.class, new CollectionSerializer());
+ registerClass(BeanContextSupport.class, new CollectionSerializer());
+ registerClass(HashSet.class, new CollectionSerializer());
+ registerClass(LinkedHashSet.class, new CollectionSerializer());
+ registerClass(LinkedList.class, new CollectionSerializer());
+ registerClass(TreeSet.class, new CollectionSerializer());
+ registerClass(Vector.class, new CollectionSerializer());
+
+ // All the Map classes go here
+ registerClass(AbstractMap.class, new MapSerializer());
+ registerClass(Attributes.class, new MapSerializer());
+ registerClass(HashMap.class, new MapSerializer());
+ registerClass(Hashtable.class, new MapSerializer());
+ registerClass(IdentityHashMap.class, new MapSerializer());
+ registerClass(TreeMap.class, new MapSerializer());
+ registerClass(WeakHashMap.class, new MapSerializer());
+
+ registerClass(Enum.class, new EnumSerializer());
+ registerClass(GZIPCompressedMessage.class, new GZIPSerializer());
+ registerClass(ZIPCompressedMessage.class, new ZIPSerializer());
+
+ registerClass(DisconnectMessage.class);
+ registerClass(ClientRegistrationMessage.class);
+ registerClass(ChannelInfoMessage.class);
+ }
+
+ /**
+ * When set to true, classes that do not have intrinsic IDs in their
+ * @Serializable will not be auto-registered during write. Defaults
+ * to true since this is almost never desired behavior with the way
+ * this code works. Set to false to get the old permissive behavior.
+ */
+ public static void setStrictRegistration( boolean b ) {
+ strictRegistration = b;
+ }
+
+ public static SerializerRegistration registerClass(Class cls) {
+ return registerClass(cls, true);
+ }
+
+ public static void registerClasses(Class... classes) {
+ for( Class c : classes ) {
+ registerClass(c);
+ }
+ }
+
+ /**
+ * Registers the specified class. The failOnMiss flag controls whether or
+ * not this method returns null for failed registration or throws an exception.
+ */
+ @SuppressWarnings("unchecked")
+ public static SerializerRegistration registerClass(Class cls, boolean failOnMiss) {
+ if (cls.isAnnotationPresent(Serializable.class)) {
+ Serializable serializable = (Serializable)cls.getAnnotation(Serializable.class);
+
+ Class serializerClass = serializable.serializer();
+ short classId = serializable.id();
+ if (classId == 0) classId = --nextId;
+
+ Serializer serializer = getSerializer(serializerClass, false);
+
+ if (serializer == null) serializer = fieldSerializer;
+
+ SerializerRegistration existingReg = getExactSerializerRegistration(cls);
+
+ if (existingReg != null) classId = existingReg.getId();
+ SerializerRegistration reg = new SerializerRegistration(serializer, cls, classId);
+
+ idRegistrations.put(classId, reg);
+ classRegistrations.put(cls, reg);
+
+ serializer.initialize(cls);
+
+ log.log( Level.INFO, "Registered class[" + classId + "]:{0}.", cls );
+ return reg;
+ }
+ if (failOnMiss) {
+ throw new IllegalArgumentException( "Class is not marked @Serializable:" + cls );
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated This cannot be implemented in a reasonable way that works in
+ * all deployment methods.
+ */
+ @Deprecated
+ public static SerializerRegistration[] registerPackage(String pkgName) {
+ try {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ String path = pkgName.replace('.', '/');
+ Enumeration<URL> resources = classLoader.getResources(path);
+ List<File> dirs = new ArrayList<File>();
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ dirs.add(new File(resource.getFile()));
+ }
+ ArrayList<Class> classes = new ArrayList<Class>();
+ for (File directory : dirs) {
+ classes.addAll(findClasses(directory, pkgName));
+ }
+
+ SerializerRegistration[] registeredClasses = new SerializerRegistration[classes.size()];
+ for (int i = 0; i != classes.size(); ++i) {
+ Class clz = classes.get(i);
+ registeredClasses[i] = registerClass(clz, false);
+ }
+ return registeredClasses;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return new SerializerRegistration[0];
+ }
+
+ private static List<Class> findClasses(File dir, String pkgName) throws ClassNotFoundException {
+ List<Class> classes = new ArrayList<Class>();
+ if (!dir.exists()) {
+ return classes;
+ }
+ File[] files = dir.listFiles();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ assert !file.getName().contains(".");
+ classes.addAll(findClasses(file, pkgName + "." + file.getName()));
+ } else if (file.getName().endsWith(".class")) {
+ classes.add(Class.forName(pkgName + '.' + file.getName().substring(0, file.getName().length() - 6)));
+ }
+ }
+ return classes;
+ }
+
+ public static SerializerRegistration registerClass(Class cls, Serializer serializer) {
+ SerializerRegistration existingReg = getExactSerializerRegistration(cls);
+
+ short id;
+ if (existingReg != null) {
+ id = existingReg.getId();
+ } else {
+ id = --nextId;
+ }
+ SerializerRegistration reg = new SerializerRegistration(serializer, cls, id);
+
+ idRegistrations.put(id, reg);
+ classRegistrations.put(cls, reg);
+
+ log.log( Level.INFO, "Registered class[" + id + "]:{0} to:" + serializer, cls );
+
+ serializer.initialize(cls);
+
+ return reg;
+ }
+
+ public static Serializer getExactSerializer(Class cls) {
+ return classRegistrations.get(cls).getSerializer();
+ }
+
+ public static Serializer getSerializer(Class cls) {
+ return getSerializer(cls, true);
+ }
+
+ public static Serializer getSerializer(Class cls, boolean failOnMiss) {
+ return getSerializerRegistration(cls, failOnMiss).getSerializer();
+ }
+
+ public static SerializerRegistration getExactSerializerRegistration(Class cls) {
+ return classRegistrations.get(cls);
+ }
+
+ public static SerializerRegistration getSerializerRegistration(Class cls) {
+ return getSerializerRegistration(cls, strictRegistration);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static SerializerRegistration getSerializerRegistration(Class cls, boolean failOnMiss) {
+ SerializerRegistration reg = classRegistrations.get(cls);
+
+ if (reg != null) return reg;
+
+ for (Map.Entry<Class, SerializerRegistration> entry : classRegistrations.entrySet()) {
+ if (entry.getKey().isAssignableFrom(Serializable.class)) continue;
+ if (entry.getKey().isAssignableFrom(cls)) return entry.getValue();
+ }
+
+ if (cls.isArray()) return registerClass(cls, arraySerializer);
+
+ if (Serializable.class.isAssignableFrom(cls)) {
+ return getExactSerializerRegistration(java.io.Serializable.class);
+ }
+
+ // See if the class could be safely auto-registered
+ if (cls.isAnnotationPresent(Serializable.class)) {
+ Serializable serializable = (Serializable)cls.getAnnotation(Serializable.class);
+ short classId = serializable.id();
+ if( classId != 0 ) {
+ // No reason to fail because the ID is fixed
+ failOnMiss = false;
+ }
+ }
+
+ if( failOnMiss ) {
+ throw new IllegalArgumentException( "Class has not been registered:" + cls );
+ }
+ return registerClass(cls, fieldSerializer);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Read the class from given buffer and return its SerializerRegistration.
+ *
+ * @param buffer The buffer to read from.
+ * @return The SerializerRegistration, or null if non-existent.
+ */
+ public static SerializerRegistration readClass(ByteBuffer buffer) {
+ short classID = buffer.getShort();
+ if (classID == -1) return NULL_CLASS;
+ return idRegistrations.get(classID);
+ }
+
+ /**
+ * Read the class and the object.
+ *
+ * @param buffer Buffer to read from.
+ * @return The Object that was read.
+ * @throws IOException If serialization failed.
+ */
+ @SuppressWarnings("unchecked")
+ public static Object readClassAndObject(ByteBuffer buffer) throws IOException {
+ SerializerRegistration reg = readClass(buffer);
+ if (reg == NULL_CLASS) return null;
+ if (reg == null) throw new SerializerException( "Class not found for buffer data." );
+ return reg.getSerializer().readObject(buffer, reg.getType());
+ }
+
+ /**
+ * Write a class and return its SerializerRegistration.
+ *
+ * @param buffer The buffer to write the given class to.
+ * @param type The class to write.
+ * @return The SerializerRegistration that's registered to the class.
+ */
+ public static SerializerRegistration writeClass(ByteBuffer buffer, Class type) throws IOException {
+ SerializerRegistration reg = getSerializerRegistration(type);
+ if (reg == null) {
+ throw new SerializerException( "Class not registered:" + type );
+ }
+ buffer.putShort(reg.getId());
+ return reg;
+ }
+
+ /**
+ * Write the class and object.
+ *
+ * @param buffer The buffer to write to.
+ * @param object The object to write.
+ * @throws IOException If serializing fails.
+ */
+ public static void writeClassAndObject(ByteBuffer buffer, Object object) throws IOException {
+ if (object == null) {
+ buffer.putShort((short)-1);
+ return;
+ }
+ SerializerRegistration reg = writeClass(buffer, object.getClass());
+ reg.getSerializer().writeObject(buffer, object);
+ }
+
+ /**
+ * Read an object from the buffer, effectively deserializing it.
+ *
+ * @param data The buffer to read from.
+ * @param c The class of the object.
+ * @return The object read.
+ * @throws IOException If deserializing fails.
+ */
+ public abstract <T> T readObject(ByteBuffer data, Class<T> c) throws IOException;
+
+ /**
+ * Write an object to the buffer, effectively serializing it.
+ *
+ * @param buffer The buffer to write to.
+ * @param object The object to serialize.
+ * @throws IOException If serializing fails.
+ */
+ public abstract void writeObject(ByteBuffer buffer, Object object) throws IOException;
+
+ /**
+ * Registration for when a serializer may need to cache something.
+ *
+ * Override to use.
+ *
+ * @param clazz The class that has been registered to the serializer.
+ */
+ public void initialize(Class clazz) { }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/SerializerException.java b/engine/src/networking/com/jme3/network/serializing/SerializerException.java
new file mode 100644
index 0000000..32e8aab
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/SerializerException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing;
+
+import java.io.IOException;
+
+/**
+ * A general exception from the serialization routines.
+ *
+ * @version $Revision: 7118 $
+ * @author Paul Speed
+ */
+public class SerializerException extends IOException
+{
+ public SerializerException( String msg, Throwable cause )
+ {
+ super( msg );
+ initCause(cause);
+ }
+
+ public SerializerException( String msg )
+ {
+ super( msg );
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/SerializerRegistration.java b/engine/src/networking/com/jme3/network/serializing/SerializerRegistration.java
new file mode 100644
index 0000000..00b365e
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/SerializerRegistration.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing;
+
+/**
+ * A SerializerRegistration represents a connection between a class, and
+ * its serializer. It also includes the class ID, as a short.
+ *
+ * @author Lars Wesselius
+ */
+public final class SerializerRegistration {
+ private Serializer serializer;
+ private short id;
+ private Class type;
+
+ public SerializerRegistration(Serializer serializer, Class cls, short id) {
+ this.serializer = serializer;
+ type = cls;
+ this.id = id;
+ }
+
+ /**
+ * Get the serializer.
+ *
+ * @return The serializer.
+ */
+ public Serializer getSerializer() {
+ return serializer;
+ }
+
+ /**
+ * Get the ID.
+ *
+ * @return The ID.
+ */
+ public short getId() {
+ return id;
+ }
+
+ /**
+ * Get the class type.
+ *
+ * @return The class type.
+ */
+ public Class getType() {
+ return type;
+ }
+
+ public String toString() {
+ return "SerializerRegistration[" + id + ", " + type + ", " + serializer + "]";
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/ArraySerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/ArraySerializer.java
new file mode 100644
index 0000000..71c43d4
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/ArraySerializer.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine, Java Game Networking
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+
+/**
+ * Array serializer
+ *
+ * @author Nathan Sweet
+ */
+@SuppressWarnings("unchecked")
+public class ArraySerializer extends Serializer {
+ private int[] getDimensions (Object array) {
+ int depth = 0;
+ Class nextClass = array.getClass().getComponentType();
+ while (nextClass != null) {
+ depth++;
+ nextClass = nextClass.getComponentType();
+ }
+ int[] dimensions = new int[depth];
+ dimensions[0] = Array.getLength(array);
+ if (depth > 1) collectDimensions(array, 1, dimensions);
+ return dimensions;
+ }
+
+ private void collectDimensions (Object array, int dimension, int[] dimensions) {
+ boolean elementsAreArrays = dimension < dimensions.length - 1;
+ for (int i = 0, s = Array.getLength(array); i < s; i++) {
+ Object element = Array.get(array, i);
+ if (element == null) continue;
+ dimensions[dimension] = Math.max(dimensions[dimension], Array.getLength(element));
+ if (elementsAreArrays) collectDimensions(element, dimension + 1, dimensions);
+ }
+ }
+
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ byte dimensionCount = data.get();
+ if (dimensionCount == 0)
+ return null;
+
+ int[] dimensions = new int[dimensionCount];
+ for (int i = 0; i < dimensionCount; i++)
+ dimensions[i] = data.getInt();
+
+ Serializer elementSerializer = null;
+
+ Class elementClass = c;
+ while (elementClass.getComponentType() != null)
+ elementClass = elementClass.getComponentType();
+
+ if (Modifier.isFinal(elementClass.getModifiers())) elementSerializer = Serializer.getSerializer(elementClass);
+ // Create array and read in the data.
+ T array = (T)Array.newInstance(elementClass, dimensions);
+ readArray(elementSerializer, elementClass, data, array, 0, dimensions);
+ return array;
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ if (object == null){
+ buffer.put((byte)0);
+ return;
+ }
+
+ int[] dimensions = getDimensions(object);
+ buffer.put((byte)dimensions.length);
+ for (int dimension : dimensions) buffer.putInt(dimension);
+ Serializer elementSerializer = null;
+
+ Class elementClass = object.getClass();
+ while (elementClass.getComponentType() != null) {
+ elementClass = elementClass.getComponentType();
+ }
+
+ if (Modifier.isFinal(elementClass.getModifiers())) elementSerializer = Serializer.getSerializer(elementClass);
+ writeArray(elementSerializer, buffer, object, 0, dimensions.length);
+ }
+
+ private void writeArray(Serializer elementSerializer, ByteBuffer buffer, Object array, int dimension, int dimensionCount) throws IOException {
+ int length = Array.getLength(array);
+ if (dimension > 0) {
+ buffer.putInt(length);
+ }
+ // Write array data.
+ boolean elementsAreArrays = dimension < dimensionCount - 1;
+ for (int i = 0; i < length; i++) {
+ Object element = Array.get(array, i);
+ if (elementsAreArrays) {
+ if (element != null) writeArray(elementSerializer, buffer, element, dimension + 1, dimensionCount);
+ } else if (elementSerializer != null) {
+ elementSerializer.writeObject(buffer, element);
+ } else {
+ // Each element could be a different type. Store the class with the object.
+ Serializer.writeClassAndObject(buffer, element);
+ }
+ }
+ }
+
+ private void readArray (Serializer elementSerializer, Class elementClass, ByteBuffer buffer, Object array, int dimension, int[] dimensions) throws IOException {
+ boolean elementsAreArrays = dimension < dimensions.length - 1;
+ int length;
+ if (dimension == 0) {
+ length = dimensions[0];
+ } else {
+ length = buffer.getInt();
+ }
+ for (int i = 0; i < length; i++) {
+ if (elementsAreArrays) {
+ // Nested array.
+ Object element = Array.get(array, i);
+ if (element != null) readArray(elementSerializer, elementClass, buffer, element, dimension + 1, dimensions);
+ } else if (elementSerializer != null) {
+ // Use same converter (and class) for all elements.
+ Array.set(array, i, elementSerializer.readObject(buffer, elementClass));
+ } else {
+ // Each element could be a different type. Look up the class with the object.
+ Array.set(array, i, Serializer.readClassAndObject(buffer));
+ }
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/BooleanSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/BooleanSerializer.java
new file mode 100644
index 0000000..52f5fea
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/BooleanSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Boolean serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class BooleanSerializer extends Serializer {
+
+ public Boolean readObject(ByteBuffer data, Class c) throws IOException {
+ return data.get() == 1;
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.put(((Boolean)object) ? (byte)1 : (byte)0);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/ByteSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/ByteSerializer.java
new file mode 100644
index 0000000..39b9a74
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/ByteSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Byte serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class ByteSerializer extends Serializer {
+
+ public Byte readObject(ByteBuffer data, Class c) throws IOException {
+ return data.get();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.put((Byte)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/CharSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/CharSerializer.java
new file mode 100644
index 0000000..5dfb852
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/CharSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Char serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class CharSerializer extends Serializer {
+
+ public Character readObject(ByteBuffer data, Class c) throws IOException {
+ return data.getChar();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putChar((Character)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/CollectionSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/CollectionSerializer.java
new file mode 100644
index 0000000..687b84d
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/CollectionSerializer.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerRegistration;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.logging.Level;
+
+/**
+ * Serializes collections.
+ *
+ * @author Lars Wesselius
+ */
+public class CollectionSerializer extends Serializer {
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ int length = data.getInt();
+
+ Collection collection;
+ try {
+ collection = (Collection)c.newInstance();
+ } catch (Exception e) {
+ log.log(Level.FINE, "[Serializer][???] Could not determine collection type. Using ArrayList.");
+ collection = new ArrayList(length);
+ }
+
+ if (length == 0) return (T)collection;
+
+ if (data.get() == (byte)1) {
+ SerializerRegistration reg = Serializer.readClass(data);
+ Class clazz = reg.getType();
+ Serializer serializer = reg.getSerializer();
+
+ for (int i = 0; i != length; ++i) {
+ collection.add(serializer.readObject(data, clazz));
+ }
+ } else {
+ for (int i = 0; i != length; ++i) {
+ collection.add(Serializer.readClassAndObject(data));
+ }
+ }
+ return (T)collection;
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ Collection collection = (Collection)object;
+ int length = collection.size();
+
+ buffer.putInt(length);
+ if (length == 0) return;
+
+ Iterator it = collection.iterator();
+ Class elementClass = it.next().getClass();
+ while (it.hasNext()) {
+ Object obj = it.next();
+
+ if (obj.getClass() != elementClass) {
+ elementClass = null;
+ break;
+ }
+ }
+
+ if (elementClass != null) {
+ buffer.put((byte)1);
+ Serializer.writeClass(buffer, elementClass);
+ Serializer serializer = Serializer.getSerializer(elementClass);
+
+ for (Object elem : collection) {
+ serializer.writeObject(buffer, elem);
+ }
+ } else {
+ buffer.put((byte)0);
+ for (Object elem : collection) {
+ Serializer.writeClassAndObject(buffer, elem);
+ }
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/DateSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/DateSerializer.java
new file mode 100644
index 0000000..957796c
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/DateSerializer.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Date;
+
+/**
+ * Date serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class DateSerializer extends Serializer {
+
+ public Date readObject(ByteBuffer data, Class c) throws IOException {
+ return new Date(data.getLong());
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putLong(((Date)object).getTime());
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/DoubleSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/DoubleSerializer.java
new file mode 100644
index 0000000..7608eba
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/DoubleSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Double serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class DoubleSerializer extends Serializer {
+
+ public Double readObject(ByteBuffer data, Class c) throws IOException {
+ return data.getDouble();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putDouble((Double)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/EnumSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/EnumSerializer.java
new file mode 100644
index 0000000..f4e5ed3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/EnumSerializer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Enum serializer.
+ *
+ * @author Lars Wesselius
+ */
+public class EnumSerializer extends Serializer {
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ try {
+ int ordinal = data.getInt();
+
+ if (ordinal == -1) return null;
+ T[] enumConstants = c.getEnumConstants();
+ if (enumConstants == null)
+ throw new SerializerException( "Class has no enum constants:" + c );
+ return enumConstants[ordinal];
+ } catch (IndexOutOfBoundsException ex) {
+ return null;
+ }
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ if (object == null) {
+ buffer.putInt(-1);
+ } else {
+ buffer.putInt(((Enum)object).ordinal());
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java
new file mode 100644
index 0000000..3f13c50
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine, Java Game Networking
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerException;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.logging.Level;
+
+/**
+ * The field serializer is the default serializer used for custom class.
+ *
+ * @author Lars Wesselius, Nathan Sweet
+ */
+public class FieldSerializer extends Serializer {
+ private static Map<Class, SavedField[]> savedFields = new HashMap<Class, SavedField[]>();
+
+ protected void checkClass(Class clazz) {
+
+ // See if the class has a public no-arg constructor
+ try {
+ clazz.getConstructor();
+ } catch( NoSuchMethodException e ) {
+ throw new RuntimeException( "Registration error: no-argument constructor not found on:" + clazz );
+ }
+ }
+
+ public void initialize(Class clazz) {
+
+ checkClass(clazz);
+
+ List<Field> fields = new ArrayList<Field>();
+
+ Class processingClass = clazz;
+ while (processingClass != Object.class ) {
+ Collections.addAll(fields, processingClass.getDeclaredFields());
+ processingClass = processingClass.getSuperclass();
+ }
+
+ List<SavedField> cachedFields = new ArrayList<SavedField>(fields.size());
+ for (Field field : fields) {
+ int modifiers = field.getModifiers();
+ if (Modifier.isTransient(modifiers)) continue;
+ if (Modifier.isFinal(modifiers)) continue;
+ if (Modifier.isStatic(modifiers)) continue;
+ if (field.isSynthetic()) continue;
+ field.setAccessible(true);
+
+ SavedField cachedField = new SavedField();
+ cachedField.field = field;
+
+ if (Modifier.isFinal(field.getType().getModifiers())) {
+ // The type of this field is implicit in the outer class
+ // definition and because the type is final, it can confidently
+ // be determined on the other end.
+ // Note: passing false to this method has the side-effect that field.getType()
+ // will be registered as a real class that can then be read/written
+ // directly as any other registered class. It should be safe to take
+ // an ID like this because Serializer.initialize() is only called
+ // during registration... so this is like nested registration and
+ // doesn't have any ordering problems.
+ // ...well, as long as the order of fields is consistent from one
+ // end to the next.
+ cachedField.serializer = Serializer.getSerializer(field.getType(), false);
+ }
+
+ cachedFields.add(cachedField);
+ }
+
+ Collections.sort(cachedFields, new Comparator<SavedField>() {
+ public int compare (SavedField o1, SavedField o2) {
+ return o1.field.getName().compareTo(o2.field.getName());
+ }
+ });
+ savedFields.put(clazz, cachedFields.toArray(new SavedField[cachedFields.size()]));
+
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+
+ // Read the null/non-null marker
+ if (data.get() == 0x0)
+ return null;
+
+ SavedField[] fields = savedFields.get(c);
+
+ T object;
+ try {
+ object = c.newInstance();
+ } catch (Exception e) {
+ throw new SerializerException( "Error creating object of type:" + c, e );
+ }
+
+ for (SavedField savedField : fields) {
+ Field field = savedField.field;
+ Serializer serializer = savedField.serializer;
+ Object value;
+
+ if (serializer != null) {
+ value = serializer.readObject(data, field.getType());
+ } else {
+ value = Serializer.readClassAndObject(data);
+ }
+ try {
+ field.set(object, value);
+ } catch (IllegalAccessException e) {
+ throw new SerializerException( "Error reading object", e);
+ }
+ }
+ return object;
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+
+ // Add the null/non-null marker
+ buffer.put( (byte)(object != null ? 0x1 : 0x0) );
+ if (object == null) {
+ // Nothing left to do
+ return;
+ }
+
+ SavedField[] fields = savedFields.get(object.getClass());
+ if (fields == null)
+ throw new IOException("The " + object.getClass() + " is not registered"
+ + " in the serializer!");
+
+ for (SavedField savedField : fields) {
+ Object val = null;
+ try {
+ val = savedField.field.get(object);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ Serializer serializer = savedField.serializer;
+
+ try {
+ if (serializer != null) {
+ serializer.writeObject(buffer, val);
+ } else {
+ Serializer.writeClassAndObject(buffer, val);
+ }
+ } catch (BufferOverflowException boe) {
+ throw boe;
+ } catch (Exception e) {
+ throw new SerializerException( "Error writing object for field:" + savedField.field, e );
+ }
+ }
+ }
+
+ private final class SavedField {
+ public Field field;
+ public Serializer serializer;
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/FloatSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/FloatSerializer.java
new file mode 100644
index 0000000..404e165
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/FloatSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Float serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class FloatSerializer extends Serializer {
+
+ public Float readObject(ByteBuffer data, Class c) throws IOException {
+ return data.getFloat();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putFloat((Float)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/GZIPSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/GZIPSerializer.java
new file mode 100644
index 0000000..b681511
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/GZIPSerializer.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.Message;
+import com.jme3.network.message.GZIPCompressedMessage;
+import com.jme3.network.serializing.Serializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Serializes GZIP messages.
+ *
+ * @author Lars Wesselius
+ */
+public class GZIPSerializer extends Serializer {
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ try
+ {
+ GZIPCompressedMessage result = new GZIPCompressedMessage();
+
+ byte[] byteArray = new byte[data.remaining()];
+
+ data.get(byteArray);
+
+ GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(byteArray));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ byte[] tmp = new byte[9012];
+ int read;
+
+ while (in.available() > 0 && ((read = in.read(tmp)) > 0)) {
+ out.write(tmp, 0, read);
+ }
+
+ result.setMessage((Message)Serializer.readClassAndObject(ByteBuffer.wrap(out.toByteArray())));
+ return (T)result;
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ throw new IOException(e.toString());
+ }
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ if (!(object instanceof GZIPCompressedMessage)) return;
+ Message message = ((GZIPCompressedMessage)object).getMessage();
+
+ ByteBuffer tempBuffer = ByteBuffer.allocate(512000);
+ Serializer.writeClassAndObject(tempBuffer, message);
+
+ ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
+ GZIPOutputStream gzipOutput = new GZIPOutputStream(byteArrayOutput);
+
+ gzipOutput.write(tempBuffer.array());
+ gzipOutput.flush();
+ gzipOutput.finish();
+ gzipOutput.close();
+
+ buffer.put(byteArrayOutput.toByteArray());
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/IntSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/IntSerializer.java
new file mode 100644
index 0000000..52a2ed3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/IntSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * The Integer serializer serializes...integers. Big surprise.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class IntSerializer extends Serializer {
+
+ public Integer readObject(ByteBuffer data, Class c) throws IOException {
+ return data.getInt();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putInt((Integer)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/LongSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/LongSerializer.java
new file mode 100644
index 0000000..287c1de
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/LongSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * The Long serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class LongSerializer extends Serializer {
+
+ public Long readObject(ByteBuffer data, Class c) throws IOException {
+ return data.getLong();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putLong((Long)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/MapSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/MapSerializer.java
new file mode 100644
index 0000000..ba5eef3
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/MapSerializer.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerRegistration;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.logging.Level;
+
+public class MapSerializer extends Serializer {
+
+ /*
+
+ Structure:
+
+ struct Map {
+ INT length
+ BYTE flags = { 0x01 = all keys have the same type,
+ 0x02 = all values have the same type }
+ if (flags has 0x01 set)
+ SHORT keyType
+ if (flags has 0x02 set)
+ SHORT valType
+
+ struct MapEntry[length] entries {
+ if (flags does not have 0x01 set)
+ SHORT keyType
+ OBJECT key
+
+ if (flags does not have 0x02 set)
+ SHORT valType
+ OBJECT value
+ }
+ }
+
+ */
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ int length = data.getInt();
+
+ Map map;
+ try {
+ map = (Map)c.newInstance();
+ } catch (Exception e) {
+ log.log(Level.WARNING, "[Serializer][???] Could not determine map type. Using HashMap.");
+ map = new HashMap();
+ }
+
+ if (length == 0) return (T)map;
+
+ int flags = data.get() & 0xff;
+ boolean uniqueKeys = (flags & 0x01) == 0;
+ boolean uniqueVals = (flags & 0x02) == 0;
+
+ Class keyClazz = null;
+ Class valClazz = null;
+ Serializer keySerial = null;
+ Serializer valSerial = null;
+ if (!uniqueKeys){
+ SerializerRegistration reg = Serializer.readClass(data);
+ keyClazz = reg.getType();
+ keySerial = reg.getSerializer();
+ }
+ if (!uniqueVals){
+ SerializerRegistration reg = Serializer.readClass(data);
+ valClazz = reg.getType();
+ valSerial = reg.getSerializer();
+ }
+
+ for (int i = 0; i < length; i++){
+ Object key;
+ Object value;
+ if (uniqueKeys){
+ key = Serializer.readClassAndObject(data);
+ }else{
+ key = keySerial.readObject(data, keyClazz);
+ }
+ if (uniqueVals){
+ value = Serializer.readClassAndObject(data);
+ }else{
+ value = valSerial.readObject(data, valClazz);
+ }
+
+ map.put(key, value);
+ }
+
+ return (T)map;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ Map map = (Map)object;
+ int length = map.size();
+
+ buffer.putInt(length);
+ if (length == 0) return;
+
+
+ Set<Entry> entries = map.entrySet();
+
+ Iterator<Entry> it = entries.iterator();
+
+ Entry entry = it.next();
+ Class keyClass = entry.getKey().getClass();
+ Class valClass = entry.getValue().getClass();
+ while (it.hasNext()) {
+ entry = it.next();
+
+ if (entry.getKey().getClass() != keyClass){
+ keyClass = null;
+ if (valClass == null)
+ break;
+ }
+ if (entry.getValue().getClass() != valClass){
+ valClass = null;
+ if (keyClass == null)
+ break;
+ }
+ }
+
+ boolean uniqueKeys = keyClass == null;
+ boolean uniqueVals = valClass == null;
+ int flags = 0;
+ if (!uniqueKeys) flags |= 0x01;
+ if (!uniqueVals) flags |= 0x02;
+ buffer.put( (byte) flags );
+
+ Serializer keySerial = null, valSerial = null;
+ if (!uniqueKeys){
+ Serializer.writeClass(buffer, keyClass);
+ keySerial = Serializer.getSerializer(keyClass);
+ }
+ if (!uniqueVals){
+ Serializer.writeClass(buffer, valClass);
+ valSerial = Serializer.getSerializer(valClass);
+ }
+
+ it = entries.iterator();
+ while (it.hasNext()) {
+ entry = it.next();
+ if (uniqueKeys){
+ Serializer.writeClassAndObject(buffer, entry.getKey());
+ }else{
+ keySerial.writeObject(buffer, entry.getKey());
+ }
+ if (uniqueVals){
+ Serializer.writeClassAndObject(buffer, entry.getValue());
+ }else{
+ valSerial.writeObject(buffer, entry.getValue());
+ }
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/SavableSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/SavableSerializer.java
new file mode 100644
index 0000000..09e991d
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/SavableSerializer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.export.Savable;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+public class SavableSerializer extends Serializer {
+
+ private BinaryExporter exporter = new BinaryExporter();
+ private BinaryImporter importer = new BinaryImporter();
+
+ private static class BufferOutputStream extends OutputStream {
+
+ ByteBuffer output;
+
+ public BufferOutputStream(ByteBuffer output){
+ this.output = output;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ output.put( (byte) b );
+ }
+
+ @Override
+ public void write(byte[] b){
+ output.put(b);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len){
+ output.put(b, off, len);
+ }
+ }
+
+ private static class BufferInputStream extends InputStream {
+
+ ByteBuffer input;
+
+ public BufferInputStream(ByteBuffer input){
+ this.input = input;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (input.remaining() == 0)
+ return -1;
+ else
+ return input.get() & 0xff;
+ }
+
+ @Override
+ public int read(byte[] b){
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len){
+ int toRead = len > input.remaining() ? input.remaining() : len;
+ input.get(b, off, len);
+ return toRead;
+ }
+
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ BufferInputStream in = new BufferInputStream(data);
+ Savable s = importer.load(in);
+ in.close();
+ return (T) s;
+ }
+
+ @Override
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ Savable s = (Savable) object;
+ BufferOutputStream out = new BufferOutputStream(buffer);
+ exporter.save(s, out);
+ out.close();
+ }
+
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/SerializableSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/SerializableSerializer.java
new file mode 100644
index 0000000..dba4db6
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/SerializableSerializer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+
+/**
+ * Serializes uses Java built-in method.
+ *
+ * TODO
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class SerializableSerializer extends Serializer {
+
+ public Serializable readObject(ByteBuffer data, Class c) throws IOException {
+ throw new UnsupportedOperationException( "Serializable serialization not supported." );
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ throw new UnsupportedOperationException( "Serializable serialization not supported." );
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/ShortSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/ShortSerializer.java
new file mode 100644
index 0000000..2c3fe29
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/ShortSerializer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Short serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class ShortSerializer extends Serializer {
+ public Short readObject(ByteBuffer data, Class c) throws IOException {
+ return data.getShort();
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ buffer.putShort((Short)object);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/StringSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/StringSerializer.java
new file mode 100644
index 0000000..ab18f85
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/StringSerializer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * String serializer.
+ *
+ * @author Lars Wesselius
+ */
+@SuppressWarnings("unchecked")
+public class StringSerializer extends Serializer {
+
+ public String readObject(ByteBuffer data, Class c) throws IOException {
+
+ int length = -1;
+ byte type = data.get();
+ if (type == (byte)0) {
+ return null;
+ } else if (type == (byte)1) {
+ // Byte
+ length = data.get();
+ } else if (type == (byte)2) {
+ // Short
+ length = data.getShort();
+ } else if (type == (byte)3) {
+ // Int
+ length = data.getInt();
+ }
+ if (length == -1) throw new IOException("Could not read String: Invalid length identifier.");
+
+ byte[] buffer = new byte[length];
+ data.get(buffer);
+ return new String(buffer, "UTF-8");
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ String string = (String)object;
+
+ if (string == null) {
+ // Write that it's 0.
+ buffer.put((byte)0);
+ return;
+ }
+ byte[] stringBytes = string.getBytes("UTF-8");
+ int bufferLength = stringBytes.length;
+
+ try {
+ if (bufferLength <= Byte.MAX_VALUE) {
+ buffer.put((byte)1);
+ buffer.put((byte)bufferLength);
+ } else if (bufferLength <= Short.MAX_VALUE) {
+ buffer.put((byte)2);
+ buffer.putShort((short)bufferLength);
+ } else {
+ buffer.put((byte)3);
+ buffer.putInt(bufferLength);
+ }
+ buffer.put(stringBytes);
+ }
+ catch (BufferOverflowException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/Vector3Serializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/Vector3Serializer.java
new file mode 100644
index 0000000..f09066a
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/Vector3Serializer.java
@@ -0,0 +1,28 @@
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.math.Vector3f;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @author Kirill Vainer
+ */
+@SuppressWarnings("unchecked")
+public class Vector3Serializer extends Serializer {
+
+ public Vector3f readObject(ByteBuffer data, Class c) throws IOException {
+ Vector3f vec3 = new Vector3f();
+ vec3.x = data.getFloat();
+ vec3.y = data.getFloat();
+ vec3.z = data.getFloat();
+ return vec3;
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ Vector3f vec3 = (Vector3f) object;
+ buffer.putFloat(vec3.x);
+ buffer.putFloat(vec3.y);
+ buffer.putFloat(vec3.z);
+ }
+}
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/ZIPSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/ZIPSerializer.java
new file mode 100644
index 0000000..fafb2fa
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/ZIPSerializer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.Message;
+import com.jme3.network.message.ZIPCompressedMessage;
+import com.jme3.network.serializing.Serializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Serializes ZIP messages.
+ *
+ * @author Lars Wesselius
+ */
+public class ZIPSerializer extends Serializer {
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+ try
+ {
+ ZIPCompressedMessage result = new ZIPCompressedMessage();
+
+ byte[] byteArray = new byte[data.remaining()];
+
+ data.get(byteArray);
+
+ ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(byteArray));
+ in.getNextEntry();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ byte[] tmp = new byte[9012];
+ int read;
+
+ while (in.available() > 0 && ((read = in.read(tmp)) > 0)) {
+ out.write(tmp, 0, read);
+ }
+
+ in.closeEntry();
+ out.flush();
+ in.close();
+
+ result.setMessage((Message)Serializer.readClassAndObject(ByteBuffer.wrap(out.toByteArray())));
+ return (T)result;
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ throw new IOException(e.toString());
+ }
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+ if (!(object instanceof ZIPCompressedMessage)) return;
+
+ ZIPCompressedMessage zipMessage = (ZIPCompressedMessage)object;
+ Message message = zipMessage.getMessage();
+ ByteBuffer tempBuffer = ByteBuffer.allocate(512000);
+ Serializer.writeClassAndObject(tempBuffer, message);
+
+ ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
+ ZipOutputStream zipOutput = new ZipOutputStream(byteArrayOutput);
+ zipOutput.setLevel(zipMessage.getLevel());
+
+ ZipEntry zipEntry = new ZipEntry("zip");
+
+ zipOutput.putNextEntry(zipEntry);
+ zipOutput.write(tempBuffer.array());
+ zipOutput.flush();
+ zipOutput.closeEntry();
+ zipOutput.close();
+
+ buffer.put(byteArrayOutput.toByteArray());
+ }
+}
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag
new file mode 100644
index 0000000..5e77548
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag
@@ -0,0 +1,13 @@
+uniform bool m_UseTex;
+uniform sampler2D m_Texture;
+uniform vec4 m_Color;
+
+varying vec2 texCoord;
+varying vec4 color;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ texVal = m_UseTex ? texVal : vec4(1.0);
+ gl_FragColor = texVal * color * m_Color;
+}
+
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md
new file mode 100644
index 0000000..9ba39b1
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md
@@ -0,0 +1,21 @@
+MaterialDef Default GUI {
+
+ MaterialParameters {
+ Texture2D Texture
+ Boolean UseTex
+ Vector4 Color (Color)
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Nifty/Nifty.vert
+ FragmentShader GLSL100: Common/MatDefs/Nifty/Nifty.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert
new file mode 100644
index 0000000..67c864d
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert
@@ -0,0 +1,16 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec4 inPosition;
+attribute vec4 inColor;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+varying vec4 color;
+
+void main() {
+ vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ gl_Position = vec4(pos, 0.0, 1.0);
+
+ texCoord = inTexCoord;
+ color = inColor;
+} \ No newline at end of file
diff --git a/engine/src/niftygui/com/jme3/cinematic/events/GuiTrack.java b/engine/src/niftygui/com/jme3/cinematic/events/GuiTrack.java
new file mode 100644
index 0000000..281c450
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/cinematic/events/GuiTrack.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import de.lessvoid.nifty.Nifty;
+import java.io.IOException;
+
+/**
+ *
+ * @author Nehon
+ */
+public class GuiTrack extends AbstractCinematicEvent {
+
+ protected String screen;
+ protected Nifty nifty;
+
+ public GuiTrack() {
+ }
+
+ public GuiTrack(Nifty nifty, String screen) {
+ this.screen = screen;
+ this.nifty = nifty;
+ }
+
+ public GuiTrack(Nifty nifty, String screen, float initialDuration) {
+ super(initialDuration);
+ this.screen = screen;
+ this.nifty = nifty;
+ }
+
+ public GuiTrack(Nifty nifty, String screen, LoopMode loopMode) {
+ super(loopMode);
+ this.screen = screen;
+ this.nifty = nifty;
+ }
+
+ public GuiTrack(Nifty nifty, String screen, float initialDuration, LoopMode loopMode) {
+ super(initialDuration, loopMode);
+ this.screen = screen;
+ this.nifty = nifty;
+ }
+
+ @Override
+ public void onPlay() {
+ System.out.println("screen should be "+screen);
+ nifty.gotoScreen(screen);
+ }
+
+ @Override
+ public void onStop() {
+ nifty.gotoScreen("");
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ public void setNifty(Nifty nifty) {
+ this.nifty = nifty;
+ }
+
+ public void setScreen(String screen) {
+ this.screen = screen;
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(screen, "screen", "");
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ screen = ic.readString("screen", "");
+ }
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
new file mode 100644
index 0000000..0b80131
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.*;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.NiftyInputConsumer;
+import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
+import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;
+import de.lessvoid.nifty.spi.input.InputSystem;
+import java.util.ArrayList;
+
+public class InputSystemJme implements InputSystem, RawInputListener {
+
+ private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
+
+ private InputManager inputManager;
+
+ private boolean isDragging = false, niftyOwnsDragging = false;
+ private boolean pressed = false;
+ private int buttonIndex;
+ private int x, y;
+ private int height;
+
+ private boolean shiftDown = false;
+ private boolean ctrlDown = false;
+
+ private Nifty nifty;
+
+ public InputSystemJme(InputManager inputManager){
+ this.inputManager = inputManager;
+ }
+
+ public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
+ }
+
+ public void setNifty(Nifty nifty) {
+ this.nifty = nifty;
+ }
+
+ /**
+ * @param height The height of the viewport. Used to convert
+ * buttom-left origin to upper-left origin.
+ */
+ public void setHeight(int height){
+ this.height = height;
+ }
+
+ public void setMousePosition(int x, int y){
+ }
+
+ public void beginInput(){
+ }
+
+ public void endInput(){
+ boolean result = nifty.update();
+ }
+
+ private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {
+ boolean consumed = false;
+
+ x = (int) evt.getX();
+ y = (int) (height - evt.getY());
+
+ switch (evt.getType()) {
+ case DOWN:
+ consumed = nic.processMouseEvent(x, y, 0, 0, false);
+ isDragging = true;
+ niftyOwnsDragging = consumed;
+ if (consumed){
+ evt.setConsumed();
+ }
+
+ break;
+
+ case UP:
+ if (niftyOwnsDragging){
+ consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
+ if (consumed){
+ evt.setConsumed();
+ }
+ }
+
+ isDragging = false;
+ niftyOwnsDragging = false;
+ break;
+ }
+ }
+
+ private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) {
+ x = evt.getX();
+ y = height - evt.getY();
+ nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed);
+// if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){
+ // Do not consume motion events
+ //evt.setConsumed();
+// }
+ }
+
+ private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) {
+ boolean wasPressed = pressed;
+ boolean forwardToNifty = true;
+
+ buttonIndex = evt.getButtonIndex();
+ pressed = evt.isPressed();
+
+ // Mouse button raised. End dragging
+ if (wasPressed && !pressed){
+ if (!niftyOwnsDragging){
+ forwardToNifty = false;
+ }
+ isDragging = false;
+ niftyOwnsDragging = false;
+ }
+
+ boolean consumed = false;
+ if (forwardToNifty){
+ consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
+ if (consumed){
+ evt.setConsumed();
+ }
+ }
+
+ // Mouse button pressed. Begin dragging
+ if (!wasPressed && pressed){
+ isDragging = true;
+ niftyOwnsDragging = consumed;
+ }
+ }
+
+ private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) {
+ int code = evt.getKeyCode();
+
+ if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) {
+ shiftDown = evt.isPressed();
+ } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) {
+ ctrlDown = evt.isPressed();
+ }
+
+ KeyboardInputEvent keyEvt = new KeyboardInputEvent(code,
+ evt.getKeyChar(),
+ evt.isPressed(),
+ shiftDown,
+ ctrlDown);
+
+ if (nic.processKeyboardEvent(keyEvt)){
+ evt.setConsumed();
+ }
+ }
+
+ public void onMouseMotionEvent(MouseMotionEvent evt) {
+ // Only forward the event if there's actual motion involved.
+ if (inputManager.isCursorVisible() && (evt.getDX() != 0 ||
+ evt.getDY() != 0 ||
+ evt.getDeltaWheel() != 0)){
+ inputQueue.add(evt);
+ }
+ }
+
+ public void onMouseButtonEvent(MouseButtonEvent evt) {
+ if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2){
+ inputQueue.add(evt);
+ }
+ }
+
+ public void onJoyAxisEvent(JoyAxisEvent evt) {
+ }
+
+ public void onJoyButtonEvent(JoyButtonEvent evt) {
+ }
+
+ public void onKeyEvent(KeyInputEvent evt) {
+ inputQueue.add(evt);
+ }
+
+ public void onTouchEvent(TouchEvent evt) {
+ inputQueue.add(evt);
+ }
+
+ public void forwardEvents(NiftyInputConsumer nic) {
+ int queueSize = inputQueue.size();
+
+ for (int i = 0; i < queueSize; i++){
+ InputEvent evt = inputQueue.get(i);
+ if (evt instanceof MouseMotionEvent){
+ onMouseMotionEventQueued( (MouseMotionEvent)evt, nic);
+ }else if (evt instanceof MouseButtonEvent){
+ onMouseButtonEventQueued( (MouseButtonEvent)evt, nic);
+ }else if (evt instanceof KeyInputEvent){
+ onKeyEventQueued( (KeyInputEvent)evt, nic);
+ }else if (evt instanceof TouchEvent){
+ onTouchEventQueued( (TouchEvent)evt, nic);
+ }
+ }
+
+ inputQueue.clear();
+ }
+
+
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/NiftyJmeDisplay.java b/engine/src/niftygui/com/jme3/niftygui/NiftyJmeDisplay.java
new file mode 100644
index 0000000..97ddf9d
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/NiftyJmeDisplay.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.input.InputManager;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.tools.TimeProvider;
+import de.lessvoid.nifty.tools.resourceloader.ResourceLocation;
+import java.io.InputStream;
+import java.net.URL;
+
+public class NiftyJmeDisplay implements SceneProcessor {
+
+ protected boolean inited = false;
+ protected Nifty nifty;
+ protected AssetManager assetManager;
+ protected RenderManager renderManager;
+ protected RenderDeviceJme renderDev;
+ protected InputSystemJme inputSys;
+ protected SoundDeviceJme soundDev;
+ protected Renderer renderer;
+ protected ViewPort vp;
+
+ protected ResourceLocationJme resourceLocation;
+
+ protected int w, h;
+
+ protected class ResourceLocationJme implements ResourceLocation {
+
+ public InputStream getResourceAsStream(String path) {
+ AssetKey<Object> key = new AssetKey<Object>(path);
+ AssetInfo info = assetManager.locateAsset(key);
+ if (info != null){
+ return info.openStream();
+ }else{
+ throw new AssetNotFoundException(path);
+ }
+ }
+
+ public URL getResource(String path) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ //Empty constructor needed for jMP to create replacement input system
+ public NiftyJmeDisplay() {
+ }
+
+ public NiftyJmeDisplay(AssetManager assetManager,
+ InputManager inputManager,
+ AudioRenderer audioRenderer,
+ ViewPort vp){
+ this.assetManager = assetManager;
+
+ w = vp.getCamera().getWidth();
+ h = vp.getCamera().getHeight();
+
+ soundDev = new SoundDeviceJme(assetManager, audioRenderer);
+ renderDev = new RenderDeviceJme(this);
+ inputSys = new InputSystemJme(inputManager);
+ if (inputManager != null)
+ inputManager.addRawInputListener(inputSys);
+
+ nifty = new Nifty(renderDev, soundDev, inputSys, new TimeProvider());
+ inputSys.setNifty(nifty);
+
+ resourceLocation = new ResourceLocationJme();
+ nifty.getResourceLoader().removeAllResourceLocations();
+ nifty.getResourceLoader().addResourceLocation(resourceLocation);
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ this.renderManager = rm;
+ renderDev.setRenderManager(rm);
+ inited = true;
+ this.vp = vp;
+ this.renderer = rm.getRenderer();
+
+ inputSys.setHeight(vp.getCamera().getHeight());
+ }
+
+ public Nifty getNifty() {
+ return nifty;
+ }
+
+ RenderDeviceJme getRenderDevice() {
+ return renderDev;
+ }
+
+ AssetManager getAssetManager() {
+ return assetManager;
+ }
+
+ RenderManager getRenderManager() {
+ return renderManager;
+ }
+
+ int getHeight() {
+ return h;
+ }
+
+ int getWidth() {
+ return w;
+ }
+
+ Renderer getRenderer(){
+ return renderer;
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ this.w = w;
+ this.h = h;
+ inputSys.setHeight(h);
+ nifty.resolutionChanged();
+ }
+
+ public boolean isInitialized() {
+ return inited;
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ // render nifty before anything else
+ renderManager.setCamera(vp.getCamera(), true);
+ //nifty.update();
+ nifty.render(false);
+ renderManager.setCamera(vp.getCamera(), false);
+ }
+
+ public void postFrame(FrameBuffer out) {
+ }
+
+ public void cleanup() {
+ inited = false;
+// nifty.exit();
+ }
+
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java
new file mode 100644
index 0000000..8977ed6
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.font.BitmapText;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.BufferUtils;
+import de.lessvoid.nifty.elements.render.TextRenderer.RenderFontNull;
+import de.lessvoid.nifty.render.BlendMode;
+import de.lessvoid.nifty.spi.render.MouseCursor;
+import de.lessvoid.nifty.spi.render.RenderDevice;
+import de.lessvoid.nifty.spi.render.RenderFont;
+import de.lessvoid.nifty.spi.render.RenderImage;
+import de.lessvoid.nifty.tools.Color;
+import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.HashMap;
+
+public class RenderDeviceJme implements RenderDevice {
+
+ private NiftyJmeDisplay display;
+ private RenderManager rm;
+ private Renderer r;
+
+ private HashMap<String, BitmapText> textCacheLastFrame = new HashMap<String, BitmapText>();
+ private HashMap<String, BitmapText> textCacheCurrentFrame = new HashMap<String, BitmapText>();
+
+ private final Quad quad = new Quad(1, -1, true);
+ private final Geometry quadGeom = new Geometry("nifty-quad", quad);
+ private final Material niftyMat;
+
+ private boolean clipWasSet = false;
+ private BlendMode blendMode = null;
+
+ private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord);
+ private VertexBuffer quadModTC = quadDefaultTC.clone();
+ private VertexBuffer quadColor;
+
+ private Matrix4f tempMat = new Matrix4f();
+ private ColorRGBA tempColor = new ColorRGBA();
+
+ public RenderDeviceJme(NiftyJmeDisplay display){
+ this.display = display;
+
+ quadColor = new VertexBuffer(Type.Color);
+ quadColor.setNormalized(true);
+ ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4);
+ quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb);
+ quad.setBuffer(quadColor);
+
+ quadModTC.setUsage(Usage.Stream);
+
+ niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/Nifty.j3md");
+ niftyMat.getAdditionalRenderState().setDepthTest(false);
+ }
+
+ public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
+ }
+
+ public void setRenderManager(RenderManager rm){
+ this.rm = rm;
+ this.r = rm.getRenderer();
+ }
+
+ // TODO: Cursor support
+ public MouseCursor createMouseCursor(String str, int x, int y){
+ return new MouseCursor() {
+ public void dispose() {
+ }
+ };
+ }
+
+ public void enableMouseCursor(MouseCursor cursor){
+ }
+
+ public void disableMouseCursor(){
+ }
+
+ public RenderImage createImage(String filename, boolean linear) {
+ return new RenderImageJme(filename, linear, display);
+ }
+
+ public RenderFont createFont(String filename) {
+ return new RenderFontJme(filename, display);
+ }
+
+ public void beginFrame() {
+ }
+
+ public void endFrame() {
+ HashMap<String, BitmapText> temp = textCacheLastFrame;
+ textCacheLastFrame = textCacheCurrentFrame;
+ textCacheCurrentFrame = temp;
+ textCacheCurrentFrame.clear();
+
+// System.exit(1);
+ }
+
+ public int getWidth() {
+ return display.getWidth();
+ }
+
+ public int getHeight() {
+ return display.getHeight();
+ }
+
+ public void clear() {
+ }
+
+ public void setBlendMode(BlendMode blendMode) {
+ if (this.blendMode != blendMode){
+ this.blendMode = blendMode;
+ }
+ }
+
+ private RenderState.BlendMode convertBlend(){
+ if (blendMode == null)
+ return RenderState.BlendMode.Off;
+ else if (blendMode == BlendMode.BLEND)
+ return RenderState.BlendMode.Alpha;
+ else if (blendMode == BlendMode.MULIPLY)
+ return RenderState.BlendMode.Modulate;
+ else
+ throw new UnsupportedOperationException();
+ }
+
+ private int convertColor(Color color){
+ int color2 = 0;
+ color2 |= ((int)(255.0 * color.getAlpha())) << 24;
+ color2 |= ((int)(255.0 * color.getBlue())) << 16;
+ color2 |= ((int)(255.0 * color.getGreen())) << 8;
+ color2 |= ((int)(255.0 * color.getRed()));
+ return color2;
+ }
+
+ private ColorRGBA convertColor(Color inColor, ColorRGBA outColor){
+ return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());
+ }
+
+ private void setColor(Color color){
+ ByteBuffer buf = (ByteBuffer) quadColor.getData();
+ buf.rewind();
+
+ int color2 = convertColor(color);
+ buf.putInt(color2);
+ buf.putInt(color2);
+ buf.putInt(color2);
+ buf.putInt(color2);
+
+ buf.flip();
+ quadColor.updateData(buf);
+ }
+
+ /**
+ *
+ * @param font
+ * @param str
+ * @param x
+ * @param y
+ * @param color
+ * @param size
+ * @deprecated use renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) instead
+ */
+ @Deprecated
+ public void renderFont(RenderFont font, String str, int x, int y, Color color, float size){
+ renderFont(font, str, x, y, color, size, size);
+ }
+
+ @Override
+ public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY){
+ //TODO find out what the f1 param is for
+ if (str.length() == 0)
+ return;
+
+ if (font instanceof RenderFontNull)
+ return;
+
+ RenderFontJme jmeFont = (RenderFontJme) font;
+
+ String key = font+str+color.getColorString();
+ BitmapText text = textCacheLastFrame.get(key);
+ if (text == null) {
+ text = jmeFont.createText();
+ text.setText(str);
+ text.updateLogicalState(0);
+ }
+ textCacheCurrentFrame.put(key, text);
+
+ niftyMat.setColor("Color", convertColor(color, tempColor));
+ niftyMat.setBoolean("UseTex", true);
+ niftyMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
+// niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
+ text.setMaterial(niftyMat);
+
+ float width = text.getLineWidth();
+ float height = text.getLineHeight();
+
+ float x0 = x + 0.5f * width * (1f - sizeX);
+ float y0 = y + 0.5f * height * (1f - sizeY);
+
+ tempMat.loadIdentity();
+ tempMat.setTranslation(x0, getHeight() - y0, 0);
+ tempMat.setScale(sizeX, sizeY, 0);
+
+ rm.setWorldMatrix(tempMat);
+ text.render(rm);
+
+// System.out.println("renderFont");
+ }
+
+ public void renderImage(RenderImage image, int x, int y, int w, int h,
+ int srcX, int srcY, int srcW, int srcH,
+ Color color, float scale,
+ int centerX, int centerY){
+ RenderImageJme jmeImage = (RenderImageJme) image;
+ Texture2D texture = jmeImage.getTexture();
+
+ niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
+ niftyMat.setColor("Color", ColorRGBA.White);
+ niftyMat.setTexture("Texture", texture);
+ niftyMat.setBoolean("UseTex", true);
+ setColor(color);
+
+ float imageWidth = jmeImage.getWidth();
+ float imageHeight = jmeImage.getHeight();
+ FloatBuffer texCoords = (FloatBuffer) quadModTC.getData();
+
+ float startX = srcX / imageWidth;
+ float startY = srcY / imageHeight;
+ float endX = startX + (srcW / imageWidth);
+ float endY = startY + (srcH / imageHeight);
+
+ startY = 1f - startY;
+ endY = 1f - endY;
+
+ texCoords.rewind();
+ texCoords.put(startX).put(startY);
+ texCoords.put(endX) .put(startY);
+ texCoords.put(endX) .put(endY);
+ texCoords.put(startX).put(endY);
+ texCoords.flip();
+ quadModTC.updateData(texCoords);
+
+ quad.clearBuffer(Type.TexCoord);
+ quad.setBuffer(quadModTC);
+
+ float x0 = centerX + (x - centerX) * scale;
+ float y0 = centerY + (y - centerY) * scale;
+
+ tempMat.loadIdentity();
+ tempMat.setTranslation(x0, getHeight() - y0, 0);
+ tempMat.setScale(w * scale, h * scale, 0);
+
+ rm.setWorldMatrix(tempMat);
+ niftyMat.render(quadGeom, rm);
+//
+// System.out.println("renderImage (Sub)");
+ }
+
+ public void renderImage(RenderImage image, int x, int y, int width, int height,
+ Color color, float imageScale){
+
+ RenderImageJme jmeImage = (RenderImageJme) image;
+
+ niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
+ niftyMat.setColor("Color", ColorRGBA.White);
+ niftyMat.setTexture("Texture", jmeImage.getTexture());
+ niftyMat.setBoolean("UseTex", true);
+ setColor(color);
+
+ quad.clearBuffer(Type.TexCoord);
+ quad.setBuffer(quadDefaultTC);
+
+ float x0 = x + 0.5f * width * (1f - imageScale);
+ float y0 = y + 0.5f * height * (1f - imageScale);
+
+ tempMat.loadIdentity();
+ tempMat.setTranslation(x0, getHeight() - y0, 0);
+ tempMat.setScale(width * imageScale, height * imageScale, 0);
+
+ rm.setWorldMatrix(tempMat);
+ niftyMat.render(quadGeom, rm);
+//
+// System.out.println("renderImage");
+ }
+
+ public void renderQuad(int x, int y, int width, int height, Color color){
+ niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
+ niftyMat.setColor("Color", ColorRGBA.White);
+ niftyMat.clearParam("Texture");
+ niftyMat.setBoolean("UseTex", false);
+ setColor(color);
+
+ tempMat.loadIdentity();
+ tempMat.setTranslation(x, getHeight() - y, 0);
+ tempMat.setScale(width, height, 0);
+
+ rm.setWorldMatrix(tempMat);
+ niftyMat.render(quadGeom, rm);
+
+// System.out.println("renderQuad (Solid)");
+ }
+
+ public void renderQuad(int x, int y, int width, int height,
+ Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) {
+
+ ByteBuffer buf = (ByteBuffer) quadColor.getData();
+ buf.rewind();
+
+ buf.putInt(convertColor(topRight));
+ buf.putInt(convertColor(topLeft));
+
+ buf.putInt(convertColor(bottomLeft));
+ buf.putInt(convertColor(bottomRight));
+
+ buf.flip();
+ quadColor.updateData(buf);
+
+ niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
+ niftyMat.setColor("Color", ColorRGBA.White);
+ niftyMat.clearParam("Texture");
+ niftyMat.setBoolean("UseTex", false);
+
+ tempMat.loadIdentity();
+ tempMat.setTranslation(x, getHeight() - y, 0);
+ tempMat.setScale(width, height, 0);
+
+ rm.setWorldMatrix(tempMat);
+ niftyMat.render(quadGeom, rm);
+//
+// System.out.println("renderQuad (Grad)");
+ }
+
+ public void enableClip(int x0, int y0, int x1, int y1){
+// System.out.println("enableClip");
+ clipWasSet = true;
+ r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0);
+ }
+
+ public void disableClip() {
+// System.out.println("disableClip");
+ if (clipWasSet){
+ r.clearClipRect();
+ clipWasSet = false;
+ }
+ }
+
+
+
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/RenderFontJme.java b/engine/src/niftygui/com/jme3/niftygui/RenderFontJme.java
new file mode 100644
index 0000000..dccef34
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/RenderFontJme.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import de.lessvoid.nifty.spi.render.RenderFont;
+
+public class RenderFontJme implements RenderFont {
+
+ private NiftyJmeDisplay display;
+ private BitmapFont font;
+ private BitmapText text;
+ private float actualSize;
+
+ /**
+ * Initialize the font.
+ * @param name font filename
+ */
+ public RenderFontJme(String name, NiftyJmeDisplay display) {
+ this.display = display;
+ font = display.getAssetManager().loadFont(name);
+ if (font == null) {
+ throw new RuntimeException( "Font not loaded:" + name );
+ }
+ text = new BitmapText(font);
+ actualSize = font.getPreferredSize();
+ text.setSize(actualSize);
+ }
+
+ public BitmapText createText() {
+ return new BitmapText(font);
+ }
+
+ public BitmapText getText(){
+ return text;
+ }
+
+ /**
+ * get font height.
+ * @return height
+ */
+ public int getHeight() {
+ return (int) text.getLineHeight();
+ }
+
+ /**
+ * get font width of the given string.
+ * @param str text
+ * @return width of the given text for the current font
+ */
+ public int getWidth(final String str) {
+ if (str.length() == 0)
+ return 0;
+
+ // Note: BitmapFont is now fixed to return the proper line width
+ // at least for now. The older commented out (by someone else, not me)
+ // code below is arguably 'more accurate' if BitmapFont gets
+ // buggy again. The issue is that the BitmapText and BitmapFont
+ // use a different algorithm for calculating size and both must
+ // be modified in sync.
+ int result = (int) font.getLineWidth(str);
+// text.setText(str);
+// text.updateLogicalState(0);
+// int result = (int) text.getLineWidth();
+
+ return result;
+ }
+
+ public int getWidth(final String str, final float size) {
+ // Note: This is supposed to return the width of the String when scaled
+ // with the size factor. Since I don't know how to do that with
+ // the font rendering in jme this will only work correctly with
+ // a size value of 1.f and will return inaccurate values otherwise.
+ return getWidth(str);
+ }
+
+ /**
+ * Return the width of the given character including kerning information.
+ * @param currentCharacter current character
+ * @param nextCharacter next character
+ * @param size font size
+ * @return width of the character or null when no information for the character is available
+ */
+ public int getCharacterAdvance(final char currentCharacter, final char nextCharacter, final float size) {
+ return Math.round(font.getCharacterAdvance(currentCharacter, nextCharacter, size));
+ }
+
+ public void dispose() {
+ }
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/RenderImageJme.java b/engine/src/niftygui/com/jme3/niftygui/RenderImageJme.java
new file mode 100644
index 0000000..e871e8a
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/RenderImageJme.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.asset.TextureKey;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture.MagFilter;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture2D;
+import de.lessvoid.nifty.spi.render.RenderImage;
+
+public class RenderImageJme implements RenderImage {
+
+ private Texture2D texture;
+ private Image image;
+ private int width;
+ private int height;
+
+ public RenderImageJme(String filename, boolean linear, NiftyJmeDisplay display){
+ TextureKey key = new TextureKey(filename, true);
+
+ key.setAnisotropy(0);
+ key.setAsCube(false);
+ key.setGenerateMips(false);
+
+ texture = (Texture2D) display.getAssetManager().loadTexture(key);
+ texture.setMagFilter(linear ? MagFilter.Bilinear : MagFilter.Nearest);
+ texture.setMinFilter(linear ? MinFilter.BilinearNoMipMaps : MinFilter.NearestNoMipMaps);
+ image = texture.getImage();
+
+ width = image.getWidth();
+ height = image.getHeight();
+ }
+
+ public RenderImageJme(Texture2D texture){
+ if (texture.getImage() == null)
+ throw new IllegalArgumentException("texture.getImage() cannot be null");
+
+ this.texture = texture;
+ this.image = texture.getImage();
+ width = image.getWidth();
+ height = image.getHeight();
+ }
+
+ public Texture2D getTexture(){
+ return texture;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void dispose() {
+ }
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/SoundDeviceJme.java b/engine/src/niftygui/com/jme3/niftygui/SoundDeviceJme.java
new file mode 100644
index 0000000..a936e03
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/SoundDeviceJme.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.AudioRenderer;
+import de.lessvoid.nifty.sound.SoundSystem;
+import de.lessvoid.nifty.spi.sound.SoundDevice;
+import de.lessvoid.nifty.spi.sound.SoundHandle;
+import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
+
+public class SoundDeviceJme implements SoundDevice {
+
+ protected AssetManager assetManager;
+ protected AudioRenderer ar;
+
+ public SoundDeviceJme(AssetManager assetManager, AudioRenderer ar){
+ this.assetManager = assetManager;
+ this.ar = ar;
+ }
+
+ public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
+ }
+
+ public SoundHandle loadSound(SoundSystem soundSystem, String filename) {
+ AudioNode an = new AudioNode(assetManager, filename, false);
+ an.setPositional(false);
+ return new SoundHandleJme(ar, an);
+ }
+
+ public SoundHandle loadMusic(SoundSystem soundSystem, String filename) {
+ return new SoundHandleJme(ar, assetManager, filename);
+ }
+
+ public void update(int delta) {
+ }
+
+}
diff --git a/engine/src/niftygui/com/jme3/niftygui/SoundHandleJme.java b/engine/src/niftygui/com/jme3/niftygui/SoundHandleJme.java
new file mode 100644
index 0000000..04ce42a
--- /dev/null
+++ b/engine/src/niftygui/com/jme3/niftygui/SoundHandleJme.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.AudioNode.Status;
+import com.jme3.audio.AudioRenderer;
+import de.lessvoid.nifty.spi.sound.SoundHandle;
+
+public class SoundHandleJme implements SoundHandle {
+
+ private AudioNode node;
+ private AssetManager am;
+ private String fileName;
+ private float volume = 1;
+
+ public SoundHandleJme(AudioRenderer ar, AudioNode node){
+ if (ar == null || node == null)
+ throw new NullPointerException();
+
+ this.node = node;
+ }
+
+ /**
+ * For streaming music only. (May need to loop..)
+ * @param ar
+ * @param am
+ * @param fileName
+ */
+ public SoundHandleJme(AudioRenderer ar, AssetManager am, String fileName){
+ if (ar == null || am == null)
+ throw new NullPointerException();
+
+ this.am = am;
+ if (fileName == null)
+ throw new NullPointerException();
+
+ this.fileName = fileName;
+ }
+
+ public void play() {
+ if (fileName != null){
+ if (node != null){
+ node.stop();
+ }
+
+ node = new AudioNode(am, fileName, true);
+ node.setPositional(false);
+ node.setVolume(volume);
+ node.play();
+ }else{
+ node.playInstance();
+ }
+ }
+
+ public void stop() {
+ if (node != null){
+ node.stop();
+ node = null;
+ }
+ }
+
+ public void setVolume(float f) {
+ if (node != null) {
+ node.setVolume(f);
+ }
+ volume = f;
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public boolean isPlaying() {
+ return node != null && node.getStatus() == Status.Playing;
+ }
+
+ public void dispose() {
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
new file mode 100644
index 0000000..f8d99aa
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Skeleton;
+import java.util.ArrayList;
+
+public class AnimData {
+
+ public final Skeleton skeleton;
+ public final ArrayList<Animation> anims;
+
+ public AnimData(Skeleton skeleton, ArrayList<Animation> anims) {
+ this.skeleton = skeleton;
+ this.anims = anims;
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
new file mode 100644
index 0000000..887ba5e
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre;
+
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader;
+import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet;
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.blockparser.BlockLanguageParser;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class MaterialLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(MaterialLoader.class.getName());
+
+ private String folderName;
+ private AssetManager assetManager;
+ private ColorRGBA ambient, diffuse, specular, emissive;
+ private Texture[] textures = new Texture[4];
+ private String texName;
+ private String matName;
+ private float shinines;
+ private boolean vcolor = false;
+ private boolean blend = false;
+ private boolean twoSide = false;
+ private boolean noLight = false;
+ private boolean separateTexCoord = false;
+ private int texUnit = 0;
+
+ private ColorRGBA readColor(String content){
+ String[] split = content.split("\\s");
+
+ ColorRGBA color = new ColorRGBA();
+ color.r = Float.parseFloat(split[0]);
+ color.g = Float.parseFloat(split[1]);
+ color.b = Float.parseFloat(split[2]);
+ if (split.length >= 4){
+ color.a = Float.parseFloat(split[3]);
+ }
+ return color;
+ }
+
+ private void readTextureImage(String content){
+ // texture image def
+ String path = null;
+
+ // find extension
+ int extStart = content.lastIndexOf(".");
+ for (int i = extStart; i < content.length(); i++){
+ char c = content.charAt(i);
+ if (Character.isWhitespace(c)){
+ // extension ends here
+ path = content.substring(0, i).trim();
+ content = content.substring(i+1).trim();
+ break;
+ }
+ }
+ if (path == null){
+ path = content.trim();
+ content = "";
+ }
+
+ Scanner lnScan = new Scanner(content);
+ String mips = null;
+ String type = null;
+ if (lnScan.hasNext()){
+ // more params
+ type = lnScan.next();
+// if (!lnScan.hasNext("\n") && lnScan.hasNext()){
+// mips = lnScan.next();
+// if (lnScan.hasNext()){
+ // even more params..
+ // will have to ignore
+// }
+// }
+ }
+
+ boolean genMips = true;
+ boolean cubic = false;
+ if (type != null && type.equals("0"))
+ genMips = false;
+
+ if (type != null && type.equals("cubic")){
+ cubic = true;
+ }
+
+ TextureKey texKey = new TextureKey(folderName + path, false);
+ texKey.setGenerateMips(genMips);
+ texKey.setAsCube(cubic);
+
+ try {
+ Texture loadedTexture = assetManager.loadTexture(texKey);
+
+ textures[texUnit].setImage(loadedTexture.getImage());
+ textures[texUnit].setMinFilter(loadedTexture.getMinFilter());
+ textures[texUnit].setKey(loadedTexture.getKey());
+
+ // XXX: Is this really neccessary?
+ textures[texUnit].setWrap(WrapMode.Repeat);
+ if (texName != null){
+ textures[texUnit].setName(texName);
+ texName = null;
+ }else{
+ textures[texUnit].setName(texKey.getName());
+ }
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, matName});
+ textures[texUnit].setImage(PlaceholderAssets.getPlaceholderImage());
+ }
+ }
+
+ private void readTextureUnitStatement(Statement statement){
+ String[] split = statement.getLine().split(" ", 2);
+ String keyword = split[0];
+ if (keyword.equals("texture")){
+ readTextureImage(split[1]);
+ }else if (keyword.equals("tex_address_mode")){
+ String mode = split[1];
+ if (mode.equals("wrap")){
+ textures[texUnit].setWrap(WrapMode.Repeat);
+ }else if (mode.equals("clamp")){
+ textures[texUnit].setWrap(WrapMode.Clamp);
+ }else if (mode.equals("mirror")){
+ textures[texUnit].setWrap(WrapMode.MirroredRepeat);
+ }else if (mode.equals("border")){
+ textures[texUnit].setWrap(WrapMode.BorderClamp);
+ }
+ }else if (keyword.equals("filtering")){
+ // ignored.. only anisotropy is considered
+ }else if (keyword.equals("tex_coord_set")){
+ int texCoord = Integer.parseInt(split[1]);
+ if (texCoord == 1){
+ separateTexCoord = true;
+ }
+ }else if (keyword.equals("max_anisotropy")){
+ int amount = Integer.parseInt(split[1]);
+ textures[texUnit].setAnisotropicFilter(amount);
+ }else{
+ logger.log(Level.WARNING, "Unsupported texture_unit directive: {0}", keyword);
+ }
+ }
+
+ private void readTextureUnit(Statement statement){
+ String[] split = statement.getLine().split(" ", 2);
+ // name is optional
+ if (split.length == 2){
+ texName = split[1];
+ }else{
+ texName = null;
+ }
+
+ textures[texUnit] = new Texture2D();
+ for (Statement texUnitStat : statement.getContents()){
+ readTextureUnitStatement(texUnitStat);
+ }
+ if (textures[texUnit].getImage() != null){
+ texUnit++;
+ }else{
+ // no image was loaded, ignore
+ textures[texUnit] = null;
+ }
+ }
+
+ private void readPassStatement(Statement statement){
+ // read until newline
+ String[] split = statement.getLine().split(" ", 2);
+ String keyword = split[0];
+ if (keyword.equals("diffuse")){
+ if (split[1].equals("vertexcolour")){
+ // use vertex colors
+ diffuse = ColorRGBA.White;
+ vcolor = true;
+ }else{
+ diffuse = readColor(split[1]);
+ }
+ }else if(keyword.equals("ambient")) {
+ if (split[1].equals("vertexcolour")){
+ // use vertex colors
+ ambient = ColorRGBA.White;
+ }else{
+ ambient = readColor(split[1]);
+ }
+ }else if (keyword.equals("emissive")){
+ emissive = readColor(split[1]);
+ }else if (keyword.equals("specular")){
+ String[] subsplit = split[1].split("\\s");
+ specular = new ColorRGBA();
+ specular.r = Float.parseFloat(subsplit[0]);
+ specular.g = Float.parseFloat(subsplit[1]);
+ specular.b = Float.parseFloat(subsplit[2]);
+ float unknown = Float.parseFloat(subsplit[3]);
+ if (subsplit.length >= 5){
+ // using 5 float values
+ specular.a = unknown;
+ shinines = Float.parseFloat(subsplit[4]);
+ }else{
+ // using 4 float values
+ specular.a = 1f;
+ shinines = unknown;
+ }
+ }else if (keyword.equals("texture_unit")){
+ readTextureUnit(statement);
+ }else if (keyword.equals("scene_blend")){
+ String mode = split[1];
+ if (mode.equals("alpha_blend")){
+ blend = true;
+ }
+ }else if (keyword.equals("cull_hardware")){
+ String mode = split[1];
+ if (mode.equals("none")){
+ twoSide = true;
+ }
+ }else if (keyword.equals("cull_software")){
+ // ignore
+ }else if (keyword.equals("lighting")){
+ String isOn = split[1];
+ if (isOn.equals("on")){
+ noLight = false;
+ }else if (isOn.equals("off")){
+ noLight = true;
+ }
+ }else{
+ logger.log(Level.WARNING, "Unsupported pass directive: {0}", keyword);
+ }
+ }
+
+ private void readPass(Statement statement){
+ String name;
+ String[] split = statement.getLine().split(" ", 2);
+ if (split.length == 1){
+ // no name
+ name = null;
+ }else{
+ name = split[1];
+ }
+
+ for (Statement passStat : statement.getContents()){
+ readPassStatement(passStat);
+ }
+
+ texUnit = 0;
+ }
+
+ private void readTechnique(Statement statement){
+ String[] split = statement.getLine().split(" ", 2);
+ String name;
+ if (split.length == 1){
+ // no name
+ name = null;
+ }else{
+ name = split[1];
+ }
+ for (Statement techStat : statement.getContents()){
+ readPass(techStat);
+ }
+ }
+
+ private void readMaterialStatement(Statement statement){
+ if (statement.getLine().startsWith("technique")){
+ readTechnique(statement);
+ }else if (statement.getLine().startsWith("receive_shadows")){
+ String isOn = statement.getLine().split("\\s")[1];
+ if (isOn != null && isOn.equals("true")){
+ }
+ }
+ }
+
+ @SuppressWarnings("empty-statement")
+ private void readMaterial(Statement statement){
+ for (Statement materialStat : statement.getContents()){
+ readMaterialStatement(materialStat);
+ }
+ }
+
+ private Material compileMaterial(){
+ Material mat;
+ if (noLight){
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ }else{
+ mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ }
+ if (blend){
+ RenderState rs = mat.getAdditionalRenderState();
+ rs.setAlphaTest(true);
+ rs.setAlphaFallOff(0.01f);
+ rs.setBlendMode(RenderState.BlendMode.Alpha);
+
+ if (twoSide){
+ rs.setFaceCullMode(RenderState.FaceCullMode.Off);
+ }
+
+// rs.setDepthWrite(false);
+ mat.setTransparent(true);
+ if (!noLight){
+ mat.setBoolean("UseAlpha", true);
+ }
+ }else{
+ if (twoSide){
+ RenderState rs = mat.getAdditionalRenderState();
+ rs.setFaceCullMode(RenderState.FaceCullMode.Off);
+ }
+ }
+
+ if (!noLight){
+ if (shinines > 0f) {
+ mat.setFloat("Shininess", shinines);
+ } else {
+ mat.setFloat("Shininess", 16f); // set shininess to some value anyway..
+ }
+
+ if (vcolor)
+ mat.setBoolean("UseVertexColor", true);
+
+ if (textures[0] != null)
+ mat.setTexture("DiffuseMap", textures[0]);
+
+ mat.setBoolean("UseMaterialColors", true);
+ if(diffuse != null){
+ mat.setColor("Diffuse", diffuse);
+ }else{
+ mat.setColor("Diffuse", ColorRGBA.White);
+ }
+
+ if(ambient != null){
+ mat.setColor("Ambient", ambient);
+ }else{
+ mat.setColor("Ambient", ColorRGBA.DarkGray);
+ }
+
+ if(specular != null){
+ mat.setColor("Specular", specular);
+ }else{
+ mat.setColor("Specular", ColorRGBA.Black);
+ }
+
+ if (emissive != null){
+ mat.setColor("GlowColor", emissive);
+ }
+ }else{
+ if (vcolor) {
+ mat.setBoolean("VertexColor", true);
+ }
+
+ if (textures[0] != null && textures[1] == null){
+ if (separateTexCoord){
+ mat.setTexture("LightMap", textures[0]);
+ mat.setBoolean("SeparateTexCoord", true);
+ }else{
+ mat.setTexture("ColorMap", textures[0]);
+ }
+ }else if (textures[1] != null){
+ mat.setTexture("ColorMap", textures[0]);
+ mat.setTexture("LightMap", textures[1]);
+ if (separateTexCoord){
+ mat.setBoolean("SeparateTexCoord", true);
+ }
+ }
+
+ if(diffuse != null){
+ mat.setColor("Color", diffuse);
+ }
+
+ if (emissive != null){
+ mat.setColor("GlowColor", emissive);
+ }
+ }
+
+ noLight = false;
+ Arrays.fill(textures, null);
+ diffuse = null;
+ specular = null;
+ shinines = 0f;
+ vcolor = false;
+ blend = false;
+ texUnit = 0;
+ separateTexCoord = false;
+ return mat;
+ }
+
+ private MaterialList load(AssetManager assetManager, AssetKey key, InputStream in) throws IOException{
+ folderName = key.getFolder();
+ this.assetManager = assetManager;
+
+ MaterialList list = null;
+ List<Statement> statements = BlockLanguageParser.parse(in);
+
+ for (Statement statement : statements){
+ if (statement.getLine().startsWith("import")){
+ MaterialExtensionSet matExts = null;
+ if (key instanceof OgreMaterialKey){
+ matExts = ((OgreMaterialKey)key).getMaterialExtensionSet();
+ }
+
+ if (matExts == null){
+ throw new IOException("Must specify MaterialExtensionSet when loading\n"+
+ "Ogre3D materials with extended materials");
+ }
+
+ list = new MaterialExtensionLoader().load(assetManager, key, matExts, statements);
+ break;
+ }else if (statement.getLine().startsWith("material")){
+ if (list == null){
+ list = new MaterialList();
+ }
+ String[] split = statement.getLine().split(" ", 2);
+ matName = split[1].trim();
+ readMaterial(statement);
+ Material mat = compileMaterial();
+ list.put(matName, mat);
+ }
+ }
+
+ return list;
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ return load(info.getManager(), info.getKey(), in);
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
new file mode 100644
index 0000000..75ff865
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre;
+
+//import static com.jmex.model.XMLUtil.getAttribute;
+//import static com.jmex.model.XMLUtil.getIntAttribute;
+//
+//import java.util.ArrayList;
+//import java.util.List;
+//import java.util.Map;
+//
+//import org.w3c.dom.Node;
+//
+//import com.jme.math.Vector3f;
+//import com.jmex.model.XMLUtil;
+//import com.jmex.model.ogrexml.anim.PoseTrack.PoseFrame;
+
+/**
+ * Utility class used by OgreLoader to load poses and mesh animations.
+ */
+public class MeshAnimationLoader {
+
+// public static void loadMeshAnimations(Node animationsNode, List<Pose> poseList, OgreMesh sharedgeom, List<OgreMesh> submeshes, Map<String, Animation> animations){
+// Node animationNode = animationsNode.getFirstChild();
+// while (animationNode != null){
+// if (animationNode.getNodeName().equals("animation")){
+// MeshAnimation mAnim =
+// loadMeshAnimation(animationNode, poseList, sharedgeom, submeshes);
+//
+// Animation anim = animations.get(mAnim.getName());
+// if (anim != null){
+// anim.setMeshAnimation(mAnim);
+// }else{
+// anim = new Animation(null, mAnim);
+// animations.put(anim.getName(), anim);
+// }
+// }
+// animationNode = animationNode.getNextSibling();
+// }
+//
+//// Map<TriMesh, List<Pose>> trimeshPoses = new HashMap<TriMesh, List<Pose>>();
+////
+//// // find the poses for each mesh
+//// for (Pose p : poses){
+//// List<Pose> poseList = trimeshPoses.get(p.getTarget());
+//// if (poseList == null){
+//// poseList = new ArrayList<Pose>();
+//// trimeshPoses.put(p.getTarget(), poseList);
+//// }
+////
+//// poseList.add(p);
+//// }
+////
+//// for (Map.Entry<TriMesh, List<Pose>> poseEntry: trimeshPoses){
+//// PoseController
+//// }
+// }
+//
+// public static MeshAnimation loadMeshAnimation(Node animationNode, List<Pose> poseList, OgreMesh sharedgeom, List<OgreMesh> submeshes){
+// String name = XMLUtil.getAttribute(animationNode, "name");
+// float length = XMLUtil.getFloatAttribute(animationNode, "length");
+//
+// MeshAnimation anim = new MeshAnimation(name, length);
+// List<Track> tracks = new ArrayList<Track>();
+//
+// Node tracksNode = XMLUtil.getChildNode(animationNode, "tracks");
+// if (tracksNode != null){
+// Node trackNode = tracksNode.getFirstChild();
+// while (trackNode != null){
+// if (trackNode.getNodeName().equals("track")){
+// int targetMeshIndex;
+// if (XMLUtil.getAttribute(trackNode, "target").equals("mesh")){
+// targetMeshIndex = -1;
+// }else{
+// if (XMLUtil.getAttribute(trackNode, "index") == null)
+// targetMeshIndex = 0;
+// else
+// targetMeshIndex = getIntAttribute(trackNode, "index");
+// }
+//
+// if (XMLUtil.getAttribute(trackNode, "type").equals("pose")){
+// PoseTrack pt = loadPoseTrack(trackNode, targetMeshIndex, poseList);
+// tracks.add(pt);
+// }else{
+// throw new UnsupportedOperationException("Morph animations not supported!");
+// }
+// }
+//
+// trackNode = trackNode.getNextSibling();
+// }
+// }
+//
+// anim.setTracks(tracks.toArray(new Track[0]));
+//
+// return anim;
+// }
+//
+// public static List<Pose> loadPoses(Node posesNode, OgreMesh sharedgeom, List<OgreMesh> submeshes){
+// List<Pose> poses = new ArrayList<Pose>();
+// Node poseNode = posesNode.getFirstChild();
+// while (poseNode != null){
+// if (poseNode.getNodeName().equals("pose")){
+// int targetMeshIndex = 0;
+// if (getAttribute(poseNode, "target").equals("mesh")){
+// targetMeshIndex = -1;
+// }else{
+// if (getAttribute(poseNode, "index") == null)
+// targetMeshIndex = 0;
+// else
+// targetMeshIndex = getIntAttribute(poseNode, "index");
+// }
+//
+// Pose p = MeshAnimationLoader.loadPose(poseNode, targetMeshIndex);
+// poses.add(p);
+// }
+//
+// poseNode = poseNode.getNextSibling();
+// }
+//
+// return poses;
+// }
+//
+// public static Pose loadPose(Node poseNode, int targetMeshIndex){
+// String name = XMLUtil.getAttribute(poseNode, "name");
+//
+// List<Vector3f> offsets = new ArrayList<Vector3f>();
+// List<Integer> indices = new ArrayList<Integer>();
+//
+// Node poseoffsetNode = poseNode.getFirstChild();
+// while (poseoffsetNode != null){
+// if (poseoffsetNode.getNodeName().equals("poseoffset")){
+// int vertIndex = XMLUtil.getIntAttribute(poseoffsetNode, "index");
+// Vector3f offset = new Vector3f();
+// offset.x = XMLUtil.getFloatAttribute(poseoffsetNode, "x");
+// offset.y = XMLUtil.getFloatAttribute(poseoffsetNode, "y");
+// offset.z = XMLUtil.getFloatAttribute(poseoffsetNode, "z");
+//
+// offsets.add(offset);
+// indices.add(vertIndex);
+// }
+//
+// poseoffsetNode = poseoffsetNode.getNextSibling();
+// }
+//
+// int[] indicesArray = new int[indices.size()];
+// for (int i = 0; i < indicesArray.length; i++){
+// indicesArray[i] = indices.get(i);
+// }
+//
+// Pose pose = new Pose(name,
+// targetMeshIndex,
+// offsets.toArray(new Vector3f[0]),
+// indicesArray);
+//
+// return pose;
+// }
+//
+// public static PoseTrack loadPoseTrack(Node trackNode, int targetMeshIndex, List<Pose> posesList){
+// List<Float> times = new ArrayList<Float>();
+// List<PoseFrame> frames = new ArrayList<PoseFrame>();
+//
+// Node keyframesNode = XMLUtil.getChildNode(trackNode, "keyframes");
+// Node keyframeNode = keyframesNode.getFirstChild();
+// while (keyframeNode != null){
+// if (keyframeNode.getNodeName().equals("keyframe")){
+// float time = XMLUtil.getFloatAttribute(keyframeNode, "time");
+// List<Pose> poses = new ArrayList<Pose>();
+// List<Float> weights = new ArrayList<Float>();
+//
+// Node poserefNode = keyframeNode.getFirstChild();
+// while (poserefNode != null){
+// if (poserefNode.getNodeName().equals("poseref")){
+// int poseindex = XMLUtil.getIntAttribute(poserefNode, "poseindex");
+// poses.add(posesList.get(poseindex));
+// float weight = XMLUtil.getFloatAttribute(poserefNode, "influence");
+// weights.add(weight);
+// }
+//
+// poserefNode = poserefNode.getNextSibling();
+// }
+//
+// // convert poses and weights to arrays and create a PoseFrame
+// float[] weightsArray = new float[weights.size()];
+// for (int i = 0; i < weightsArray.length; i++){
+// weightsArray[i] = weights.get(i);
+// }
+// PoseFrame frame = new PoseFrame(poses.toArray(new Pose[0]), weightsArray);
+//
+// times.add(time);
+// frames.add(frame);
+// }
+//
+// keyframeNode = keyframeNode.getNextSibling();
+// }
+//
+// // convert times and frames to arrays and write to the track
+// float[] timesArray = new float[times.size()];
+// for (int i = 0; i < timesArray.length; i++){
+// timesArray[i] = times.get(i);
+// }
+//
+// PoseTrack track = new PoseTrack(targetMeshIndex,
+// timesArray,
+// frames.toArray(new PoseFrame[0]));
+//
+// return track;
+// }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
new file mode 100644
index 0000000..e7f4b10
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.ogre;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.PlaceholderAssets;
+import static com.jme3.util.xml.SAXUtil.*;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Loads Ogre3D mesh.xml files.
+ */
+public class MeshLoader extends DefaultHandler implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
+ public static boolean AUTO_INTERLEAVE = true;
+ public static boolean HARDWARE_SKINNING = false;
+ private static final Type[] TEXCOORD_TYPES =
+ new Type[]{
+ Type.TexCoord,
+ Type.TexCoord2,
+ Type.TexCoord3,
+ Type.TexCoord4,
+ Type.TexCoord5,
+ Type.TexCoord6,
+ Type.TexCoord7,
+ Type.TexCoord8,};
+ private AssetKey key;
+ private String meshName;
+ private String folderName;
+ private AssetManager assetManager;
+ private MaterialList materialList;
+ // Data per submesh/sharedgeom
+ private ShortBuffer sb;
+ private IntBuffer ib;
+ private FloatBuffer fb;
+ private VertexBuffer vb;
+ private Mesh mesh;
+ private Geometry geom;
+ private ByteBuffer indicesData;
+ private FloatBuffer weightsFloatData;
+ private boolean actuallyHasWeights = false;
+ private int vertCount;
+ private boolean usesSharedVerts;
+ private boolean usesBigIndices;
+ // Global data
+ private Mesh sharedMesh;
+ private int meshIndex = 0;
+ private int texCoordIndex = 0;
+ private String ignoreUntilEnd = null;
+ private List<Geometry> geoms = new ArrayList<Geometry>();
+ private ArrayList<Boolean> usesSharedMesh = new ArrayList<Boolean>();
+ private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
+ private AnimData animData;
+
+ public MeshLoader() {
+ super();
+ }
+
+ @Override
+ public void startDocument() {
+ geoms.clear();
+ lodLevels.clear();
+
+ sb = null;
+ ib = null;
+ fb = null;
+ vb = null;
+ mesh = null;
+ geom = null;
+ sharedMesh = null;
+
+ usesSharedMesh.clear();
+ usesSharedVerts = false;
+ vertCount = 0;
+ meshIndex = 0;
+ texCoordIndex = 0;
+ ignoreUntilEnd = null;
+
+ animData = null;
+
+ actuallyHasWeights = false;
+ indicesData = null;
+ weightsFloatData = null;
+ }
+
+ @Override
+ public void endDocument() {
+ }
+
+ private void pushIndex(int index){
+ if (ib != null){
+ ib.put(index);
+ }else{
+ sb.put((short)index);
+ }
+ }
+
+ private void pushFace(String v1, String v2, String v3) throws SAXException {
+ // TODO: fan/strip support
+ switch (mesh.getMode()){
+ case Triangles:
+ pushIndex(parseInt(v1));
+ pushIndex(parseInt(v2));
+ pushIndex(parseInt(v3));
+ break;
+ case Lines:
+ pushIndex(parseInt(v1));
+ pushIndex(parseInt(v2));
+ break;
+ case Points:
+ pushIndex(parseInt(v1));
+ break;
+ }
+ }
+
+// private boolean isUsingSharedVerts(Geometry geom) {
+ // Old code for buffer sharer
+ //return geom.getUserData(UserData.JME_SHAREDMESH) != null;
+// }
+
+ private void startFaces(String count) throws SAXException {
+ int numFaces = parseInt(count);
+ int indicesPerFace = 0;
+
+ switch (mesh.getMode()){
+ case Triangles:
+ indicesPerFace = 3;
+ break;
+ case Lines:
+ indicesPerFace = 2;
+ break;
+ case Points:
+ indicesPerFace = 1;
+ break;
+ default:
+ throw new SAXException("Strips or fans not supported!");
+ }
+
+ int numIndices = indicesPerFace * numFaces;
+
+ vb = new VertexBuffer(VertexBuffer.Type.Index);
+ if (!usesBigIndices) {
+ sb = BufferUtils.createShortBuffer(numIndices);
+ ib = null;
+ vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb);
+ } else {
+ ib = BufferUtils.createIntBuffer(numIndices);
+ sb = null;
+ vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib);
+ }
+ mesh.setBuffer(vb);
+ }
+
+ private void applyMaterial(Geometry geom, String matName) {
+ Material mat = null;
+ if (matName.endsWith(".j3m")) {
+ // load as native jme3 material instance
+ try {
+ mat = assetManager.loadMaterial(matName);
+ } catch (AssetNotFoundException ex){
+ // Warning will be raised (see below)
+ if (!ex.getMessage().equals(matName)){
+ throw ex;
+ }
+ }
+ } else {
+ if (materialList != null) {
+ mat = materialList.get(matName);
+ }
+ }
+
+ if (mat == null) {
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{matName, key});
+ mat = PlaceholderAssets.getPlaceholderMaterial(assetManager);
+ }
+
+ if (mat.isTransparent()) {
+ geom.setQueueBucket(Bucket.Transparent);
+ }
+
+ geom.setMaterial(mat);
+ }
+
+ private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException {
+ mesh = new Mesh();
+ if (opType == null || opType.equals("triangle_list")) {
+ mesh.setMode(Mesh.Mode.Triangles);
+ //} else if (opType.equals("triangle_strip")) {
+ // mesh.setMode(Mesh.Mode.TriangleStrip);
+ //} else if (opType.equals("triangle_fan")) {
+ // mesh.setMode(Mesh.Mode.TriangleFan);
+ } else if (opType.equals("line_list")) {
+ mesh.setMode(Mesh.Mode.Lines);
+ } else {
+ throw new SAXException("Unsupported operation type: " + opType);
+ }
+
+ usesBigIndices = parseBool(use32bitIndices, false);
+ usesSharedVerts = parseBool(usesharedvertices, false);
+ if (usesSharedVerts) {
+ usesSharedMesh.add(true);
+
+ // Old code for buffer sharer
+ // import vertexbuffers from shared geom
+// IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
+// for (Entry<VertexBuffer> entry : sharedBufs) {
+// mesh.setBuffer(entry.getValue());
+// }
+ }else{
+ usesSharedMesh.add(false);
+ }
+
+ if (meshName == null) {
+ geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh);
+ } else {
+ geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh);
+ }
+
+ if (usesSharedVerts) {
+ // Old code for buffer sharer
+ // this mesh is shared!
+ //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
+ }
+
+ applyMaterial(geom, matName);
+ geoms.add(geom);
+ }
+
+ private void startSharedGeom(String vertexcount) throws SAXException {
+ sharedMesh = new Mesh();
+ vertCount = parseInt(vertexcount);
+ usesSharedVerts = false;
+
+ geom = null;
+ mesh = sharedMesh;
+ }
+
+ private void startGeometry(String vertexcount) throws SAXException {
+ vertCount = parseInt(vertexcount);
+ }
+
+ /**
+ * Normalizes weights if needed and finds largest amount of weights used
+ * for all vertices in the buffer.
+ */
+ private void endBoneAssigns() {
+// if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
+// return;
+// }
+
+ if (!actuallyHasWeights){
+ // No weights were actually written (the tag didn't have any entries)
+ // remove those buffers
+ mesh.clearBuffer(Type.BoneIndex);
+ mesh.clearBuffer(Type.BoneWeight);
+
+ weightsFloatData = null;
+ indicesData = null;
+
+ return;
+ }
+
+ //int vertCount = mesh.getVertexCount();
+ int maxWeightsPerVert = 0;
+ weightsFloatData.rewind();
+ for (int v = 0; v < vertCount; v++) {
+ float w0 = weightsFloatData.get(),
+ w1 = weightsFloatData.get(),
+ w2 = weightsFloatData.get(),
+ w3 = weightsFloatData.get();
+
+ if (w3 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
+ } else if (w2 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
+ } else if (w1 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
+ } else if (w0 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
+ }
+
+ float sum = w0 + w1 + w2 + w3;
+ if (sum != 1f) {
+ weightsFloatData.position(weightsFloatData.position() - 4);
+ // compute new vals based on sum
+ float sumToB = 1f / sum;
+ weightsFloatData.put(w0 * sumToB);
+ weightsFloatData.put(w1 * sumToB);
+ weightsFloatData.put(w2 * sumToB);
+ weightsFloatData.put(w3 * sumToB);
+ }
+ }
+ weightsFloatData.rewind();
+
+ actuallyHasWeights = false;
+ weightsFloatData = null;
+ indicesData = null;
+
+ mesh.setMaxNumWeights(maxWeightsPerVert);
+ }
+
+ private void startBoneAssigns() {
+ if (mesh != sharedMesh && usesSharedVerts) {
+ // will use bone assignments from shared mesh (?)
+ return;
+ }
+
+ // current mesh will have bone assigns
+ //int vertCount = mesh.getVertexCount();
+ // each vertex has
+ // - 4 bone weights
+ // - 4 bone indices
+ if (HARDWARE_SKINNING) {
+ weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4);
+ indicesData = BufferUtils.createByteBuffer(vertCount * 4);
+ } else {
+ // create array-backed buffers if software skinning for access speed
+ weightsFloatData = FloatBuffer.allocate(vertCount * 4);
+ indicesData = ByteBuffer.allocate(vertCount * 4);
+ }
+
+ VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
+ VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
+
+ Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly;
+ weights.setupData(usage, 4, Format.Float, weightsFloatData);
+ indices.setupData(usage, 4, Format.UnsignedByte, indicesData);
+
+ mesh.setBuffer(weights);
+ mesh.setBuffer(indices);
+ }
+
+ private void startVertexBuffer(Attributes attribs) throws SAXException {
+ if (parseBool(attribs.getValue("positions"), false)) {
+ vb = new VertexBuffer(Type.Position);
+ fb = BufferUtils.createFloatBuffer(vertCount * 3);
+ vb.setupData(Usage.Static, 3, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("normals"), false)) {
+ vb = new VertexBuffer(Type.Normal);
+ fb = BufferUtils.createFloatBuffer(vertCount * 3);
+ vb.setupData(Usage.Static, 3, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("colours_diffuse"), false)) {
+ vb = new VertexBuffer(Type.Color);
+ fb = BufferUtils.createFloatBuffer(vertCount * 4);
+ vb.setupData(Usage.Static, 4, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("tangents"), false)) {
+ int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3);
+ vb = new VertexBuffer(Type.Tangent);
+ fb = BufferUtils.createFloatBuffer(vertCount * dimensions);
+ vb.setupData(Usage.Static, dimensions, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ if (parseBool(attribs.getValue("binormals"), false)) {
+ vb = new VertexBuffer(Type.Binormal);
+ fb = BufferUtils.createFloatBuffer(vertCount * 3);
+ vb.setupData(Usage.Static, 3, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+
+ int texCoords = parseInt(attribs.getValue("texture_coords"), 0);
+ for (int i = 0; i < texCoords; i++) {
+ int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2);
+ if (dims < 1 || dims > 4) {
+ throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4");
+ }
+
+ if (i <= 7) {
+ vb = new VertexBuffer(TEXCOORD_TYPES[i]);
+ } else {
+ // more than 8 texture coordinates are not supported by ogre.
+ throw new SAXException("More than 8 texture coordinates not supported");
+ }
+ fb = BufferUtils.createFloatBuffer(vertCount * dims);
+ vb.setupData(Usage.Static, dims, Format.Float, fb);
+ mesh.setBuffer(vb);
+ }
+ }
+
+ private void startVertex() {
+ texCoordIndex = 0;
+ }
+
+ private void pushAttrib(Type type, Attributes attribs) throws SAXException {
+ try {
+ FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData();
+ buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
+ } catch (Exception ex) {
+ throw new SAXException("Failed to push attrib", ex);
+ }
+ }
+
+ private void pushTangent(Attributes attribs) throws SAXException {
+ try {
+ VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent);
+ FloatBuffer buf = (FloatBuffer) tangentBuf.getData();
+ buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
+ if (tangentBuf.getNumComponents() == 4) {
+ buf.put(parseFloat(attribs.getValue("w")));
+ }
+ } catch (Exception ex) {
+ throw new SAXException("Failed to push attrib", ex);
+ }
+ }
+
+ private void pushTexCoord(Attributes attribs) throws SAXException {
+ if (texCoordIndex >= 8) {
+ return; // More than 8 not supported by ogre.
+ }
+ Type type = TEXCOORD_TYPES[texCoordIndex];
+
+ VertexBuffer tcvb = mesh.getBuffer(type);
+ FloatBuffer buf = (FloatBuffer) tcvb.getData();
+
+ buf.put(parseFloat(attribs.getValue("u")));
+ if (tcvb.getNumComponents() >= 2) {
+ buf.put(parseFloat(attribs.getValue("v")));
+ if (tcvb.getNumComponents() >= 3) {
+ buf.put(parseFloat(attribs.getValue("w")));
+ if (tcvb.getNumComponents() == 4) {
+ buf.put(parseFloat(attribs.getValue("x")));
+ }
+ }
+ }
+
+ texCoordIndex++;
+ }
+
+ private void pushColor(Attributes attribs) throws SAXException {
+ FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
+ String value = parseString(attribs.getValue("value"));
+ String[] vals = value.split("\\s");
+ if (vals.length != 3 && vals.length != 4) {
+ throw new SAXException("Color value must contain 3 or 4 components");
+ }
+
+ ColorRGBA color = new ColorRGBA();
+ color.r = parseFloat(vals[0]);
+ color.g = parseFloat(vals[1]);
+ color.b = parseFloat(vals[2]);
+ if (vals.length == 3) {
+ color.a = 1f;
+ } else {
+ color.a = parseFloat(vals[3]);
+ }
+
+ buf.put(color.r).put(color.g).put(color.b).put(color.a);
+ }
+
+ private void startLodFaceList(String submeshindex, String numfaces) {
+ int index = Integer.parseInt(submeshindex);
+ mesh = geoms.get(index).getMesh();
+ int faceCount = Integer.parseInt(numfaces);
+
+ VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index);
+ vb = new VertexBuffer(VertexBuffer.Type.Index);
+ if (originalIndexBuffer.getFormat() == Format.UnsignedInt){
+ // LOD buffer should also be integer
+ ib = BufferUtils.createIntBuffer(faceCount * 3);
+ sb = null;
+ vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
+ }else{
+ sb = BufferUtils.createShortBuffer(faceCount * 3);
+ ib = null;
+ vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
+ }
+
+ List<VertexBuffer> levels = lodLevels.get(index);
+ if (levels == null) {
+ // Create the LOD levels list
+ levels = new ArrayList<VertexBuffer>();
+
+ // Add the first LOD level (always the original index buffer)
+ levels.add(originalIndexBuffer);
+ lodLevels.put(index, levels);
+ }
+ levels.add(vb);
+ }
+
+ private void startLevelOfDetail(String numlevels) {
+// numLevels = Integer.parseInt(numlevels);
+ }
+
+ private void endLevelOfDetail() {
+ // set the lod data for each mesh
+ for (Entry<List<VertexBuffer>> entry : lodLevels) {
+ Mesh m = geoms.get(entry.getKey()).getMesh();
+ List<VertexBuffer> levels = entry.getValue();
+ VertexBuffer[] levelArray = new VertexBuffer[levels.size()];
+ levels.toArray(levelArray);
+ m.setLodLevels(levelArray);
+ }
+ }
+
+ private void startLodGenerated(String depthsqr) {
+ }
+
+ private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException {
+ int vert = parseInt(vertIndex);
+ float w = parseFloat(weight);
+ byte bone = (byte) parseInt(boneIndex);
+
+ assert bone >= 0;
+ assert vert >= 0 && vert < mesh.getVertexCount();
+
+ int i;
+ float v = 0;
+ // see which weights are unused for a given bone
+ for (i = vert * 4; i < vert * 4 + 4; i++) {
+ v = weightsFloatData.get(i);
+ if (v == 0) {
+ break;
+ }
+ }
+ if (v != 0) {
+ logger.log(Level.WARNING, "Vertex {0} has more than 4 weights per vertex! Ignoring..", vert);
+ return;
+ }
+
+ weightsFloatData.put(i, w);
+ indicesData.put(i, bone);
+ actuallyHasWeights = true;
+ }
+
+ private void startSkeleton(String name) {
+ AssetKey assetKey = new AssetKey(folderName + name + ".xml");
+ try {
+ animData = (AnimData) assetManager.loadAsset(assetKey);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{assetKey, key});
+ animData = null;
+ }
+ }
+
+ private void startSubmeshName(String indexStr, String nameStr) {
+ int index = Integer.parseInt(indexStr);
+ geoms.get(index).setName(nameStr);
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
+ if (ignoreUntilEnd != null) {
+ return;
+ }
+
+ if (qName.equals("texcoord")) {
+ pushTexCoord(attribs);
+ } else if (qName.equals("vertexboneassignment")) {
+ pushBoneAssign(attribs.getValue("vertexindex"),
+ attribs.getValue("boneindex"),
+ attribs.getValue("weight"));
+ } else if (qName.equals("face")) {
+ pushFace(attribs.getValue("v1"),
+ attribs.getValue("v2"),
+ attribs.getValue("v3"));
+ } else if (qName.equals("position")) {
+ pushAttrib(Type.Position, attribs);
+ } else if (qName.equals("normal")) {
+ pushAttrib(Type.Normal, attribs);
+ } else if (qName.equals("tangent")) {
+ pushTangent(attribs);
+ } else if (qName.equals("binormal")) {
+ pushAttrib(Type.Binormal, attribs);
+ } else if (qName.equals("colour_diffuse")) {
+ pushColor(attribs);
+ } else if (qName.equals("vertex")) {
+ startVertex();
+ } else if (qName.equals("faces")) {
+ startFaces(attribs.getValue("count"));
+ } else if (qName.equals("geometry")) {
+ String count = attribs.getValue("vertexcount");
+ if (count == null) {
+ count = attribs.getValue("count");
+ }
+ startGeometry(count);
+ } else if (qName.equals("vertexbuffer")) {
+ startVertexBuffer(attribs);
+ } else if (qName.equals("lodfacelist")) {
+ startLodFaceList(attribs.getValue("submeshindex"),
+ attribs.getValue("numfaces"));
+ } else if (qName.equals("lodgenerated")) {
+ startLodGenerated(attribs.getValue("fromdepthsquared"));
+ } else if (qName.equals("levelofdetail")) {
+ startLevelOfDetail(attribs.getValue("numlevels"));
+ } else if (qName.equals("boneassignments")) {
+ startBoneAssigns();
+ } else if (qName.equals("submesh")) {
+ startSubMesh(attribs.getValue("material"),
+ attribs.getValue("usesharedvertices"),
+ attribs.getValue("use32bitindexes"),
+ attribs.getValue("operationtype"));
+ } else if (qName.equals("sharedgeometry")) {
+ String count = attribs.getValue("vertexcount");
+ if (count == null) {
+ count = attribs.getValue("count");
+ }
+
+ if (count != null && !count.equals("0")) {
+ startSharedGeom(count);
+ }
+ } else if (qName.equals("submeshes")) {
+ // ok
+ } else if (qName.equals("skeletonlink")) {
+ startSkeleton(attribs.getValue("name"));
+ } else if (qName.equals("submeshnames")) {
+ // ok
+ } else if (qName.equals("submeshname")) {
+ startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
+ } else if (qName.equals("mesh")) {
+ // ok
+ } else {
+ logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
+ ignoreUntilEnd = qName;
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String name, String qName) {
+ if (ignoreUntilEnd != null) {
+ if (ignoreUntilEnd.equals(qName)) {
+ ignoreUntilEnd = null;
+ }
+ return;
+ }
+
+ if (qName.equals("submesh")) {
+ usesBigIndices = false;
+ geom = null;
+ mesh = null;
+ } else if (qName.equals("submeshes")) {
+ // IMPORTANT: restore sharedmesh, for use with shared boneweights
+ geom = null;
+ mesh = sharedMesh;
+ usesSharedVerts = false;
+ } else if (qName.equals("faces")) {
+ if (ib != null) {
+ ib.flip();
+ } else {
+ sb.flip();
+ }
+
+ vb = null;
+ ib = null;
+ sb = null;
+ } else if (qName.equals("vertexbuffer")) {
+ fb = null;
+ vb = null;
+ } else if (qName.equals("geometry")
+ || qName.equals("sharedgeometry")) {
+ // finish writing to buffers
+ IntMap<VertexBuffer> bufs = mesh.getBuffers();
+ for (Entry<VertexBuffer> entry : bufs) {
+ Buffer data = entry.getValue().getData();
+ if (data.position() != 0) {
+ data.flip();
+ }
+ }
+ mesh.updateBound();
+ mesh.setStatic();
+
+ if (qName.equals("sharedgeometry")) {
+ geom = null;
+ mesh = null;
+ }
+ } else if (qName.equals("lodfacelist")) {
+ sb.flip();
+ vb = null;
+ sb = null;
+ } else if (qName.equals("levelofdetail")) {
+ endLevelOfDetail();
+ } else if (qName.equals("boneassignments")) {
+ endBoneAssigns();
+ }
+ }
+
+ @Override
+ public void characters(char ch[], int start, int length) {
+ }
+
+ private Node compileModel() {
+ Node model = new Node(meshName + "-ogremesh");
+
+ for (int i = 0; i < geoms.size(); i++) {
+ Geometry g = geoms.get(i);
+ Mesh m = g.getMesh();
+
+ // New code for buffer extract
+ if (sharedMesh != null && usesSharedMesh.get(i)) {
+ m.extractVertexData(sharedMesh);
+ }
+
+ // Old code for buffer sharer
+ //if (sharedMesh != null && isUsingSharedVerts(g)) {
+ // m.setBound(sharedMesh.getBound().clone());
+ //}
+ model.attachChild(geoms.get(i));
+ }
+
+ // Do not attach shared geometry to the node!
+
+ if (animData != null) {
+ // This model uses animation
+
+ // Old code for buffer sharer
+ // generate bind pose for mesh
+ // ONLY if not using shared geometry
+ // This includes the shared geoemtry itself actually
+ //if (sharedMesh != null) {
+ // sharedMesh.generateBindPose(!HARDWARE_SKINNING);
+ //}
+
+ for (int i = 0; i < geoms.size(); i++) {
+ Geometry g = geoms.get(i);
+ Mesh m = geoms.get(i).getMesh();
+
+ m.generateBindPose(!HARDWARE_SKINNING);
+
+ // Old code for buffer sharer
+ //boolean useShared = isUsingSharedVerts(g);
+ //if (!useShared) {
+ // create bind pose
+ //m.generateBindPose(!HARDWARE_SKINNING);
+ //}
+ }
+
+ // Put the animations in the AnimControl
+ HashMap<String, Animation> anims = new HashMap<String, Animation>();
+ ArrayList<Animation> animList = animData.anims;
+ for (int i = 0; i < animList.size(); i++) {
+ Animation anim = animList.get(i);
+ anims.put(anim.getName(), anim);
+ }
+
+ AnimControl ctrl = new AnimControl(animData.skeleton);
+ ctrl.setAnimations(anims);
+ model.addControl(ctrl);
+
+ // Put the skeleton in the skeleton control
+ SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
+
+ // This will acquire the targets from the node
+ model.addControl(skeletonControl);
+ }
+
+ return model;
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ try {
+ key = info.getKey();
+ meshName = key.getName();
+ folderName = key.getFolder();
+ String ext = key.getExtension();
+ meshName = meshName.substring(0, meshName.length() - ext.length() - 1);
+ if (folderName != null && folderName.length() > 0) {
+ meshName = meshName.substring(folderName.length());
+ }
+ assetManager = info.getManager();
+
+ if (key instanceof OgreMeshKey) {
+ // OgreMeshKey is being used, try getting the material list
+ // from it
+ OgreMeshKey meshKey = (OgreMeshKey) key;
+ materialList = meshKey.getMaterialList();
+ String materialName = meshKey.getMaterialName();
+
+ // Material list not set but material name is available
+ if (materialList == null && materialName != null) {
+ OgreMaterialKey materialKey = new OgreMaterialKey(folderName + materialName + ".material");
+ try {
+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
+ } catch (AssetNotFoundException e) {
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key});
+ }
+ }
+ }else{
+ // Make sure to reset it to null so that previous state
+ // doesn't leak onto this one
+ materialList = null;
+ }
+
+ // If for some reason material list could not be found through
+ // OgreMeshKey, or if regular ModelKey specified, load using
+ // default method.
+ if (materialList == null){
+ OgreMaterialKey materialKey = new OgreMaterialKey(folderName + meshName + ".material");
+ try {
+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
+ } catch (AssetNotFoundException e) {
+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{ materialKey, key });
+ }
+ }
+
+ // Added by larynx 25.06.2011
+ // Android needs the namespace aware flag set to true
+ // Kirill 30.06.2011
+ // Now, hack is applied for both desktop and android to avoid
+ // checking with JmeSystem.
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+
+ XMLReader xr = factory.newSAXParser().getXMLReader();
+ xr.setContentHandler(this);
+ xr.setErrorHandler(this);
+
+ InputStreamReader r = null;
+ try {
+ r = new InputStreamReader(info.openStream());
+ xr.parse(new InputSource(r));
+ } finally {
+ if (r != null){
+ r.close();
+ }
+ }
+
+ return compileModel();
+ } catch (SAXException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
+ ioEx.initCause(ex);
+ throw ioEx;
+ } catch (ParserConfigurationException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
+ ioEx.initCause(ex);
+ throw ioEx;
+ }
+
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
new file mode 100644
index 0000000..1141944
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre;
+
+import com.jme3.asset.ModelKey;
+import com.jme3.material.MaterialList;
+
+/**
+ * OgreMeshKey is used to load Ogre3D mesh.xml models with a specific
+ * material file or list. This allows customizing from where the materials
+ * are retrieved, instead of loading the material file as the same
+ * name as the model (the default).
+ *
+ * @author Kirill Vainer
+ */
+public class OgreMeshKey extends ModelKey {
+
+ private MaterialList materialList;
+ private String materialName;
+
+ public OgreMeshKey(){
+ super();
+ }
+
+ public OgreMeshKey(String name){
+ super(name);
+ }
+
+ public OgreMeshKey(String name, MaterialList materialList){
+ super(name);
+ this.materialList = materialList;
+ }
+
+ public OgreMeshKey(String name, String materialName){
+ super(name);
+ this.materialName = materialName;
+ }
+
+ public MaterialList getMaterialList() {
+ return materialList;
+ }
+
+ public void setMaterialList(MaterialList materialList){
+ this.materialList = materialList;
+ }
+
+ public String getMaterialName() {
+ return materialName;
+ }
+
+ public void setMaterialName(String name) {
+ materialName = name;
+ }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
new file mode 100644
index 0000000..b59275e
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre;
+
+import com.jme3.asset.*;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.MaterialList;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.xml.SAXUtil;
+import static com.jme3.util.xml.SAXUtil.*;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Stack;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class SceneLoader extends DefaultHandler implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
+
+ private Stack<String> elementStack = new Stack<String>();
+ private AssetKey key;
+ private String sceneName;
+ private String folderName;
+ private AssetManager assetManager;
+ private MaterialList materialList;
+ private com.jme3.scene.Node root;
+ private com.jme3.scene.Node node;
+ private com.jme3.scene.Node entityNode;
+ private Light light;
+ private int nodeIdx = 0;
+ private static volatile int sceneIdx = 0;
+
+ public SceneLoader(){
+ super();
+ }
+
+ @Override
+ public void startDocument() {
+ }
+
+ @Override
+ public void endDocument() {
+ }
+
+ private void reset(){
+ elementStack.clear();
+ nodeIdx = 0;
+
+ // NOTE: Setting some of those to null is only needed
+ // if the parsed file had an error e.g. startElement was called
+ // but not endElement
+ root = null;
+ node = null;
+ entityNode = null;
+ light = null;
+ }
+
+ private void checkTopNode(String topNode) throws SAXException{
+ if (!elementStack.peek().equals(topNode)){
+ throw new SAXException("dotScene parse error: Expected parent node to be " + topNode);
+ }
+ }
+
+ private Quaternion parseQuat(Attributes attribs) throws SAXException{
+ if (attribs.getValue("x") != null){
+ // defined as quaternion
+ float x = parseFloat(attribs.getValue("x"));
+ float y = parseFloat(attribs.getValue("y"));
+ float z = parseFloat(attribs.getValue("z"));
+ float w = parseFloat(attribs.getValue("w"));
+ return new Quaternion(x,y,z,w);
+ }else if (attribs.getValue("qx") != null){
+ // defined as quaternion with prefix "q"
+ float x = parseFloat(attribs.getValue("qx"));
+ float y = parseFloat(attribs.getValue("qy"));
+ float z = parseFloat(attribs.getValue("qz"));
+ float w = parseFloat(attribs.getValue("qw"));
+ return new Quaternion(x,y,z,w);
+ }else if (attribs.getValue("angle") != null){
+ // defined as angle + axis
+ float angle = parseFloat(attribs.getValue("angle"));
+ float axisX = parseFloat(attribs.getValue("axisX"));
+ float axisY = parseFloat(attribs.getValue("axisY"));
+ float axisZ = parseFloat(attribs.getValue("axisZ"));
+ Quaternion q = new Quaternion();
+ q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));
+ return q;
+ }else{
+ // defines as 3 angles along XYZ axes
+ float angleX = parseFloat(attribs.getValue("angleX"));
+ float angleY = parseFloat(attribs.getValue("angleY"));
+ float angleZ = parseFloat(attribs.getValue("angleZ"));
+ Quaternion q = new Quaternion();
+ q.fromAngles(angleX, angleY, angleZ);
+ return q;
+ }
+ }
+
+ private void parseLightNormal(Attributes attribs) throws SAXException {
+ checkTopNode("light");
+
+ // SpotLight will be supporting a direction-normal, too.
+ if (light instanceof DirectionalLight)
+ ((DirectionalLight) light).setDirection(parseVector3(attribs));
+ else if (light instanceof SpotLight){
+ ((SpotLight) light).setDirection(parseVector3(attribs));
+ }
+ }
+
+ private void parseLightAttenuation(Attributes attribs) throws SAXException {
+ // NOTE: Derives range based on "linear" if it is used solely
+ // for the attenuation. Otherwise derives it from "range"
+ checkTopNode("light");
+
+ if (light instanceof PointLight || light instanceof SpotLight){
+ float range = parseFloat(attribs.getValue("range"));
+ float constant = parseFloat(attribs.getValue("constant"));
+ float linear = parseFloat(attribs.getValue("linear"));
+
+ String quadraticStr = attribs.getValue("quadratic");
+ if (quadraticStr == null)
+ quadraticStr = attribs.getValue("quadric");
+
+ float quadratic = parseFloat(quadraticStr);
+
+ if (constant == 1 && quadratic == 0 && linear > 0){
+ range = 1f / linear;
+ }
+
+ if (light instanceof PointLight){
+ ((PointLight) light).setRadius(range);
+ }else{
+ ((SpotLight)light).setSpotRange(range);
+ }
+ }
+ }
+
+ private void parseLightSpotLightRange(Attributes attribs) throws SAXException{
+ checkTopNode("light");
+
+ float outer = SAXUtil.parseFloat(attribs.getValue("outer"));
+ float inner = SAXUtil.parseFloat(attribs.getValue("inner"));
+
+ if (!(light instanceof SpotLight)){
+ throw new SAXException("dotScene parse error: spotLightRange "
+ + "can only appear under 'spot' light elements");
+ }
+
+ SpotLight sl = (SpotLight) light;
+ sl.setSpotInnerAngle(inner * 0.5f);
+ sl.setSpotOuterAngle(outer * 0.5f);
+ }
+
+ private void parseLight(Attributes attribs) throws SAXException {
+ if (node == null || node.getParent() == null)
+ throw new SAXException("dotScene parse error: light can only appear under a node");
+
+ checkTopNode("node");
+
+ String lightType = parseString(attribs.getValue("type"), "point");
+ if(lightType.equals("point")) {
+ light = new PointLight();
+ } else if(lightType.equals("directional") || lightType.equals("sun")) {
+ light = new DirectionalLight();
+ // Assuming "normal" property is not provided
+ ((DirectionalLight)light).setDirection(Vector3f.UNIT_Z);
+ } else if(lightType.equals("spotLight") || lightType.equals("spot")) {
+ light = new SpotLight();
+ } else {
+ logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType);
+ }
+ logger.log(Level.FINEST, "{0} created.", light);
+
+ if (!parseBool(attribs.getValue("visible"), true)){
+ // set to disabled
+ }
+
+ // "attach" it to the parent of this node
+ if (light != null)
+ node.getParent().addLight(light);
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{
+ if (qName.equals("scene")){
+ if (elementStack.size() != 0){
+ throw new SAXException("dotScene parse error: 'scene' element must be the root XML element");
+ }
+
+ String version = attribs.getValue("formatVersion");
+ if (version == null || (!version.equals("1.0.0") && !version.equals("1.0.1")))
+ logger.log(Level.WARNING, "Unrecognized version number"
+ + " in dotScene file: {0}", version);
+
+ }else if (qName.equals("nodes")){
+ if (root != null){
+ throw new SAXException("dotScene parse error: nodes element was specified twice");
+ }
+ if (sceneName == null)
+ root = new com.jme3.scene.Node("OgreDotScene"+(++sceneIdx));
+ else
+ root = new com.jme3.scene.Node(sceneName+"-scene_node");
+
+ node = root;
+ }else if (qName.equals("externals")){
+ checkTopNode("scene");
+ // Not loaded currently
+ }else if (qName.equals("item")){
+ checkTopNode("externals");
+ }else if (qName.equals("file")){
+ checkTopNode("item");
+
+ // XXX: Currently material file name is based
+ // on the scene's filename. THIS IS NOT CORRECT.
+ // To solve, port SceneLoader to use DOM instead of SAX
+
+ //String matFile = folderName+attribs.getValue("name");
+ //try {
+ // materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile));
+ //} catch (AssetNotFoundException ex){
+ // materialList = null;
+ // logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile);
+ //}
+ }else if (qName.equals("node")){
+ String curElement = elementStack.peek();
+ if (!curElement.equals("node") && !curElement.equals("nodes")){
+ throw new SAXException("dotScene parse error: "
+ + "node element can only appear under 'node' or 'nodes'");
+ }
+
+ String name = attribs.getValue("name");
+ if (name == null)
+ name = "OgreNode-" + (++nodeIdx);
+
+ com.jme3.scene.Node newNode = new com.jme3.scene.Node(name);
+ if (node != null){
+ node.attachChild(newNode);
+ }
+ node = newNode;
+ }else if (qName.equals("property")){
+ if (node != null){
+ String type = attribs.getValue("type");
+ String name = attribs.getValue("name");
+ String data = attribs.getValue("data");
+ if (type.equals("BOOL")){
+ node.setUserData(name, Boolean.parseBoolean(data)||data.equals("1"));
+ }else if (type.equals("FLOAT")){
+ node.setUserData(name, Float.parseFloat(data));
+ }else if (type.equals("STRING")){
+ node.setUserData(name, data);
+ }else if (type.equals("INT")){
+ node.setUserData(name, Integer.parseInt(data));
+ }
+ }
+ }else if (qName.equals("entity")){
+ checkTopNode("node");
+
+ String name = attribs.getValue("name");
+ if (name == null)
+ name = "OgreEntity-" + (++nodeIdx);
+ else
+ name += "-entity";
+
+ String meshFile = attribs.getValue("meshFile");
+ if (meshFile == null) {
+ throw new SAXException("Required attribute 'meshFile' missing for 'entity' node");
+ }
+
+ // TODO: Not currently used
+ String materialName = attribs.getValue("materialName");
+
+ if (folderName != null) {
+ meshFile = folderName + meshFile;
+ }
+
+ // NOTE: append "xml" since its assumed mesh files are binary in dotScene
+ meshFile += ".xml";
+
+ entityNode = new com.jme3.scene.Node(name);
+ OgreMeshKey meshKey = new OgreMeshKey(meshFile, materialList);
+ try {
+ Spatial ogreMesh = assetManager.loadModel(meshKey);
+ entityNode.attachChild(ogreMesh);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{meshKey, key});
+ // Attach placeholder asset.
+ entityNode.attachChild(PlaceholderAssets.getPlaceholderModel(assetManager));
+ }
+
+ node.attachChild(entityNode);
+ node = null;
+ }else if (qName.equals("position")){
+ if (elementStack.peek().equals("node")){
+ node.setLocalTranslation(SAXUtil.parseVector3(attribs));
+ }
+ }else if (qName.equals("quaternion") || qName.equals("rotation")){
+ node.setLocalRotation(parseQuat(attribs));
+ }else if (qName.equals("scale")){
+ node.setLocalScale(SAXUtil.parseVector3(attribs));
+ } else if (qName.equals("light")) {
+ parseLight(attribs);
+ } else if (qName.equals("colourDiffuse") || qName.equals("colorDiffuse")) {
+ if (elementStack.peek().equals("light")){
+ if (light != null){
+ light.setColor(parseColor(attribs));
+ }
+ }else{
+ checkTopNode("environment");
+ }
+ } else if (qName.equals("normal") || qName.equals("direction")) {
+ checkTopNode("light");
+ parseLightNormal(attribs);
+ } else if (qName.equals("lightAttenuation")) {
+ parseLightAttenuation(attribs);
+ } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {
+ parseLightSpotLightRange(attribs);
+ }
+
+ elementStack.push(qName);
+ }
+
+ @Override
+ public void endElement(String uri, String name, String qName) throws SAXException {
+ if (qName.equals("node")){
+ node = node.getParent();
+ }else if (qName.equals("nodes")){
+ node = null;
+ }else if (qName.equals("entity")){
+ node = entityNode.getParent();
+ entityNode = null;
+ }else if (qName.equals("light")){
+ // apply the node's world transform on the light..
+ root.updateGeometricState();
+ if (light != null){
+ if (light instanceof DirectionalLight){
+ DirectionalLight dl = (DirectionalLight) light;
+ Quaternion q = node.getWorldRotation();
+ Vector3f dir = dl.getDirection();
+ q.multLocal(dir);
+ dl.setDirection(dir);
+ }else if (light instanceof PointLight){
+ PointLight pl = (PointLight) light;
+ Vector3f pos = node.getWorldTranslation();
+ pl.setPosition(pos);
+ }else if (light instanceof SpotLight){
+ SpotLight sl = (SpotLight) light;
+
+ Vector3f pos = node.getWorldTranslation();
+ sl.setPosition(pos);
+
+ Quaternion q = node.getWorldRotation();
+ Vector3f dir = sl.getDirection();
+ q.multLocal(dir);
+ sl.setDirection(dir);
+ }
+ }
+ light = null;
+ }
+ checkTopNode(qName);
+ elementStack.pop();
+ }
+
+ @Override
+ public void characters(char ch[], int start, int length) {
+ }
+
+
+
+ public Object load(AssetInfo info) throws IOException {
+ try{
+ key = info.getKey();
+ assetManager = info.getManager();
+ sceneName = key.getName();
+ String ext = key.getExtension();
+ folderName = key.getFolder();
+ sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1);
+
+ OgreMaterialKey materialKey = new OgreMaterialKey(sceneName+".material");
+ try {
+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key});
+ materialList = null;
+ }
+
+ reset();
+
+ // Added by larynx 25.06.2011
+ // Android needs the namespace aware flag set to true
+ // Kirill 30.06.2011
+ // Now, hack is applied for both desktop and android to avoid
+ // checking with JmeSystem.
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ XMLReader xr = factory.newSAXParser().getXMLReader();
+
+ xr.setContentHandler(this);
+ xr.setErrorHandler(this);
+
+ InputStreamReader r = null;
+
+ try {
+ r = new InputStreamReader(info.openStream());
+ xr.parse(new InputSource(r));
+ } finally {
+ if (r != null){
+ r.close();
+ }
+ }
+
+ return root;
+ }catch (SAXException ex){
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ throw ioEx;
+ } catch (ParserConfigurationException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ throw ioEx;
+ }
+ }
+
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
new file mode 100644
index 0000000..c96a7b7
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.ogre;
+
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.util.xml.SAXUtil;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+import java.util.logging.Logger;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class SkeletonLoader extends DefaultHandler implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
+ private AssetManager assetManager;
+ private Stack<String> elementStack = new Stack<String>();
+ private HashMap<Integer, Bone> indexToBone = new HashMap<Integer, Bone>();
+ private HashMap<String, Bone> nameToBone = new HashMap<String, Bone>();
+ private BoneTrack track;
+ private ArrayList<BoneTrack> tracks = new ArrayList<BoneTrack>();
+ private Animation animation;
+ private ArrayList<Animation> animations;
+ private Bone bone;
+ private Skeleton skeleton;
+ private ArrayList<Float> times = new ArrayList<Float>();
+ private ArrayList<Vector3f> translations = new ArrayList<Vector3f>();
+ private ArrayList<Quaternion> rotations = new ArrayList<Quaternion>();
+ private ArrayList<Vector3f> scales = new ArrayList<Vector3f>();
+ private float time = -1;
+ private Vector3f position;
+ private Quaternion rotation;
+ private Vector3f scale;
+ private float angle;
+ private Vector3f axis;
+
+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
+ if (qName.equals("position") || qName.equals("translate")) {
+ position = SAXUtil.parseVector3(attribs);
+ } else if (qName.equals("rotation") || qName.equals("rotate")) {
+ angle = SAXUtil.parseFloat(attribs.getValue("angle"));
+ } else if (qName.equals("axis")) {
+ assert elementStack.peek().equals("rotation")
+ || elementStack.peek().equals("rotate");
+ axis = SAXUtil.parseVector3(attribs);
+ } else if (qName.equals("scale")) {
+ scale = SAXUtil.parseVector3(attribs);
+ } else if (qName.equals("keyframe")) {
+ assert elementStack.peek().equals("keyframes");
+ time = SAXUtil.parseFloat(attribs.getValue("time"));
+ } else if (qName.equals("keyframes")) {
+ assert elementStack.peek().equals("track");
+ } else if (qName.equals("track")) {
+ assert elementStack.peek().equals("tracks");
+ String boneName = SAXUtil.parseString(attribs.getValue("bone"));
+ Bone bone = nameToBone.get(boneName);
+ int index = skeleton.getBoneIndex(bone);
+ track = new BoneTrack(index);
+ } else if (qName.equals("boneparent")) {
+ assert elementStack.peek().equals("bonehierarchy");
+ String boneName = attribs.getValue("bone");
+ String parentName = attribs.getValue("parent");
+ Bone bone = nameToBone.get(boneName);
+ Bone parent = nameToBone.get(parentName);
+ parent.addChild(bone);
+ } else if (qName.equals("bone")) {
+ assert elementStack.peek().equals("bones");
+
+ // insert bone into indexed map
+ bone = new Bone(attribs.getValue("name"));
+ int id = SAXUtil.parseInt(attribs.getValue("id"));
+ indexToBone.put(id, bone);
+ nameToBone.put(bone.getName(), bone);
+ } else if (qName.equals("tracks")) {
+ assert elementStack.peek().equals("animation");
+ tracks.clear();
+ } else if (qName.equals("animation")) {
+ assert elementStack.peek().equals("animations");
+ String name = SAXUtil.parseString(attribs.getValue("name"));
+ float length = SAXUtil.parseFloat(attribs.getValue("length"));
+ animation = new Animation(name, length);
+ } else if (qName.equals("bonehierarchy")) {
+ assert elementStack.peek().equals("skeleton");
+ } else if (qName.equals("animations")) {
+ assert elementStack.peek().equals("skeleton");
+ animations = new ArrayList<Animation>();
+ } else if (qName.equals("bones")) {
+ assert elementStack.peek().equals("skeleton");
+ } else if (qName.equals("skeleton")) {
+ assert elementStack.size() == 0;
+ }
+ elementStack.add(qName);
+ }
+
+ public void endElement(String uri, String name, String qName) {
+ if (qName.equals("translate") || qName.equals("position") || qName.equals("scale")) {
+ } else if (qName.equals("axis")) {
+ } else if (qName.equals("rotate") || qName.equals("rotation")) {
+ rotation = new Quaternion();
+ axis.normalizeLocal();
+ rotation.fromAngleNormalAxis(angle, axis);
+ angle = 0;
+ axis = null;
+ } else if (qName.equals("bone")) {
+ bone.setBindTransforms(position, rotation, scale);
+ bone = null;
+ position = null;
+ rotation = null;
+ scale = null;
+ } else if (qName.equals("bonehierarchy")) {
+ Bone[] bones = new Bone[indexToBone.size()];
+ // find bones without a parent and attach them to the skeleton
+ // also assign the bones to the bonelist
+ for (Map.Entry<Integer, Bone> entry : indexToBone.entrySet()) {
+ Bone bone = entry.getValue();
+ bones[entry.getKey()] = bone;
+ }
+ indexToBone.clear();
+ skeleton = new Skeleton(bones);
+ } else if (qName.equals("animation")) {
+ animations.add(animation);
+ animation = null;
+ } else if (qName.equals("track")) {
+ if (track != null) { // if track has keyframes
+ tracks.add(track);
+ track = null;
+ }
+ } else if (qName.equals("tracks")) {
+ BoneTrack[] trackList = tracks.toArray(new BoneTrack[tracks.size()]);
+ animation.setTracks(trackList);
+ tracks.clear();
+ } else if (qName.equals("keyframe")) {
+ assert time >= 0;
+ assert position != null;
+ assert rotation != null;
+
+ times.add(time);
+ translations.add(position);
+ rotations.add(rotation);
+ if (scale != null) {
+ scales.add(scale);
+ }else{
+ scales.add(new Vector3f(1,1,1));
+ }
+
+ time = -1;
+ position = null;
+ rotation = null;
+ scale = null;
+ } else if (qName.equals("keyframes")) {
+ if (times.size() > 0) {
+ float[] timesArray = new float[times.size()];
+ for (int i = 0; i < timesArray.length; i++) {
+ timesArray[i] = times.get(i);
+ }
+
+ Vector3f[] transArray = translations.toArray(new Vector3f[translations.size()]);
+ Quaternion[] rotArray = rotations.toArray(new Quaternion[rotations.size()]);
+ Vector3f[] scalesArray = scales.toArray(new Vector3f[scales.size()]);
+
+ track.setKeyframes(timesArray, transArray, rotArray, scalesArray);
+ //track.setKeyframes(timesArray, transArray, rotArray);
+ } else {
+ track = null;
+ }
+
+ times.clear();
+ translations.clear();
+ rotations.clear();
+ scales.clear();
+ } else if (qName.equals("skeleton")) {
+ nameToBone.clear();
+ }
+ assert elementStack.peek().equals(qName);
+ elementStack.pop();
+ }
+
+ /**
+ * Reset the SkeletonLoader in case an error occured while parsing XML.
+ * This allows future use of the loader even after an error.
+ */
+ private void fullReset() {
+ elementStack.clear();
+ indexToBone.clear();
+ nameToBone.clear();
+ track = null;
+ tracks.clear();
+ animation = null;
+ if (animations != null) {
+ animations.clear();
+ }
+
+ bone = null;
+ skeleton = null;
+ times.clear();
+ rotations.clear();
+ translations.clear();
+ time = -1;
+ position = null;
+ rotation = null;
+ scale = null;
+ angle = 0;
+ axis = null;
+ }
+
+ public Object load(InputStream in) throws IOException {
+ try {
+
+ // Added by larynx 25.06.2011
+ // Android needs the namespace aware flag set to true
+ // Kirill 30.06.2011
+ // Now, hack is applied for both desktop and android to avoid
+ // checking with JmeSystem.
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ XMLReader xr = factory.newSAXParser().getXMLReader();
+
+ xr.setContentHandler(this);
+ xr.setErrorHandler(this);
+ InputStreamReader r = new InputStreamReader(in);
+ xr.parse(new InputSource(r));
+ if (animations == null) {
+ animations = new ArrayList<Animation>();
+ }
+ AnimData data = new AnimData(skeleton, animations);
+ skeleton = null;
+ animations = null;
+ return data;
+ } catch (SAXException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ fullReset();
+ throw ioEx;
+ } catch (ParserConfigurationException ex) {
+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
+ ioEx.initCause(ex);
+ fullReset();
+ throw ioEx;
+ }
+
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ assetManager = info.getManager();
+ InputStream in = null;
+ try {
+ in = info.openStream();
+ return load(in);
+ } finally {
+ if (in != null){
+ in.close();
+ }
+ }
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
new file mode 100644
index 0000000..d68b7ae
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre.matext;
+
+import java.util.HashMap;
+
+/**
+ * <code>MaterialExtension</code> defines a mapping from an Ogre3D "base" material
+ * to a jME3 material definition.
+ */
+public class MaterialExtension {
+
+ private String baseMatName;
+ private String jmeMatDefName;
+ private HashMap<String, String> textureMappings = new HashMap<String, String>();
+
+ /**
+ * Material extension defines a mapping from an Ogre3D "base" material
+ * to a jME3 material definition.
+ *
+ * @param baseMatName The base material name for Ogre3D
+ * @param jmeMatDefName The material definition name for jME3
+ */
+ public MaterialExtension(String baseMatName, String jmeMatDefName) {
+ this.baseMatName = baseMatName;
+ this.jmeMatDefName = jmeMatDefName;
+ }
+
+ public String getBaseMaterialName() {
+ return baseMatName;
+ }
+
+ public String getJmeMatDefName() {
+ return jmeMatDefName;
+ }
+
+ /**
+ * Set mapping from an Ogre3D base material texture alias to a
+ * jME3 texture param
+ * @param ogreTexAlias The texture alias in the Ogre3D base material
+ * @param jmeTexParam The texture param name in the jME3 material definition.
+ */
+ public void setTextureMapping(String ogreTexAlias, String jmeTexParam){
+ textureMappings.put(ogreTexAlias, jmeTexParam);
+ }
+
+ /**
+ * Retreives a mapping from an Ogre3D base material texture alias
+ * to a jME3 texture param
+ * @param ogreTexAlias The texture alias in the Ogre3D base material
+ * @return The texture alias in the Ogre3D base material
+ */
+ public String getTextureMapping(String ogreTexAlias){
+ return textureMappings.get(ogreTexAlias);
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
new file mode 100644
index 0000000..d497d51
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre.matext;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.scene.plugins.ogre.MaterialLoader;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Used internally by {@link MaterialLoader}
+ */
+public class MaterialExtensionLoader {
+
+ private static final Logger logger = Logger.getLogger(MaterialExtensionLoader.class.getName());
+
+ private AssetKey key;
+ private AssetManager assetManager;
+ private MaterialList list;
+ private MaterialExtensionSet matExts;
+ private MaterialExtension matExt;
+ private String matName;
+ private Material material;
+
+
+ private void readExtendingMaterialStatement(Statement statement) throws IOException {
+ if (statement.getLine().startsWith("set_texture_alias")){
+ String[] split = statement.getLine().split(" ", 3);
+ String aliasName = split[1];
+ String texturePath = split[2];
+
+ String jmeParamName = matExt.getTextureMapping(aliasName);
+
+ TextureKey texKey = new TextureKey(texturePath, false);
+ texKey.setGenerateMips(true);
+ texKey.setAsCube(false);
+ Texture tex;
+
+ try {
+ tex = assetManager.loadTexture(texKey);
+ tex.setWrap(WrapMode.Repeat);
+ } catch (AssetNotFoundException ex){
+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key});
+ tex = new Texture2D( PlaceholderAssets.getPlaceholderImage() );
+ }
+
+ material.setTexture(jmeParamName, tex);
+ }
+ }
+
+ private Material readExtendingMaterial(Statement statement) throws IOException{
+ String[] split = statement.getLine().split(" ", 2);
+ String[] subsplit = split[1].split(":");
+ matName = subsplit[0].trim();
+ String extendedMat = subsplit[1].trim();
+
+ matExt = matExts.getMaterialExtension(extendedMat);
+ if (matExt == null){
+ logger.log(Level.WARNING, "Cannot find MaterialExtension for: {0}. Ignoring material.", extendedMat);
+ matExt = null;
+ return null;
+ }
+
+ material = new Material(assetManager, matExt.getJmeMatDefName());
+ for (Statement extMatStat : statement.getContents()){
+ readExtendingMaterialStatement(extMatStat);
+ }
+ return material;
+ }
+
+ public MaterialList load(AssetManager assetManager, AssetKey key, MaterialExtensionSet matExts,
+ List<Statement> statements) throws IOException{
+ this.assetManager = assetManager;
+ this.matExts = matExts;
+ this.key = key;
+
+ list = new MaterialList();
+
+ for (Statement statement : statements){
+ if (statement.getLine().startsWith("import")){
+ // ignore
+ continue;
+ }else if (statement.getLine().startsWith("material")){
+ Material material = readExtendingMaterial(statement);
+ list.put(matName, material);
+ List<String> matAliases = matExts.getNameMappings(matName);
+ if(matAliases != null){
+ for (String string : matAliases) {
+ list.put(string, material);
+ }
+ }
+ }
+ }
+ return list;
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
new file mode 100644
index 0000000..2c81e7b
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre.matext;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <code>MaterialExtensionSet</code> is simply a container for several
+ * {@link MaterialExtension}s so that it can be set globally for all
+ * {@link OgreMaterialKey}s used.
+ */
+public class MaterialExtensionSet {
+ private HashMap<String, MaterialExtension> extensions
+ = new HashMap<String, MaterialExtension>();
+ private HashMap<String, List<String>> nameMappings = new HashMap<String, List<String>>();
+
+ /**
+ * Adds a new material extension to the set of extensions.
+ * @param extension The {@link MaterialExtension} to add.
+ */
+ public void addMaterialExtension(MaterialExtension extension){
+ extensions.put(extension.getBaseMaterialName(), extension);
+ }
+
+ /**
+ * Returns the {@link MaterialExtension} for a given Ogre3D base
+ * material name.
+ *
+ * @param baseMatName The ogre3D base material name.
+ * @return {@link MaterialExtension} that is set, or null if not set.
+ */
+ public MaterialExtension getMaterialExtension(String baseMatName){
+ return extensions.get(baseMatName);
+ }
+
+ /**
+ * Adds an alternative name for a material
+ * @param name The material name to be found in a .mesh.xml file
+ * @param alias The material name to be found in a .material file
+ */
+ public void setNameMapping(String name, String alias){
+ List<String> list = nameMappings.get(name);
+ if(list==null){
+ list = new ArrayList<String>();
+ nameMappings.put(name, list);
+ }
+ list.add(alias);
+ }
+
+ public List<String> getNameMappings(String name){
+ return nameMappings.get(name);
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java
new file mode 100644
index 0000000..fa57d48
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins.ogre.matext;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.material.MaterialList;
+
+/**
+ * <code>OgreMaterialKey</code> allows specifying material extensions,
+ * which map from Ogre3D base materials to jME3 materials
+ */
+public class OgreMaterialKey extends AssetKey<MaterialList> {
+
+ private MaterialExtensionSet matExts;
+
+ public OgreMaterialKey(String name){
+ super(name);
+ }
+
+ public OgreMaterialKey(){
+ super();
+ }
+
+ /**
+ * Set the {@link MaterialExtensionSet} to use for mapping
+ * base materials to jME3 matdefs when loading.
+ * Set to <code>null</code> to disable this functionality.
+ *
+ * @param matExts The {@link MaterialExtensionSet} to use
+ */
+ public void setMaterialExtensionSet(MaterialExtensionSet matExts){
+ this.matExts = matExts;
+ }
+
+ /**
+ * Returns the {@link MaterialExtensionSet} previously set using
+ * {@link OgreMaterialKey#setMaterialExtensionSet(com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet) } method.
+ * @return
+ */
+ public MaterialExtensionSet getMaterialExtensionSet() {
+ return matExts;
+ }
+}
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html
new file mode 100644
index 0000000..0b2725c
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/package.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+<code>com.jme3.scene.plugins.ogre.matext</code> allows loading of more advanced
+Ogre3D materials that use "base" materials to abstract functionality.
+<br/><br/>
+E.g. example of an Ogre3D material instance:<br/>
+<code>
+import * from "materials/baselighting.material"
+
+material MyMaterial : BaseLightingMaterial
+{
+ set_texture_alias MyTexture textures/mytex.png
+}
+</code>
+
+<h3>Usage</h3>
+
+<p>
+Example code of loading the above material:<br/>
+<code>
+MaterialExtensionSet matExts = new MaterialExtensionSet();<br/>
+MaterialExtension baseLightExt = new MaterialExtension("BaseLightingMaterial", <br/>
+ "Common/MatDefs/Light/Lighting.j3md");<br/>
+baseLightExt.setTextureMapping("MyTexture", "m_DiffuseMap");<br/>
+matExts.addMaterialExtension(baseLightExt);<br/>
+<br/>
+OgreMaterialKey matKey = new OgreMaterialKey("materials/mymaterial.material");<br/>
+matKey.setMaterialExtensionSet(matExts);<br/>
+MaterialList ogreMats = assetManager.loadAsset(matKey);<br/>
+</code>
+
+</body>
+</html>
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.frag b/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.frag
new file mode 100644
index 0000000..b0b594c
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.frag
@@ -0,0 +1,76 @@
+uniform vec3 m_region1;
+uniform vec3 m_region2;
+uniform vec3 m_region3;
+uniform vec3 m_region4;
+
+uniform sampler2D m_region1ColorMap;
+uniform sampler2D m_region2ColorMap;
+uniform sampler2D m_region3ColorMap;
+uniform sampler2D m_region4ColorMap;
+uniform sampler2D m_slopeColorMap;
+
+uniform float m_slopeTileFactor;
+uniform float m_terrainSize;
+
+varying vec3 normal;
+varying vec4 position;
+
+vec4 GenerateTerrainColor() {
+ float height = position.y;
+ vec4 p = position / m_terrainSize;
+
+ vec3 blend = abs( normal );
+ blend = (blend -0.2) * 0.7;
+ blend = normalize(max(blend, 0.00001)); // Force weights to sum to 1.0 (very important!)
+ float b = (blend.x + blend.y + blend.z);
+ blend /= vec3(b, b, b);
+
+ vec4 terrainColor = vec4(0.0, 0.0, 0.0, 1.0);
+
+ float m_regionMin = 0.0;
+ float m_regionMax = 0.0;
+ float m_regionRange = 0.0;
+ float m_regionWeight = 0.0;
+
+ vec4 slopeCol1 = texture2D(m_slopeColorMap, p.yz * m_slopeTileFactor);
+ vec4 slopeCol2 = texture2D(m_slopeColorMap, p.xy * m_slopeTileFactor);
+
+ // Terrain m_region 1.
+ m_regionMin = m_region1.x;
+ m_regionMax = m_region1.y;
+ m_regionRange = m_regionMax - m_regionMin;
+ m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
+ m_regionWeight = max(0.0, m_regionWeight);
+ terrainColor += m_regionWeight * texture2D(m_region1ColorMap, p.xz * m_region1.z);
+
+ // Terrain m_region 2.
+ m_regionMin = m_region2.x;
+ m_regionMax = m_region2.y;
+ m_regionRange = m_regionMax - m_regionMin;
+ m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
+ m_regionWeight = max(0.0, m_regionWeight);
+ terrainColor += m_regionWeight * (texture2D(m_region2ColorMap, p.xz * m_region2.z));
+
+ // Terrain m_region 3.
+ m_regionMin = m_region3.x;
+ m_regionMax = m_region3.y;
+ m_regionRange = m_regionMax - m_regionMin;
+ m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
+ m_regionWeight = max(0.0, m_regionWeight);
+ terrainColor += m_regionWeight * texture2D(m_region3ColorMap, p.xz * m_region3.z);
+
+ // Terrain m_region 4.
+ m_regionMin = m_region4.x;
+ m_regionMax = m_region4.y;
+ m_regionRange = m_regionMax - m_regionMin;
+ m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
+ m_regionWeight = max(0.0, m_regionWeight);
+ terrainColor += m_regionWeight * texture2D(m_region4ColorMap, p.xz * m_region4.z);
+
+ return (blend.y * terrainColor + blend.x * slopeCol1 + blend.z * slopeCol2);
+}
+
+void main() {
+ vec4 color = GenerateTerrainColor();
+ gl_FragColor = color;
+}
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.j3md b/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.j3md
new file mode 100644
index 0000000..4b836f0
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.j3md
@@ -0,0 +1,41 @@
+MaterialDef Terrain {
+
+ // Parameters to material:
+ // regionXColorMap: X = 1..4 the texture that should be appliad to state X
+ // regionX: a Vector3f containing the following information:
+ // regionX.x: the start height of the region
+ // regionX.y: the end height of the region
+ // regionX.z: the texture scale for the region
+ // it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
+ // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
+ // slopeTileFactor: the texture scale for slopes
+ // terrainSize: the total size of the terrain (used for scaling the texture)
+ MaterialParameters {
+ Texture2D region1ColorMap
+ Texture2D region2ColorMap
+ Texture2D region3ColorMap
+ Texture2D region4ColorMap
+ Texture2D slopeColorMap
+ Float slopeTileFactor
+ Float terrainSize
+ Vector3 region1
+ Vector3 region2
+ Vector3 region3
+ Vector3 region4
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Terrain/HeightBasedTerrain.vert
+ FragmentShader GLSL100: Common/MatDefs/Terrain/HeightBasedTerrain.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ NormalMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.vert b/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.vert
new file mode 100644
index 0000000..8260d8f
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.vert
@@ -0,0 +1,22 @@
+uniform float m_tilingFactor;
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+uniform mat3 g_NormalMatrix;
+
+uniform float m_terrainSize;
+
+attribute vec4 inTexCoord;
+attribute vec3 inNormal;
+attribute vec3 inPosition;
+
+varying vec3 normal;
+varying vec4 position;
+
+void main()
+{
+ normal = normalize(inNormal);
+ position = g_WorldMatrix * vec4(inPosition, 0.0);
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1);
+}
+
+
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/Terrain.frag b/engine/src/terrain/Common/MatDefs/Terrain/Terrain.frag
new file mode 100644
index 0000000..7ae56cb
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/Terrain.frag
@@ -0,0 +1,63 @@
+uniform sampler2D m_Alpha;
+uniform sampler2D m_Tex1;
+uniform sampler2D m_Tex2;
+uniform sampler2D m_Tex3;
+uniform float m_Tex1Scale;
+uniform float m_Tex2Scale;
+uniform float m_Tex3Scale;
+
+varying vec2 texCoord;
+
+#ifdef TRI_PLANAR_MAPPING
+ varying vec4 vVertex;
+ varying vec3 vNormal;
+#endif
+
+void main(void)
+{
+
+ // get the alpha value at this 2D texture coord
+ vec4 alpha = texture2D( m_Alpha, texCoord.xy );
+
+#ifdef TRI_PLANAR_MAPPING
+ // tri-planar texture bending factor for this fragment's normal
+ vec3 blending = abs( vNormal );
+ blending = (blending -0.2) * 0.7;
+ blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0 (very important!)
+ float b = (blending.x + blending.y + blending.z);
+ blending /= vec3(b, b, b);
+
+ // texture coords
+ vec4 coords = vVertex;
+
+ vec4 col1 = texture2D( m_Tex1, coords.yz * m_Tex1Scale );
+ vec4 col2 = texture2D( m_Tex1, coords.xz * m_Tex1Scale );
+ vec4 col3 = texture2D( m_Tex1, coords.xy * m_Tex1Scale );
+ // blend the results of the 3 planar projections.
+ vec4 tex1 = col1 * blending.x + col2 * blending.y + col3 * blending.z;
+
+ col1 = texture2D( m_Tex2, coords.yz * m_Tex2Scale );
+ col2 = texture2D( m_Tex2, coords.xz * m_Tex2Scale );
+ col3 = texture2D( m_Tex2, coords.xy * m_Tex2Scale );
+ // blend the results of the 3 planar projections.
+ vec4 tex2 = col1 * blending.x + col2 * blending.y + col3 * blending.z;
+
+ col1 = texture2D( m_Tex3, coords.yz * m_Tex3Scale );
+ col2 = texture2D( m_Tex3, coords.xz * m_Tex3Scale );
+ col3 = texture2D( m_Tex3, coords.xy * m_Tex3Scale );
+ // blend the results of the 3 planar projections.
+ vec4 tex3 = col1 * blending.x + col2 * blending.y + col3 * blending.z;
+
+#else
+ vec4 tex1 = texture2D( m_Tex1, texCoord.xy * m_Tex1Scale ); // Tile
+ vec4 tex2 = texture2D( m_Tex2, texCoord.xy * m_Tex2Scale ); // Tile
+ vec4 tex3 = texture2D( m_Tex3, texCoord.xy * m_Tex3Scale ); // Tile
+
+#endif
+
+ vec4 outColor = tex1 * alpha.r; // Red channel
+ outColor = mix( outColor, tex2, alpha.g ); // Green channel
+ outColor = mix( outColor, tex3, alpha.b ); // Blue channel
+ gl_FragColor = outColor;
+}
+
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/Terrain.j3md b/engine/src/terrain/Common/MatDefs/Terrain/Terrain.j3md
new file mode 100644
index 0000000..152f511
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/Terrain.j3md
@@ -0,0 +1,33 @@
+MaterialDef Terrain {
+
+ MaterialParameters {
+
+ // use tri-planar mapping
+ Boolean useTriPlanarMapping
+
+ Texture2D Alpha
+ Texture2D Tex1
+ Texture2D Tex2
+ Texture2D Tex3
+ Float Tex1Scale
+ Float Tex2Scale
+ Float Tex3Scale
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Terrain/Terrain.vert
+ FragmentShader GLSL100: Common/MatDefs/Terrain/Terrain.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TRI_PLANAR_MAPPING : useTriPlanarMapping
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/Terrain.vert b/engine/src/terrain/Common/MatDefs/Terrain/Terrain.vert
new file mode 100644
index 0000000..ddb40a9
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/Terrain.vert
@@ -0,0 +1,23 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+
+#ifdef TRI_PLANAR_MAPPING
+ varying vec4 vVertex;
+ varying vec3 vNormal;
+#endif
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ texCoord = inTexCoord;
+
+#ifdef TRI_PLANAR_MAPPING
+ vVertex = vec4(inPosition,0.0);
+ vNormal = inNormal;
+#endif
+
+} \ No newline at end of file
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
new file mode 100644
index 0000000..4d1d41a
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
@@ -0,0 +1,625 @@
+
+uniform float m_Shininess;
+uniform vec4 g_LightDirection;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+varying vec3 vNormal;
+varying vec2 texCoord;
+varying vec3 vPosition;
+varying vec3 vnPosition;
+varying vec3 vViewDir;
+varying vec4 vLightDir;
+varying vec4 vnLightDir;
+varying vec3 lightVec;
+
+
+#ifdef DIFFUSEMAP
+ uniform sampler2D m_DiffuseMap;
+#endif
+#ifdef DIFFUSEMAP_1
+ uniform sampler2D m_DiffuseMap_1;
+#endif
+#ifdef DIFFUSEMAP_2
+ uniform sampler2D m_DiffuseMap_2;
+#endif
+#ifdef DIFFUSEMAP_3
+ uniform sampler2D m_DiffuseMap_3;
+#endif
+#ifdef DIFFUSEMAP_4
+ uniform sampler2D m_DiffuseMap_4;
+#endif
+#ifdef DIFFUSEMAP_5
+ uniform sampler2D m_DiffuseMap_5;
+#endif
+#ifdef DIFFUSEMAP_6
+ uniform sampler2D m_DiffuseMap_6;
+#endif
+#ifdef DIFFUSEMAP_7
+ uniform sampler2D m_DiffuseMap_7;
+#endif
+#ifdef DIFFUSEMAP_8
+ uniform sampler2D m_DiffuseMap_8;
+#endif
+#ifdef DIFFUSEMAP_9
+ uniform sampler2D m_DiffuseMap_9;
+#endif
+#ifdef DIFFUSEMAP_10
+ uniform sampler2D m_DiffuseMap_10;
+#endif
+#ifdef DIFFUSEMAP_11
+ uniform sampler2D m_DiffuseMap_11;
+#endif
+
+
+#ifdef DIFFUSEMAP_0_SCALE
+ uniform float m_DiffuseMap_0_scale;
+#endif
+#ifdef DIFFUSEMAP_1_SCALE
+ uniform float m_DiffuseMap_1_scale;
+#endif
+#ifdef DIFFUSEMAP_2_SCALE
+ uniform float m_DiffuseMap_2_scale;
+#endif
+#ifdef DIFFUSEMAP_3_SCALE
+ uniform float m_DiffuseMap_3_scale;
+#endif
+#ifdef DIFFUSEMAP_4_SCALE
+ uniform float m_DiffuseMap_4_scale;
+#endif
+#ifdef DIFFUSEMAP_5_SCALE
+ uniform float m_DiffuseMap_5_scale;
+#endif
+#ifdef DIFFUSEMAP_6_SCALE
+ uniform float m_DiffuseMap_6_scale;
+#endif
+#ifdef DIFFUSEMAP_7_SCALE
+ uniform float m_DiffuseMap_7_scale;
+#endif
+#ifdef DIFFUSEMAP_8_SCALE
+ uniform float m_DiffuseMap_8_scale;
+#endif
+#ifdef DIFFUSEMAP_9_SCALE
+ uniform float m_DiffuseMap_9_scale;
+#endif
+#ifdef DIFFUSEMAP_10_SCALE
+ uniform float m_DiffuseMap_10_scale;
+#endif
+#ifdef DIFFUSEMAP_11_SCALE
+ uniform float m_DiffuseMap_11_scale;
+#endif
+
+
+#ifdef ALPHAMAP
+ uniform sampler2D m_AlphaMap;
+#endif
+#ifdef ALPHAMAP_1
+ uniform sampler2D m_AlphaMap_1;
+#endif
+#ifdef ALPHAMAP_2
+ uniform sampler2D m_AlphaMap_2;
+#endif
+
+#ifdef NORMALMAP
+ uniform sampler2D m_NormalMap;
+#endif
+#ifdef NORMALMAP_1
+ uniform sampler2D m_NormalMap_1;
+#endif
+#ifdef NORMALMAP_2
+ uniform sampler2D m_NormalMap_2;
+#endif
+#ifdef NORMALMAP_3
+ uniform sampler2D m_NormalMap_3;
+#endif
+#ifdef NORMALMAP_4
+ uniform sampler2D m_NormalMap_4;
+#endif
+#ifdef NORMALMAP_5
+ uniform sampler2D m_NormalMap_5;
+#endif
+#ifdef NORMALMAP_6
+ uniform sampler2D m_NormalMap_6;
+#endif
+#ifdef NORMALMAP_7
+ uniform sampler2D m_NormalMap_7;
+#endif
+#ifdef NORMALMAP_8
+ uniform sampler2D m_NormalMap_8;
+#endif
+#ifdef NORMALMAP_9
+ uniform sampler2D m_NormalMap_9;
+#endif
+#ifdef NORMALMAP_10
+ uniform sampler2D m_NormalMap_10;
+#endif
+#ifdef NORMALMAP_11
+ uniform sampler2D m_NormalMap_11;
+#endif
+
+
+#ifdef TRI_PLANAR_MAPPING
+ varying vec4 wVertex;
+ varying vec3 wNormal;
+#endif
+
+
+
+float tangDot(in vec3 v1, in vec3 v2){
+ float d = dot(v1,v2);
+ #ifdef V_TANGENT
+ d = 1.0 - d*d;
+ return step(0.0, d) * sqrt(d);
+ #else
+ return d;
+ #endif
+}
+
+
+float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
+ return max(0.0, dot(norm, lightdir));
+}
+
+float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+ #ifdef WARDISO
+ // Isotropic Ward
+ vec3 halfVec = normalize(viewdir + lightdir);
+ float NdotH = max(0.001, tangDot(norm, halfVec));
+ float NdotV = max(0.001, tangDot(norm, viewdir));
+ float NdotL = max(0.001, tangDot(norm, lightdir));
+ float a = tan(acos(NdotH));
+ float p = max(shiny/128.0, 0.001);
+ return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
+ #else
+ // Standard Phong
+ vec3 R = reflect(-lightdir, norm);
+ return pow(max(tangDot(R, viewdir), 0.0), shiny);
+ #endif
+}
+
+vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
+ float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);
+ specularFactor *= step(1.0, m_Shininess);
+
+ float att = vLightDir.w;
+
+ return vec2(diffuseFactor, specularFactor) * vec2(att);
+}
+
+
+#ifdef ALPHAMAP
+
+ vec4 calculateDiffuseBlend(in vec2 texCoord) {
+ vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );
+
+ #ifdef ALPHAMAP_1
+ vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
+ #endif
+ #ifdef ALPHAMAP_2
+ vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
+ #endif
+
+ vec4 diffuseColor = texture2D(m_DiffuseMap, texCoord * m_DiffuseMap_0_scale);
+ diffuseColor *= alphaBlend.r;
+ #ifdef DIFFUSEMAP_1
+ vec4 diffuseColor1 = texture2D(m_DiffuseMap_1, texCoord * m_DiffuseMap_1_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor1, alphaBlend.g );
+ #ifdef DIFFUSEMAP_2
+ vec4 diffuseColor2 = texture2D(m_DiffuseMap_2, texCoord * m_DiffuseMap_2_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor2, alphaBlend.b );
+ #ifdef DIFFUSEMAP_3
+ vec4 diffuseColor3 = texture2D(m_DiffuseMap_3, texCoord * m_DiffuseMap_3_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor3, alphaBlend.a );
+ #ifdef ALPHAMAP_1
+ #ifdef DIFFUSEMAP_4
+ vec4 diffuseColor4 = texture2D(m_DiffuseMap_4, texCoord * m_DiffuseMap_4_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor4, alphaBlend1.r );
+ #ifdef DIFFUSEMAP_5
+ vec4 diffuseColor5 = texture2D(m_DiffuseMap_5, texCoord * m_DiffuseMap_5_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor5, alphaBlend1.g );
+ #ifdef DIFFUSEMAP_6
+ vec4 diffuseColor6 = texture2D(m_DiffuseMap_6, texCoord * m_DiffuseMap_6_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor6, alphaBlend1.b );
+ #ifdef DIFFUSEMAP_7
+ vec4 diffuseColor7 = texture2D(m_DiffuseMap_7, texCoord * m_DiffuseMap_7_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor7, alphaBlend1.a );
+ #ifdef ALPHAMAP_2
+ #ifdef DIFFUSEMAP_8
+ vec4 diffuseColor8 = texture2D(m_DiffuseMap_8, texCoord * m_DiffuseMap_8_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor8, alphaBlend2.r );
+ #ifdef DIFFUSEMAP_9
+ vec4 diffuseColor9 = texture2D(m_DiffuseMap_9, texCoord * m_DiffuseMap_9_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor9, alphaBlend2.g );
+ #ifdef DIFFUSEMAP_10
+ vec4 diffuseColor10 = texture2D(m_DiffuseMap_10, texCoord * m_DiffuseMap_10_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor10, alphaBlend2.b );
+ #ifdef DIFFUSEMAP_11
+ vec4 diffuseColor11 = texture2D(m_DiffuseMap_11, texCoord * m_DiffuseMap_11_scale);
+ diffuseColor = mix( diffuseColor, diffuseColor11, alphaBlend2.a );
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ return diffuseColor;
+ }
+
+ vec3 calculateNormal(in vec2 texCoord) {
+ vec3 normal = vec3(0,0,1);
+ vec3 n = vec3(0,0,0);
+
+ vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );
+
+ #ifdef ALPHAMAP_1
+ vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
+ #endif
+ #ifdef ALPHAMAP_2
+ vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
+ #endif
+
+ #ifdef NORMALMAP
+ n = texture2D(m_NormalMap, texCoord * m_DiffuseMap_0_scale).xyz;
+ normal += n * alphaBlend.r;
+ #endif
+
+ #ifdef NORMALMAP_1
+ n = texture2D(m_NormalMap_1, texCoord * m_DiffuseMap_1_scale).xyz;
+ normal += n * alphaBlend.g;
+ #endif
+
+ #ifdef NORMALMAP_2
+ n = texture2D(m_NormalMap_2, texCoord * m_DiffuseMap_2_scale).xyz;
+ normal += n * alphaBlend.b;
+ #endif
+
+ #ifdef NORMALMAP_3
+ n = texture2D(m_NormalMap_3, texCoord * m_DiffuseMap_3_scale).xyz;
+ normal += n * alphaBlend.a;
+ #endif
+
+ #ifdef ALPHAMAP_1
+ #ifdef NORMALMAP_4
+ n = texture2D(m_NormalMap_4, texCoord * m_DiffuseMap_4_scale).xyz;
+ normal += n * alphaBlend1.r;
+ #endif
+
+ #ifdef NORMALMAP_5
+ n = texture2D(m_NormalMap_5, texCoord * m_DiffuseMap_5_scale).xyz;
+ normal += n * alphaBlend1.g;
+ #endif
+
+ #ifdef NORMALMAP_6
+ n = texture2D(m_NormalMap_6, texCoord * m_DiffuseMap_6_scale).xyz;
+ normal += n * alphaBlend1.b;
+ #endif
+
+ #ifdef NORMALMAP_7
+ n = texture2D(m_NormalMap_7, texCoord * m_DiffuseMap_7_scale).xyz;
+ normal += n * alphaBlend1.a;
+ #endif
+ #endif
+
+ #ifdef ALPHAMAP_2
+ #ifdef NORMALMAP_8
+ n = texture2D(m_NormalMap_8, texCoord * m_DiffuseMap_8_scale).xyz;
+ normal += n * alphaBlend2.r;
+ #endif
+
+ #ifdef NORMALMAP_9
+ n = texture2D(m_NormalMap_9, texCoord * m_DiffuseMap_9_scale);
+ normal += n * alphaBlend2.g;
+ #endif
+
+ #ifdef NORMALMAP_10
+ n = texture2D(m_NormalMap_10, texCoord * m_DiffuseMap_10_scale);
+ normal += n * alphaBlend2.b;
+ #endif
+
+ #ifdef NORMALMAP_11
+ n = texture2D(m_NormalMap_11, texCoord * m_DiffuseMap_11_scale);
+ normal += n * alphaBlend2.a;
+ #endif
+ #endif
+
+ normal = (normal.xyz * vec3(2.0) - vec3(1.0));
+ return normalize(normal);
+ }
+
+ #ifdef TRI_PLANAR_MAPPING
+
+ vec4 getTriPlanarBlend(in vec4 coords, in vec3 blending, in sampler2D map, in float scale) {
+ vec4 col1 = texture2D( map, coords.yz * scale);
+ vec4 col2 = texture2D( map, coords.xz * scale);
+ vec4 col3 = texture2D( map, coords.xy * scale);
+ // blend the results of the 3 planar projections.
+ vec4 tex = col1 * blending.x + col2 * blending.y + col3 * blending.z;
+ return tex;
+ }
+
+ vec4 calculateTriPlanarDiffuseBlend(in vec3 wNorm, in vec4 wVert, in vec2 texCoord) {
+ // tri-planar texture bending factor for this fragment's normal
+ vec3 blending = abs( wNorm );
+ blending = (blending -0.2) * 0.7;
+ blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0 (very important!)
+ float b = (blending.x + blending.y + blending.z);
+ blending /= vec3(b, b, b);
+
+ // texture coords
+ vec4 coords = wVert;
+
+ // blend the results of the 3 planar projections.
+ vec4 tex0 = getTriPlanarBlend(coords, blending, m_DiffuseMap, m_DiffuseMap_0_scale);
+
+ #ifdef DIFFUSEMAP_1
+ // blend the results of the 3 planar projections.
+ vec4 tex1 = getTriPlanarBlend(coords, blending, m_DiffuseMap_1, m_DiffuseMap_1_scale);
+ #endif
+ #ifdef DIFFUSEMAP_2
+ // blend the results of the 3 planar projections.
+ vec4 tex2 = getTriPlanarBlend(coords, blending, m_DiffuseMap_2, m_DiffuseMap_2_scale);
+ #endif
+ #ifdef DIFFUSEMAP_3
+ // blend the results of the 3 planar projections.
+ vec4 tex3 = getTriPlanarBlend(coords, blending, m_DiffuseMap_3, m_DiffuseMap_3_scale);
+ #endif
+ #ifdef DIFFUSEMAP_4
+ // blend the results of the 3 planar projections.
+ vec4 tex4 = getTriPlanarBlend(coords, blending, m_DiffuseMap_4, m_DiffuseMap_4_scale);
+ #endif
+ #ifdef DIFFUSEMAP_5
+ // blend the results of the 3 planar projections.
+ vec4 tex5 = getTriPlanarBlend(coords, blending, m_DiffuseMap_5, m_DiffuseMap_5_scale);
+ #endif
+ #ifdef DIFFUSEMAP_6
+ // blend the results of the 3 planar projections.
+ vec4 tex6 = getTriPlanarBlend(coords, blending, m_DiffuseMap_6, m_DiffuseMap_6_scale);
+ #endif
+ #ifdef DIFFUSEMAP_7
+ // blend the results of the 3 planar projections.
+ vec4 tex7 = getTriPlanarBlend(coords, blending, m_DiffuseMap_7, m_DiffuseMap_7_scale);
+ #endif
+ #ifdef DIFFUSEMAP_8
+ // blend the results of the 3 planar projections.
+ vec4 tex8 = getTriPlanarBlend(coords, blending, m_DiffuseMap_8, m_DiffuseMap_8_scale);
+ #endif
+ #ifdef DIFFUSEMAP_9
+ // blend the results of the 3 planar projections.
+ vec4 tex9 = getTriPlanarBlend(coords, blending, m_DiffuseMap_9, m_DiffuseMap_9_scale);
+ #endif
+ #ifdef DIFFUSEMAP_10
+ // blend the results of the 3 planar projections.
+ vec4 tex10 = getTriPlanarBlend(coords, blending, m_DiffuseMap_10, m_DiffuseMap_10_scale);
+ #endif
+ #ifdef DIFFUSEMAP_11
+ // blend the results of the 3 planar projections.
+ vec4 tex11 = getTriPlanarBlend(coords, blending, m_DiffuseMap_11, m_DiffuseMap_11_scale);
+ #endif
+
+ vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );
+
+ #ifdef ALPHAMAP_1
+ vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
+ #endif
+ #ifdef ALPHAMAP_2
+ vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
+ #endif
+
+ vec4 diffuseColor = tex0 * alphaBlend.r;
+ #ifdef DIFFUSEMAP_1
+ diffuseColor = mix( diffuseColor, tex1, alphaBlend.g );
+ #ifdef DIFFUSEMAP_2
+ diffuseColor = mix( diffuseColor, tex2, alphaBlend.b );
+ #ifdef DIFFUSEMAP_3
+ diffuseColor = mix( diffuseColor, tex3, alphaBlend.a );
+ #ifdef ALPHAMAP_1
+ #ifdef DIFFUSEMAP_4
+ diffuseColor = mix( diffuseColor, tex4, alphaBlend1.r );
+ #ifdef DIFFUSEMAP_5
+ diffuseColor = mix( diffuseColor, tex5, alphaBlend1.g );
+ #ifdef DIFFUSEMAP_6
+ diffuseColor = mix( diffuseColor, tex6, alphaBlend1.b );
+ #ifdef DIFFUSEMAP_7
+ diffuseColor = mix( diffuseColor, tex7, alphaBlend1.a );
+ #ifdef ALPHAMAP_2
+ #ifdef DIFFUSEMAP_8
+ diffuseColor = mix( diffuseColor, tex8, alphaBlend2.r );
+ #ifdef DIFFUSEMAP_9
+ diffuseColor = mix( diffuseColor, tex9, alphaBlend2.g );
+ #ifdef DIFFUSEMAP_10
+ diffuseColor = mix( diffuseColor, tex10, alphaBlend2.b );
+ #ifdef DIFFUSEMAP_11
+ diffuseColor = mix( diffuseColor, tex11, alphaBlend2.a );
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+
+ return diffuseColor;
+ }
+
+ vec3 calculateNormalTriPlanar(in vec3 wNorm, in vec4 wVert,in vec2 texCoord) {
+ // tri-planar texture bending factor for this fragment's world-space normal
+ vec3 blending = abs( wNorm );
+ blending = (blending -0.2) * 0.7;
+ blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0 (very important!)
+ float b = (blending.x + blending.y + blending.z);
+ blending /= vec3(b, b, b);
+
+ // texture coords
+ vec4 coords = wVert;
+ vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );
+
+ #ifdef ALPHAMAP_1
+ vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
+ #endif
+ #ifdef ALPHAMAP_2
+ vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
+ #endif
+
+ vec3 normal = vec3(0,0,1);
+ vec3 n = vec3(0,0,0);
+
+ #ifdef NORMALMAP
+ n = getTriPlanarBlend(coords, blending, m_NormalMap, m_DiffuseMap_0_scale).xyz;
+ normal += n * alphaBlend.r;
+ #endif
+
+ #ifdef NORMALMAP_1
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_1, m_DiffuseMap_1_scale).xyz;
+ normal += n * alphaBlend.g;
+ #endif
+
+ #ifdef NORMALMAP_2
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_2, m_DiffuseMap_2_scale).xyz;
+ normal += n * alphaBlend.b;
+ #endif
+
+ #ifdef NORMALMAP_3
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_3, m_DiffuseMap_3_scale).xyz;
+ normal += n * alphaBlend.a;
+ #endif
+
+ #ifdef ALPHAMAP_1
+ #ifdef NORMALMAP_4
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_4, m_DiffuseMap_4_scale).xyz;
+ normal += n * alphaBlend1.r;
+ #endif
+
+ #ifdef NORMALMAP_5
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_5, m_DiffuseMap_5_scale).xyz;
+ normal += n * alphaBlend1.g;
+ #endif
+
+ #ifdef NORMALMAP_6
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_6, m_DiffuseMap_6_scale).xyz;
+ normal += n * alphaBlend1.b;
+ #endif
+
+ #ifdef NORMALMAP_7
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_7, m_DiffuseMap_7_scale).xyz;
+ normal += n * alphaBlend1.a;
+ #endif
+ #endif
+
+ #ifdef ALPHAMAP_2
+ #ifdef NORMALMAP_8
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_8, m_DiffuseMap_8_scale).xyz;
+ normal += n * alphaBlend2.r;
+ #endif
+
+ #ifdef NORMALMAP_9
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_9, m_DiffuseMap_9_scale).xyz;
+ normal += n * alphaBlend2.g;
+ #endif
+
+ #ifdef NORMALMAP_10
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_10, m_DiffuseMap_10_scale).xyz;
+ normal += n * alphaBlend2.b;
+ #endif
+
+ #ifdef NORMALMAP_11
+ n = getTriPlanarBlend(coords, blending, m_NormalMap_11, m_DiffuseMap_11_scale).xyz;
+ normal += n * alphaBlend2.a;
+ #endif
+ #endif
+
+ normal = (normal.xyz * vec3(2.0) - vec3(1.0));
+ return normalize(normal);
+ }
+ #endif
+
+#endif
+
+
+
+void main(){
+
+ //----------------------
+ // diffuse calculations
+ //----------------------
+ #ifdef DIFFUSEMAP
+ #ifdef ALPHAMAP
+ #ifdef TRI_PLANAR_MAPPING
+ vec4 diffuseColor = calculateTriPlanarDiffuseBlend(wNormal, wVertex, texCoord);
+ #else
+ vec4 diffuseColor = calculateDiffuseBlend(texCoord);
+ #endif
+ #else
+ vec4 diffuseColor = texture2D(m_DiffuseMap, texCoord);
+ #endif
+ #else
+ vec4 diffuseColor = vec4(1.0);
+ #endif
+
+ float spotFallOff = 1.0;
+ if(g_LightDirection.w!=0.0){
+ vec3 L=normalize(lightVec.xyz);
+ vec3 spotdir = normalize(g_LightDirection.xyz);
+ float curAngleCos = dot(-L, spotdir);
+ float innerAngleCos = floor(g_LightDirection.w) * 0.001;
+ float outerAngleCos = fract(g_LightDirection.w);
+ float innerMinusOuter = innerAngleCos - outerAngleCos;
+
+ spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;
+
+ if(spotFallOff <= 0.0){
+ gl_FragColor = AmbientSum * diffuseColor;
+ return;
+ }else{
+ spotFallOff = clamp(spotFallOff, 0.0, 1.0);
+ }
+ }
+
+ //---------------------
+ // normal calculations
+ //---------------------
+ #if defined(NORMALMAP) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(NORMALMAP_8) || defined(NORMALMAP_9) || defined(NORMALMAP_10) || defined(NORMALMAP_11)
+ #ifdef TRI_PLANAR_MAPPING
+ vec3 normal = calculateNormalTriPlanar(wNormal, wVertex, texCoord);
+ #else
+ vec3 normal = calculateNormal(texCoord);
+ #endif
+ #else
+ vec3 normal = vNormal;
+ #endif
+
+
+ //-----------------------
+ // lighting calculations
+ //-----------------------
+ vec4 lightDir = vLightDir;
+ lightDir.xyz = normalize(lightDir.xyz);
+
+ vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz)*spotFallOff;
+
+ vec4 specularColor = vec4(1.0);
+
+ //--------------------------
+ // final color calculations
+ //--------------------------
+ gl_FragColor = AmbientSum * diffuseColor +
+ DiffuseSum * diffuseColor * light.x +
+ SpecularSum * specularColor * light.y;
+
+ //gl_FragColor.a = alpha;
+} \ No newline at end of file
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.j3md b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.j3md
new file mode 100644
index 0000000..0c48999
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.j3md
@@ -0,0 +1,254 @@
+// NOTE: Doesn't support OpenGL1
+MaterialDef Terrain Lighting {
+
+ MaterialParameters {
+
+ // use tri-planar mapping
+ Boolean useTriPlanarMapping
+
+ // Use ward specular instead of phong
+ Boolean WardIso
+
+ // Are we rendering TerrainGrid
+ Boolean isTerrainGrid
+
+ // Ambient color
+ Color Ambient
+
+ // Diffuse color
+ Color Diffuse
+
+ // Specular color
+ Color Specular
+
+ // Specular power/shininess
+ Float Shininess : 1
+
+ // Texture map #0
+ Texture2D DiffuseMap
+ Float DiffuseMap_0_scale
+ Texture2D NormalMap
+
+ // Texture map #1
+ Texture2D DiffuseMap_1
+ Float DiffuseMap_1_scale
+ Texture2D NormalMap_1
+
+ // Texture map #2
+ Texture2D DiffuseMap_2
+ Float DiffuseMap_2_scale
+ Texture2D NormalMap_2
+
+ // Texture map #3
+ Texture2D DiffuseMap_3
+ Float DiffuseMap_3_scale
+ Texture2D NormalMap_3
+
+ // Texture map #4
+ Texture2D DiffuseMap_4
+ Float DiffuseMap_4_scale
+ Texture2D NormalMap_4
+
+ // Texture map #5
+ Texture2D DiffuseMap_5
+ Float DiffuseMap_5_scale
+ Texture2D NormalMap_5
+
+ // Texture map #6
+ Texture2D DiffuseMap_6
+ Float DiffuseMap_6_scale
+ Texture2D NormalMap_6
+
+ // Texture map #7
+ Texture2D DiffuseMap_7
+ Float DiffuseMap_7_scale
+ Texture2D NormalMap_7
+
+ // Texture map #8
+ Texture2D DiffuseMap_8
+ Float DiffuseMap_8_scale
+ Texture2D NormalMap_8
+
+ // Texture map #9
+ Texture2D DiffuseMap_9
+ Float DiffuseMap_9_scale
+ Texture2D NormalMap_9
+
+ // Texture map #10
+ Texture2D DiffuseMap_10
+ Float DiffuseMap_10_scale
+ Texture2D NormalMap_10
+
+ // Texture map #11
+ Texture2D DiffuseMap_11
+ Float DiffuseMap_11_scale
+ Texture2D NormalMap_11
+
+
+ // Specular/gloss map
+ Texture2D SpecularMap
+
+
+ // Texture that specifies alpha values
+ Texture2D AlphaMap
+ Texture2D AlphaMap_1
+ Texture2D AlphaMap_2
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+
+ LightMode MultiPass
+
+ VertexShader GLSL100: Common/MatDefs/Terrain/TerrainLighting.vert
+ FragmentShader GLSL100: Common/MatDefs/Terrain/TerrainLighting.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ NormalMatrix
+ WorldViewMatrix
+ ViewMatrix
+ }
+
+ Defines {
+ TRI_PLANAR_MAPPING : useTriPlanarMapping
+ TERRAIN_GRID : isTerrainGrid
+ WARDISO : WardIso
+
+ DIFFUSEMAP : DiffuseMap
+ DIFFUSEMAP_1 : DiffuseMap_1
+ DIFFUSEMAP_2 : DiffuseMap_2
+ DIFFUSEMAP_3 : DiffuseMap_3
+ DIFFUSEMAP_4 : DiffuseMap_4
+ DIFFUSEMAP_5 : DiffuseMap_5
+ DIFFUSEMAP_6 : DiffuseMap_6
+ DIFFUSEMAP_7 : DiffuseMap_7
+ DIFFUSEMAP_8 : DiffuseMap_8
+ DIFFUSEMAP_9 : DiffuseMap_9
+ DIFFUSEMAP_10 : DiffuseMap_10
+ DIFFUSEMAP_11 : DiffuseMap_11
+ NORMALMAP : NormalMap
+ NORMALMAP_1 : NormalMap_1
+ NORMALMAP_2 : NormalMap_2
+ NORMALMAP_3 : NormalMap_3
+ NORMALMAP_4 : NormalMap_4
+ NORMALMAP_5 : NormalMap_5
+ NORMALMAP_6 : NormalMap_6
+ NORMALMAP_7 : NormalMap_7
+ NORMALMAP_8 : NormalMap_8
+ NORMALMAP_9 : NormalMap_9
+ NORMALMAP_10 : NormalMap_10
+ NORMALMAP_11 : NormalMap_11
+ SPECULARMAP : SpecularMap
+ ALPHAMAP : AlphaMap
+ ALPHAMAP_1 : AlphaMap_1
+ ALPHAMAP_2 : AlphaMap_2
+ DIFFUSEMAP_0_SCALE : DiffuseMap_0_scale
+ DIFFUSEMAP_1_SCALE : DiffuseMap_1_scale
+ DIFFUSEMAP_2_SCALE : DiffuseMap_2_scale
+ DIFFUSEMAP_3_SCALE : DiffuseMap_3_scale
+ DIFFUSEMAP_4_SCALE : DiffuseMap_4_scale
+ DIFFUSEMAP_5_SCALE : DiffuseMap_5_scale
+ DIFFUSEMAP_6_SCALE : DiffuseMap_6_scale
+ DIFFUSEMAP_7_SCALE : DiffuseMap_7_scale
+ DIFFUSEMAP_8_SCALE : DiffuseMap_8_scale
+ DIFFUSEMAP_9_SCALE : DiffuseMap_9_scale
+ DIFFUSEMAP_10_SCALE : DiffuseMap_10_scale
+ DIFFUSEMAP_11_SCALE : DiffuseMap_11_scale
+ }
+ }
+
+ Technique PreShadow {
+
+ VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
+ FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ }
+
+ RenderState {
+ FaceCull Off
+ DepthTest On
+ DepthWrite On
+ PolyOffset 5 0
+ ColorWrite Off
+ }
+
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ }
+
+ RenderState {
+
+ }
+
+ }
+
+ Technique GBuf {
+
+ VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ VERTEX_COLOR : UseVertexColor
+ MATERIAL_COLORS : UseMaterialColors
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+
+ DIFFUSEMAP : DiffuseMap
+ NORMALMAP : NormalMap
+ SPECULARMAP : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ }
+ }
+
+ Technique FixedFunc {
+ LightMode FixedPipeline
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert
new file mode 100644
index 0000000..a3a1cc2
--- /dev/null
+++ b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert
@@ -0,0 +1,107 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+uniform mat3 g_NormalMatrix;
+uniform mat4 g_ViewMatrix;
+
+uniform vec4 g_LightColor;
+uniform vec4 g_LightPosition;
+uniform vec4 g_AmbientLightColor;
+
+uniform float m_Shininess;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+attribute vec2 inTexCoord;
+attribute vec4 inTangent;
+
+varying vec3 vNormal;
+varying vec2 texCoord;
+varying vec3 vPosition;
+varying vec3 vnPosition;
+varying vec3 vViewDir;
+varying vec3 vnViewDir;
+varying vec4 vLightDir;
+varying vec4 vnLightDir;
+
+varying vec3 lightVec;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+#ifdef TRI_PLANAR_MAPPING
+ varying vec4 wVertex;
+ varying vec3 wNormal;
+#endif
+
+// JME3 lights in world space
+void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ lightVec.xyz = tempVec;
+ float dist = length(tempVec);
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / vec3(dist);
+}
+
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ #ifdef TERRAIN_GRID
+ texCoord = inTexCoord * 2.0;
+ #else
+ texCoord = inTexCoord;
+ #endif
+
+ vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
+ vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
+ vec3 viewDir = normalize(-wvPosition);
+
+ vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
+ wvLightPos.w = g_LightPosition.w;
+ vec4 lightColor = g_LightColor;
+
+ //--------------------------
+ // specific to normal maps:
+ //--------------------------
+ #if defined(NORMALMAP) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(NORMALMAP_8) || defined(NORMALMAP_9) || defined(NORMALMAP_10) || defined(NORMALMAP_11)
+ vec3 wvTangent = normalize(g_NormalMatrix * inTangent.xyz);
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+
+ mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal);
+
+ vPosition = wvPosition * tbnMat;
+ vViewDir = viewDir * tbnMat;
+ lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+ vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
+ #else
+
+ //-------------------------
+ // general to all lighting
+ //-------------------------
+ vNormal = wvNormal;
+
+ vPosition = wvPosition;
+ vViewDir = viewDir;
+
+ lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+
+ #endif
+
+ //computing spot direction in view space and unpacking spotlight cos
+ // spotVec=(g_ViewMatrix *vec4(g_LightDirection.xyz,0.0) );
+ // spotVec.w=floor(g_LightDirection.w)*0.001;
+ // lightVec.w = fract(g_LightDirection.w);
+
+ AmbientSum = vec4(0.2, 0.2, 0.2, 1.0) * g_AmbientLightColor; // Default: ambient color is dark gray
+ DiffuseSum = lightColor;
+ SpecularSum = lightColor;
+
+
+#ifdef TRI_PLANAR_MAPPING
+ wVertex = vec4(inPosition,0.0);
+ wNormal = inNormal;
+#endif
+
+} \ No newline at end of file
diff --git a/engine/src/terrain/com/jme3/terrain/GeoMap.java b/engine/src/terrain/com/jme3/terrain/GeoMap.java
new file mode 100644
index 0000000..dc9264f
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/GeoMap.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain;
+
+import com.jme3.export.*;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Constructs heightfields to be used in Terrain.
+ */
+public class GeoMap implements Savable {
+
+ protected float[] hdata;
+ protected int width, height, maxval;
+
+ public GeoMap() {}
+
+ @Deprecated
+ public GeoMap(FloatBuffer heightData, int width, int height, int maxval){
+ hdata = new float[heightData.limit()];
+ heightData.get(hdata);
+ this.width = width;
+ this.height = height;
+ this.maxval = maxval;
+ }
+
+ public GeoMap(float[] heightData, int width, int height, int maxval){
+ this.hdata = heightData;
+ this.width = width;
+ this.height = height;
+ this.maxval = maxval;
+ }
+
+ @Deprecated
+ public FloatBuffer getHeightData(){
+ if (!isLoaded())
+ return null;
+ return BufferUtils.createFloatBuffer(hdata);
+ }
+
+ public float[] getHeightArray(){
+ if (!isLoaded())
+ return null;
+ return hdata;
+ }
+
+ /**
+ * @return The maximum possible value that <code>getValue()</code> can
+ * return. Mostly depends on the source data format (byte, short, int, etc).
+ */
+ public int getMaximumValue(){
+ return maxval;
+ }
+
+ /**
+ * Returns the height value for a given point.
+ *
+ * MUST return the same value as getHeight(y*getWidth()+x)
+ *
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @returns an arbitrary height looked up from the heightmap
+ *
+ * @throws NullPointerException If isLoaded() is false
+ */
+ public float getValue(int x, int y) {
+ return hdata[y*width+x];
+ }
+
+ /**
+ * Returns the height value at the given index.
+ *
+ * zero index is top left of map,
+ * getWidth()*getHeight() index is lower right
+ *
+ * @param i The index
+ * @returns an arbitrary height looked up from the heightmap
+ *
+ * @throws NullPointerException If isLoaded() is false
+ */
+ public float getValue(int i) {
+ return hdata[i];
+ }
+
+
+ /**
+ * Returns the width of this Geomap
+ *
+ * @returns the width of this Geomap
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Returns the height of this Geomap
+ *
+ * @returns the height of this Geomap
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Returns true if the Geomap data is loaded in memory
+ * If false, then the data is unavailable- must be loaded with load()
+ * before the methods getHeight/getNormal can be used
+ *
+ * @returns wether the geomap data is loaded in system memory
+ */
+ public boolean isLoaded() {
+ return true;
+ }
+
+ /**
+ * Creates a normal array from the normal data in this Geomap
+ *
+ * @param store A preallocated FloatBuffer where to store the data (optional), size must be >= getWidth()*getHeight()*3
+ * @returns store, or a new FloatBuffer if store is null
+ *
+ * @throws NullPointerException If isLoaded() or hasNormalmap() is false
+ */
+ public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) {
+
+ if (store!=null){
+ if (store.remaining() < getWidth()*getHeight()*3)
+ throw new BufferUnderflowException();
+ }else{
+ store = BufferUtils.createFloatBuffer(getWidth()*getHeight()*3);
+ }
+ store.rewind();
+
+ Vector3f oppositePoint = new Vector3f();
+ Vector3f adjacentPoint = new Vector3f();
+ Vector3f rootPoint = new Vector3f();
+ Vector3f tempNorm = new Vector3f();
+ int normalIndex = 0;
+
+ for (int y = 0; y < getHeight(); y++) {
+ for (int x = 0; x < getWidth(); x++) {
+ rootPoint.set(x, getValue(x,y), y);
+ if (y == getHeight() - 1) {
+ if (x == getWidth() - 1) { // case #4 : last row, last col
+ // left cross up
+// adj = normalIndex - getWidth();
+// opp = normalIndex - 1;
+ adjacentPoint.set(x, getValue(x,y-1), y-1);
+ oppositePoint.set(x-1, getValue(x-1, y), y);
+ } else { // case #3 : last row, except for last col
+ // right cross up
+// adj = normalIndex + 1;
+// opp = normalIndex - getWidth();
+ adjacentPoint.set(x+1, getValue(x+1,y), y);
+ oppositePoint.set(x, getValue(x,y-1), y-1);
+ }
+ } else {
+ if (x == getWidth() - 1) { // case #2 : last column except for last row
+ // left cross down
+ adjacentPoint.set(x-1, getValue(x-1,y), y);
+ oppositePoint.set(x, getValue(x,y+1), y+1);
+// adj = normalIndex - 1;
+// opp = normalIndex + getWidth();
+ } else { // case #1 : most cases
+ // right cross down
+ adjacentPoint.set(x, getValue(x,y+1), y+1);
+ oppositePoint.set(x+1, getValue(x+1,y), y);
+// adj = normalIndex + getWidth();
+// opp = normalIndex + 1;
+ }
+ }
+
+
+
+ tempNorm.set(adjacentPoint).subtractLocal(rootPoint)
+ .crossLocal(oppositePoint.subtractLocal(rootPoint));
+ tempNorm.multLocal(scale).normalizeLocal();
+// store.put(tempNorm.x).put(tempNorm.y).put(tempNorm.z);
+ BufferUtils.setInBuffer(tempNorm, store,
+ normalIndex);
+ normalIndex++;
+ }
+ }
+
+ return store;
+ }
+
+ /**
+ * Creates a vertex array from the height data in this Geomap
+ *
+ * The scale argument specifies the scale to use for the vertex buffer.
+ * For example, if scale is 10,1,10, then the greatest X value is getWidth()*10
+ *
+ * @param store A preallocated FloatBuffer where to store the data (optional), size must be >= getWidth()*getHeight()*3
+ * @param scale Created vertexes are scaled by this vector
+ *
+ * @returns store, or a new FloatBuffer if store is null
+ *
+ * @throws NullPointerException If isLoaded() is false
+ */
+ public FloatBuffer writeVertexArray(FloatBuffer store, Vector3f scale, boolean center) {
+
+ if (store!=null){
+ if (store.remaining() < width*height*3)
+ throw new BufferUnderflowException();
+ }else{
+ store = BufferUtils.createFloatBuffer(width*height*3);
+ }
+
+ assert hdata.length == height*width;
+
+ Vector3f offset = new Vector3f(-getWidth() * scale.x * 0.5f,
+ 0,
+ -getWidth() * scale.z * 0.5f);
+ if (!center)
+ offset.zero();
+
+ int i = 0;
+ for (int z = 0; z < height; z++){
+ for (int x = 0; x < width; x++){
+ store.put( (float)x*scale.x + offset.x );
+ store.put( (float)hdata[i++]*scale.y );
+ store.put( (float)z*scale.z + offset.z );
+ }
+ }
+
+ return store;
+ }
+
+ public Vector2f getUV(int x, int y, Vector2f store){
+ store.set( (float)x / (float)getWidth(),
+ (float)y / (float)getHeight() );
+ return store;
+ }
+
+ public Vector2f getUV(int i, Vector2f store){
+ return store;
+ }
+
+ public FloatBuffer writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale){
+ if (store!=null){
+ if (store.remaining() < getWidth()*getHeight()*2)
+ throw new BufferUnderflowException();
+ }else{
+ store = BufferUtils.createFloatBuffer(getWidth()*getHeight()*2);
+ }
+
+ if (offset == null)
+ offset = new Vector2f();
+
+ Vector2f tcStore = new Vector2f();
+ for (int y = 0; y < getHeight(); y++){
+ for (int x = 0; x < getWidth(); x++){
+ getUV(x,y,tcStore);
+ store.put( offset.x + tcStore.x * scale.x );
+ store.put( offset.y + tcStore.y * scale.y );
+ }
+
+ }
+
+ return store;
+ }
+
+ public IntBuffer writeIndexArray(IntBuffer store){
+ int faceN = (getWidth()-1)*(getHeight()-1)*2;
+
+ if (store!=null){
+ if (store.remaining() < faceN*3)
+ throw new BufferUnderflowException();
+ }else{
+ store = BufferUtils.createIntBuffer(faceN*3);
+ }
+
+ int i = 0;
+ for (int z = 0; z < getHeight()-1; z++){
+ for (int x = 0; x < getWidth()-1; x++){
+ store.put(i).put(i+getWidth()).put(i+getWidth()+1);
+ store.put(i+getWidth()+1).put(i+1).put(i);
+ i++;
+
+ // TODO: There's probably a better way to do this..
+ if (x==getWidth()-2) i++;
+ }
+ }
+ store.flip();
+
+ return store;
+ }
+
+ public Mesh createMesh(Vector3f scale, Vector2f tcScale, boolean center){
+ FloatBuffer pb = writeVertexArray(null, scale, center);
+ FloatBuffer tb = writeTexCoordArray(null, Vector2f.ZERO, tcScale);
+ FloatBuffer nb = writeNormalArray(null, scale);
+ IntBuffer ib = writeIndexArray(null);
+ Mesh m = new Mesh();
+ m.setBuffer(Type.Position, 3, pb);
+ m.setBuffer(Type.Normal, 3, nb);
+ m.setBuffer(Type.TexCoord, 2, tb);
+ m.setBuffer(Type.Index, 3, ib);
+ m.setStatic();
+ m.updateBound();
+ return m;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(hdata, "hdataarray", null);
+ oc.write(width, "width", 0);
+ oc.write(height, "height", 0);
+ oc.write(maxval, "maxval", 0);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ hdata = ic.readFloatArray("hdataarray", null);
+ if (hdata == null) {
+ FloatBuffer buf = ic.readFloatBuffer("hdata", null);
+ if (buf != null) {
+ hdata = new float[buf.limit()];
+ buf.get(hdata);
+ }
+ }
+ width = ic.readInt("width", 0);
+ height = ic.readInt("height", 0);
+ maxval = ic.readInt("maxval", 0);
+ }
+
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/ProgressMonitor.java b/engine/src/terrain/com/jme3/terrain/ProgressMonitor.java
new file mode 100644
index 0000000..4664231
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/ProgressMonitor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain;
+
+/**
+ * Monitor the progress of an expensive terrain operation.
+ *
+ * Monitors are passed into the expensive operations, and those operations
+ * call the incrementProgress method whenever they determine that progress
+ * has changed. It is up to the monitor to determine if the increment is
+ * percentage or a unit of another measure, but anything calling it should
+ * use the setMonitorMax() method and make sure incrementProgress() match up
+ * in terms of units.
+ *
+ * @author Brent Owens
+ */
+public interface ProgressMonitor {
+
+ /**
+ * Increment the progress by a unit.
+ */
+ public void incrementProgress(float increment);
+
+ /**
+ * The max value that when reached, the progress is at 100%.
+ */
+ public void setMonitorMax(float max);
+
+ /**
+ * The max value of the progress. When incrementProgress()
+ * reaches this value, progress is complete
+ */
+ public float getMonitorMax();
+
+ /**
+ * The progress has completed
+ */
+ public void progressComplete();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/Terrain.java b/engine/src/terrain/com/jme3/terrain/Terrain.java
new file mode 100644
index 0000000..6f3872e
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/Terrain.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain;
+
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
+import java.util.List;
+
+/**
+ * Terrain can be one or many meshes comprising of a, probably large, piece of land.
+ * Terrain is Y-up in the grid axis, meaning gravity acts in the -Y direction.
+ * Level of Detail (LOD) is supported and expected as terrains can get very large. LOD can
+ * also be disabled if you so desire, however some terrain implementations can choose to ignore
+ * useLOD(boolean).
+ * Terrain implementations should extend Node, or at least Spatial.
+ *
+ * @author bowens
+ */
+public interface Terrain {
+
+ /**
+ * Get the real-world height of the terrain at the specified X-Z coorindate.
+ * @param xz the X-Z world coordinate
+ * @return the height at the given point
+ */
+ public float getHeight(Vector2f xz);
+
+ /**
+ * Get the normal vector for the surface of the terrain at the specified
+ * X-Z coordinate. This normal vector can be a close approximation. It does not
+ * take into account any normal maps on the material.
+ * @param xz the X-Z world coordinate
+ * @return the normal vector at the given point
+ */
+ public Vector3f getNormal(Vector2f xz);
+
+ /**
+ * Get the heightmap height at the specified X-Z coordinate. This does not
+ * count scaling and snaps the XZ coordinate to the nearest (rounded) heightmap grid point.
+ * @param xz world coordinate
+ * @return the height, unscaled and uninterpolated
+ */
+ public float getHeightmapHeight(Vector2f xz);
+
+ /**
+ * Set the height at the specified X-Z coordinate.
+ * To set the height of the terrain and see it, you will have
+ * to unlock the terrain meshes by calling terrain.setLocked(false) before
+ * you call setHeight().
+ * @param xzCoordinate coordinate to set the height
+ * @param height that will be set at the coordinate
+ */
+ public void setHeight(Vector2f xzCoordinate, float height);
+
+ /**
+ * Set the height at many points. The two lists must be the same size.
+ * Each xz coordinate entry matches to a height entry, 1 for 1. So the
+ * first coordinate matches to the first height value, the last to the
+ * last etc.
+ * @param xz a list of coordinates where the hight will be set
+ * @param height the heights that match the xz coordinates
+ */
+ public void setHeight(List<Vector2f> xz, List<Float> height);
+
+ /**
+ * Raise/lower the height in one call (instead of getHeight then setHeight).
+ * @param xzCoordinate world coordinate to adjust the terrain height
+ * @param delta +- value to adjust the height by
+ */
+ public void adjustHeight(Vector2f xzCoordinate, float delta);
+
+ /**
+ * Raise/lower the height at many points. The two lists must be the same size.
+ * Each xz coordinate entry matches to a height entry, 1 for 1. So the
+ * first coordinate matches to the first height value, the last to the
+ * last etc.
+ * @param xz a list of coordinates where the hight will be adjusted
+ * @param height +- value to adjust the height by, that matches the xz coordinates
+ */
+ public void adjustHeight(List<Vector2f> xz, List<Float> height);
+
+ /**
+ * Get the heightmap of the entire terrain.
+ * This can return null if that terrain object does not store the height data.
+ * Infinite or "paged" terrains will not be able to support this, so use with caution.
+ */
+ public float[] getHeightMap();
+
+ /**
+ * This is calculated by the specific LOD algorithm.
+ * A value of one means that the terrain is showing full detail.
+ * The higher the value, the more the terrain has been generalized
+ * and the less detailed it will be.
+ */
+ public int getMaxLod();
+
+ /**
+ * Called by an LodControl.
+ * Calculates the level of detail of the terrain and adjusts its geometry.
+ * This is where the Terrain's LOD algorithm will change the detail of
+ * the terrain based on how far away this position is from the particular
+ * terrain patch.
+ * @param location the Camera's location. A list of one camera location is normal
+ * if you just have one camera in your scene.
+ */
+ public void update(List<Vector3f> location, LodCalculator lodCalculator);
+
+ /**
+ * Lock or unlock the meshes of this terrain.
+ * Locked meshes are un-editable but have better performance.
+ * This should call the underlying getMesh().setStatic()/setDynamic() methods.
+ * @param locked or unlocked
+ */
+ public void setLocked(boolean locked);
+
+ /**
+ * Pre-calculate entropy values.
+ * Some terrain systems support entropy calculations to determine LOD
+ * changes. Often these entropy calculations are expensive and can be
+ * cached ahead of time. Use this method to do that.
+ */
+ public void generateEntropy(ProgressMonitor monitor);
+
+ /**
+ * Returns the material that this terrain uses.
+ * If it uses many materials, just return the one you think is best.
+ * For TerrainQuads this is sufficient. For TerrainGrid you want to call
+ * getMaterial(Vector3f) instead.
+ */
+ public Material getMaterial();
+
+ /**
+ * Returns the material that this terrain uses.
+ * Terrain can have different materials in different locations.
+ * In general, the TerrainQuad will only have one material. But
+ * TerrainGrid will have a different material per tile.
+ *
+ * It could be possible to pass in null for the location, some Terrain
+ * implementations might just have the one material and not care where
+ * you are looking. So implementations must handle null being supplied.
+ *
+ * @param worldLocation the location, in world coordinates, of where
+ * we are interested in the underlying texture.
+ */
+ public Material getMaterial(Vector3f worldLocation);
+
+ /**
+ * Used for painting to get the number of vertices along the edge of the
+ * terrain.
+ * This is an un-scaled size, and should represent the vertex count (ie. the
+ * texture coord count) along an edge of a square terrain.
+ *
+ * In the standard TerrainQuad default implementation, this will return
+ * the "totalSize" of the terrain (512 or so).
+ */
+ public int getTerrainSize();
+
+ /**
+ * Get the scale of the texture coordinates. Normally if the texture is
+ * laid on the terrain and not scaled so that the texture does not repeat,
+ * then each texture coordinate (on a vertex) will be 1/(terrain size).
+ * That is: the coverage between each consecutive texture coordinate will
+ * be a percentage of the total terrain size.
+ * So if the terrain is 512 vertexes wide, then each texture coord will cover
+ * 1/512 (or 0.00195) percent of the texture.
+ * This is used for converting between tri-planar texture scales and regular
+ * texture scales.
+ *
+ * not needed
+ */
+ //public float getTextureCoordinateScale();
+
+ /**
+ *
+ *
+ */
+ public int getNumMajorSubdivisions();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
new file mode 100644
index 0000000..3944b98
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.FastMath;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.terrain.GeoMap;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Produces the mesh for the TerrainPatch.
+ * This LOD algorithm generates a single triangle strip by first building the center of the
+ * mesh, minus one outer edge around it. Then it builds the edges in counter-clockwise order,
+ * starting at the bottom right and working up, then left across the top, then down across the
+ * left, then right across the bottom.
+ * It needs to know what its neighbour's LOD's are so it can stitch the edges.
+ * It creates degenerate polygons in order to keep the winding order of the polygons and to move
+ * the strip to a new position while still maintaining the continuity of the overall mesh. These
+ * degenerates are removed quickly by the video card.
+ *
+ * @author Brent Owens
+ */
+public class LODGeomap extends GeoMap {
+
+ public LODGeomap() {
+ }
+
+ @Deprecated
+ public LODGeomap(int size, FloatBuffer heightMap) {
+ super(heightMap, size, size, 1);
+ }
+
+ public LODGeomap(int size, float[] heightMap) {
+ super(heightMap, size, size, 1);
+ }
+
+ public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center) {
+ return this.createMesh(scale, tcScale, tcOffset, offsetAmount, totalSize, center, 1, false, false, false, false);
+ }
+
+ public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod) {
+ FloatBuffer pb = writeVertexArray(null, scale, center);
+ FloatBuffer texb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize);
+ FloatBuffer nb = writeNormalArray(null, scale);
+ IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod);
+ FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
+ FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
+ writeTangentArray(tanb, bb, texb, scale);
+ Mesh m = new Mesh();
+ m.setMode(Mode.TriangleStrip);
+ m.setBuffer(Type.Position, 3, pb);
+ m.setBuffer(Type.Normal, 3, nb);
+ m.setBuffer(Type.Tangent, 3, tanb);
+ m.setBuffer(Type.Binormal, 3, bb);
+ m.setBuffer(Type.TexCoord, 2, texb);
+ m.setBuffer(Type.Index, 3, ib);
+ m.setStatic();
+ m.updateBound();
+ return m;
+ }
+
+ public FloatBuffer writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale, float offsetAmount, int totalSize) {
+ if (store != null) {
+ if (store.remaining() < getWidth() * getHeight() * 2) {
+ throw new BufferUnderflowException();
+ }
+ } else {
+ store = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 2);
+ }
+
+ if (offset == null) {
+ offset = new Vector2f();
+ }
+
+ Vector2f tcStore = new Vector2f();
+
+ // work from bottom of heightmap up, so we don't flip the coords
+ for (int y = getHeight() - 1; y >= 0; y--) {
+ for (int x = 0; x < getWidth(); x++) {
+ getUV(x, y, tcStore, offset, offsetAmount, totalSize);
+ float tx = tcStore.x * scale.x;
+ float ty = tcStore.y * scale.y;
+ store.put(tx);
+ store.put(ty);
+ }
+ }
+
+ return store;
+ }
+
+ public Vector2f getUV(int x, int y, Vector2f store, Vector2f offset, float offsetAmount, int totalSize) {
+ float offsetX = offset.x + (offsetAmount * 1.0f);
+ float offsetY = -offset.y + (offsetAmount * 1.0f);//note the -, we flip the tex coords
+
+ store.set((((float) x) + offsetX) / (float) (totalSize - 1), // calculates percentage of texture here
+ (((float) y) + offsetY) / (float) (totalSize - 1));
+ return store;
+ }
+
+ /**
+ * Create the LOD index array that will seam its edges with its neighbour's LOD.
+ * This is a scary method!!! It will break your mind.
+ *
+ * @param store to store the index buffer
+ * @param lod level of detail of the mesh
+ * @param rightLod LOD of the right neighbour
+ * @param topLod LOD of the top neighbour
+ * @param leftLod LOD of the left neighbour
+ * @param bottomLod LOD of the bottom neighbour
+ * @return the LOD-ified index buffer
+ */
+ public IntBuffer writeIndexArrayLodDiff(IntBuffer store, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod) {
+
+ IntBuffer buffer2 = store;
+ int numIndexes = calculateNumIndexesLodDiff(lod);
+ if (store == null) {
+ buffer2 = BufferUtils.createIntBuffer(numIndexes);
+ }
+ VerboseIntBuffer buffer = new VerboseIntBuffer(buffer2);
+
+
+ // generate center squares minus the edges
+ //System.out.println("for (x="+lod+"; x<"+(getWidth()-(2*lod))+"; x+="+lod+")");
+ //System.out.println(" for (z="+lod+"; z<"+(getWidth()-(1*lod))+"; z+="+lod+")");
+ for (int r = lod; r < getWidth() - (2 * lod); r += lod) { // row
+ int rowIdx = r * getWidth();
+ int nextRowIdx = (r + 1 * lod) * getWidth();
+ for (int c = lod; c < getWidth() - (1 * lod); c += lod) { // column
+ int idx = rowIdx + c;
+ buffer.put(idx);
+ idx = nextRowIdx + c;
+ buffer.put(idx);
+ }
+
+ // add degenerate triangles
+ if (r < getWidth() - (3 * lod)) {
+ int idx = nextRowIdx + getWidth() - (1 * lod) - 1;
+ buffer.put(idx);
+ idx = nextRowIdx + (1 * lod); // inset by 1
+ buffer.put(idx);
+ //System.out.println("");
+ }
+ }
+ //System.out.println("\nright:");
+
+ //int runningBufferCount = buffer.getCount();
+ //System.out.println("buffer start: "+runningBufferCount);
+
+
+ // right
+ int br = getWidth() * (getWidth() - lod) - 1 - lod;
+ buffer.put(br); // bottom right -1
+ int corner = getWidth() * getWidth() - 1;
+ buffer.put(corner); // bottom right corner
+ if (rightLod) { // if lower LOD
+ for (int row = getWidth() - lod; row >= 1 + lod; row -= 2 * lod) {
+ int idx = (row) * getWidth() - 1 - lod;
+ buffer.put(idx);
+ idx = (row - lod) * getWidth() - 1;
+ buffer.put(idx);
+ if (row > lod + 1) { //if not the last one
+ idx = (row - lod) * getWidth() - 1 - lod;
+ buffer.put(idx);
+ idx = (row - lod) * getWidth() - 1;
+ buffer.put(idx);
+ } else {
+ }
+ }
+ } else {
+ buffer.put(corner);//br+1);//degenerate to flip winding order
+ for (int row = getWidth() - lod; row > lod; row -= lod) {
+ int idx = row * getWidth() - 1; // mult to get row
+ buffer.put(idx);
+ buffer.put(idx - lod);
+ }
+
+ }
+
+ buffer.put(getWidth() - 1);
+
+
+ //System.out.println("\nbuffer right: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+
+ //System.out.println("\ntop:");
+
+ // top (the order gets reversed here so the diagonals line up)
+ if (topLod) { // if lower LOD
+ if (rightLod) {
+ buffer.put(getWidth() - 1);
+ }
+ for (int col = getWidth() - 1; col >= lod; col -= 2 * lod) {
+ int idx = (lod * getWidth()) + col - lod; // next row
+ buffer.put(idx);
+ idx = col - 2 * lod;
+ buffer.put(idx);
+ if (col > lod * 2) { //if not the last one
+ idx = (lod * getWidth()) + col - 2 * lod;
+ buffer.put(idx);
+ idx = col - 2 * lod;
+ buffer.put(idx);
+ } else {
+ }
+ }
+ } else {
+ if (rightLod) {
+ buffer.put(getWidth() - 1);
+ }
+ for (int col = getWidth() - 1 - lod; col > 0; col -= lod) {
+ int idx = col + (lod * getWidth());
+ buffer.put(idx);
+ idx = col;
+ buffer.put(idx);
+ }
+ buffer.put(0);
+ }
+ buffer.put(0);
+
+ //System.out.println("\nbuffer top: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+ //System.out.println("\nleft:");
+
+ // left
+ if (leftLod) { // if lower LOD
+ if (topLod) {
+ buffer.put(0);
+ }
+ for (int row = 0; row < getWidth() - lod; row += 2 * lod) {
+ int idx = (row + lod) * getWidth() + lod;
+ buffer.put(idx);
+ idx = (row + 2 * lod) * getWidth();
+ buffer.put(idx);
+ if (row < getWidth() - lod - 2 - 1) { //if not the last one
+ idx = (row + 2 * lod) * getWidth() + lod;
+ buffer.put(idx);
+ idx = (row + 2 * lod) * getWidth();
+ buffer.put(idx);
+ } else {
+ }
+ }
+ } else {
+ if (!topLod) {
+ buffer.put(0);
+ }
+ //buffer.put(getWidth()+1); // degenerate
+ //buffer.put(0); // degenerate winding-flip
+ for (int row = lod; row < getWidth() - lod; row += lod) {
+ int idx = row * getWidth();
+ buffer.put(idx);
+ idx = row * getWidth() + lod;
+ buffer.put(idx);
+ }
+
+ }
+ buffer.put(getWidth() * (getWidth() - 1));
+
+
+ //System.out.println("\nbuffer left: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+ //if (true) return buffer.delegate;
+ //System.out.println("\nbottom");
+
+ // bottom
+ if (bottomLod) { // if lower LOD
+ if (leftLod) {
+ buffer.put(getWidth() * (getWidth() - 1));
+ }
+ // there was a slight bug here when really high LOD near maxLod
+ // far right has extra index one row up and all the way to the right, need to skip last index entered
+ // seemed to be fixed by making "getWidth()-1-2-lod" this: "getWidth()-1-2*lod", which seems more correct
+ for (int col = 0; col < getWidth() - lod; col += 2 * lod) {
+ int idx = getWidth() * (getWidth() - 1 - lod) + col + lod;
+ buffer.put(idx);
+ idx = getWidth() * (getWidth() - 1) + col + 2 * lod;
+ buffer.put(idx);
+ if (col < getWidth() - 1 - 2 * lod) { //if not the last one
+ idx = getWidth() * (getWidth() - 1 - lod) + col + 2 * lod;
+ buffer.put(idx);
+ idx = getWidth() * (getWidth() - 1) + col + 2 * lod;
+ buffer.put(idx);
+ } else {
+ }
+ }
+ } else {
+ if (leftLod) {
+ buffer.put(getWidth() * (getWidth() - 1));
+ }
+ for (int col = lod; col < getWidth() - lod; col += lod) {
+ int idx = getWidth() * (getWidth() - 1 - lod) + col; // up
+ buffer.put(idx);
+ idx = getWidth() * (getWidth() - 1) + col; // down
+ buffer.put(idx);
+ }
+ //buffer.put(getWidth()*getWidth()-1-lod); // <-- THIS caused holes at the end!
+ }
+
+ buffer.put(getWidth() * getWidth() - 1);
+
+ //System.out.println("\nbuffer bottom: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+ //System.out.println("\nBuffer size: "+buffer.getCount());
+
+ // fill in the rest of the buffer with degenerates, there should only be a couple
+ for (int i = buffer.getCount(); i < numIndexes; i++) {
+ buffer.put(getWidth() * getWidth() - 1);
+ }
+
+ return buffer.delegate;
+ }
+
+ public IntBuffer writeIndexArrayLodVariable(IntBuffer store, int lod, int rightLod, int topLod, int leftLod, int bottomLod) {
+
+ IntBuffer buffer2 = store;
+ int numIndexes = calculateNumIndexesLodDiff(lod);
+ if (store == null) {
+ buffer2 = BufferUtils.createIntBuffer(numIndexes);
+ }
+ VerboseIntBuffer buffer = new VerboseIntBuffer(buffer2);
+
+
+ // generate center squares minus the edges
+ //System.out.println("for (x="+lod+"; x<"+(getWidth()-(2*lod))+"; x+="+lod+")");
+ //System.out.println(" for (z="+lod+"; z<"+(getWidth()-(1*lod))+"; z+="+lod+")");
+ for (int r = lod; r < getWidth() - (2 * lod); r += lod) { // row
+ int rowIdx = r * getWidth();
+ int nextRowIdx = (r + 1 * lod) * getWidth();
+ for (int c = lod; c < getWidth() - (1 * lod); c += lod) { // column
+ int idx = rowIdx + c;
+ buffer.put(idx);
+ idx = nextRowIdx + c;
+ buffer.put(idx);
+ }
+
+ // add degenerate triangles
+ if (r < getWidth() - (3 * lod)) {
+ int idx = nextRowIdx + getWidth() - (1 * lod) - 1;
+ buffer.put(idx);
+ idx = nextRowIdx + (1 * lod); // inset by 1
+ buffer.put(idx);
+ //System.out.println("");
+ }
+ }
+ //System.out.println("\nright:");
+
+ //int runningBufferCount = buffer.getCount();
+ //System.out.println("buffer start: "+runningBufferCount);
+
+
+ // right
+ int br = getWidth() * (getWidth() - lod) - 1 - lod;
+ buffer.put(br); // bottom right -1
+ int corner = getWidth() * getWidth() - 1;
+ buffer.put(corner); // bottom right corner
+ if (rightLod > lod) { // if lower LOD
+ int idx = corner;
+ int it = (getWidth() - 1) / rightLod; // iterations
+ int lodDiff = rightLod / lod;
+ for (int i = it; i > 0; i--) { // for each lod level of the neighbour
+ idx = getWidth() * (i * rightLod + 1) - 1;
+ for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
+ int idxB = idx - (getWidth() * (j * lod)) - lod;
+
+ if (j == lodDiff && i == 1) {// the last one
+ buffer.put(getWidth() - 1);
+ } else if (j == lodDiff) {
+ buffer.put(idxB);
+ buffer.put(idxB + lod);
+ } else {
+ buffer.put(idxB);
+ buffer.put(idx);
+ }
+ }
+ }
+ // reset winding order
+ buffer.put(getWidth() * (lod + 1) - lod - 1); // top-right +1row
+ buffer.put(getWidth() - 1);// top-right
+
+ } else {
+ buffer.put(corner);//br+1);//degenerate to flip winding order
+ for (int row = getWidth() - lod; row > lod; row -= lod) {
+ int idx = row * getWidth() - 1; // mult to get row
+ buffer.put(idx);
+ buffer.put(idx - lod);
+ }
+ buffer.put(getWidth() - 1);
+ }
+
+
+ //System.out.println("\nbuffer right: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+
+ //System.out.println("\ntop:");
+
+ // top (the order gets reversed here so the diagonals line up)
+ if (topLod > lod) { // if lower LOD
+ if (rightLod > lod) {
+ // need to flip winding order
+ buffer.put(getWidth() - 1);
+ buffer.put(getWidth() * lod - 1);
+ buffer.put(getWidth() - 1);
+ }
+ int idx = getWidth() - 1;
+ int it = (getWidth() - 1) / topLod; // iterations
+ int lodDiff = topLod / lod;
+ for (int i = it; i > 0; i--) { // for each lod level of the neighbour
+ idx = (i * topLod);
+ for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
+ int idxB = lod * getWidth() + (i * topLod) - (j * lod);
+
+ if (j == lodDiff && i == 1) {// the last one
+ buffer.put(0);
+ } else if (j == lodDiff) {
+ buffer.put(idxB);
+ buffer.put(idx - topLod);
+ } else {
+ buffer.put(idxB);
+ buffer.put(idx);
+ }
+ }
+ }
+ } else {
+ if (rightLod > lod) {
+ buffer.put(getWidth() - 1);
+ }
+ for (int col = getWidth() - 1 - lod; col > 0; col -= lod) {
+ int idx = col + (lod * getWidth());
+ buffer.put(idx);
+ idx = col;
+ buffer.put(idx);
+ }
+ buffer.put(0);
+ }
+ buffer.put(0);
+
+ //System.out.println("\nbuffer top: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+ //System.out.println("\nleft:");
+
+ // left
+ if (leftLod > lod) { // if lower LOD
+
+ int idx = 0;
+ int it = (getWidth() - 1) / leftLod; // iterations
+ int lodDiff = leftLod / lod;
+ for (int i = 0; i < it; i++) { // for each lod level of the neighbour
+ idx = getWidth() * (i * leftLod);
+ for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
+ int idxB = idx + (getWidth() * (j * lod)) + lod;
+
+ if (j == lodDiff && i == it - 1) {// the last one
+ buffer.put(getWidth() * getWidth() - getWidth());
+ } else if (j == lodDiff) {
+ buffer.put(idxB);
+ buffer.put(idxB - lod);
+ } else {
+ buffer.put(idxB);
+ buffer.put(idx);
+ }
+ }
+ }
+
+ } else {
+ buffer.put(0);
+ buffer.put(getWidth() * lod + lod);
+ buffer.put(0);
+ for (int row = lod; row < getWidth() - lod; row += lod) {
+ int idx = row * getWidth();
+ buffer.put(idx);
+ idx = row * getWidth() + lod;
+ buffer.put(idx);
+ }
+ buffer.put(getWidth() * (getWidth() - 1));
+ }
+ //buffer.put(getWidth()*(getWidth()-1));
+
+
+ //System.out.println("\nbuffer left: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+ //if (true) return buffer.delegate;
+ //System.out.println("\nbottom");
+
+ // bottom
+ if (bottomLod > lod) { // if lower LOD
+ if (leftLod > lod) {
+ buffer.put(getWidth() * (getWidth() - 1));
+ buffer.put(getWidth() * (getWidth() - lod));
+ buffer.put(getWidth() * (getWidth() - 1));
+ }
+
+ int idx = getWidth() * getWidth() - getWidth();
+ int it = (getWidth() - 1) / bottomLod; // iterations
+ int lodDiff = bottomLod / lod;
+ for (int i = 0; i < it; i++) { // for each lod level of the neighbour
+ idx = getWidth() * getWidth() - getWidth() + (i * bottomLod);
+ for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
+ int idxB = idx - (getWidth() * lod) + j * lod;
+
+ if (j == lodDiff && i == it - 1) {// the last one
+ buffer.put(getWidth() * getWidth() - 1);
+ } else if (j == lodDiff) {
+ buffer.put(idxB);
+ buffer.put(idx + bottomLod);
+ } else {
+ buffer.put(idxB);
+ buffer.put(idx);
+ }
+ }
+ }
+ } else {
+ if (leftLod > lod) {
+ buffer.put(getWidth() * (getWidth() - 1));
+ buffer.put(getWidth() * getWidth() - (getWidth() * lod) + lod);
+ buffer.put(getWidth() * (getWidth() - 1));
+ }
+ for (int col = lod; col < getWidth() - lod; col += lod) {
+ int idx = getWidth() * (getWidth() - 1 - lod) + col; // up
+ buffer.put(idx);
+ idx = getWidth() * (getWidth() - 1) + col; // down
+ buffer.put(idx);
+ }
+ //buffer.put(getWidth()*getWidth()-1-lod); // <-- THIS caused holes at the end!
+ }
+
+ buffer.put(getWidth() * getWidth() - 1);
+
+ //System.out.println("\nbuffer bottom: "+(buffer.getCount()-runningBufferCount));
+ //runningBufferCount = buffer.getCount();
+
+ //System.out.println("\nBuffer size: "+buffer.getCount());
+
+ // fill in the rest of the buffer with degenerates, there should only be a couple
+ for (int i = buffer.getCount(); i < numIndexes; i++) {
+ buffer.put(getWidth() * getWidth() - 1);
+ }
+
+ return buffer.delegate;
+ }
+
+
+ /*private int calculateNumIndexesNormal(int lod) {
+ int length = getWidth()-1;
+ int num = ((length/lod)+1)*((length/lod)+1)*2;
+ System.out.println("num: "+num);
+ num -= 2*((length/lod)+1);
+ System.out.println("num2: "+num);
+ // now get the degenerate indexes that exist between strip rows
+ num += 2*(((length/lod)+1)-2); // every row except the first and last
+ System.out.println("Index buffer size: "+num);
+ return num;
+ }*/
+ /**
+ * calculate how many indexes there will be.
+ * This isn't that precise and there might be a couple extra.
+ */
+ private int calculateNumIndexesLodDiff(int lod) {
+ if (lod == 0) {
+ lod = 1;
+ }
+ int length = getWidth() - 1; // make it even for lod calc
+ int side = (length / lod) + 1 - (2);
+ //System.out.println("side: "+side);
+ int num = side * side * 2;
+ //System.out.println("num: "+num);
+ num -= 2 * side; // remove one first row and one last row (they are only hit once each)
+ //System.out.println("num2: "+num);
+ // now get the degenerate indexes that exist between strip rows
+ int degenerates = 2 * (side - (2)); // every row except the first and last
+ num += degenerates;
+ //System.out.println("degenerates: "+degenerates);
+
+ //System.out.println("center, before edges: "+num);
+
+ num += (getWidth() / lod) * 2 * 4;
+ num++;
+
+ num += 10;// TODO remove me: extra
+ //System.out.println("Index buffer size: "+num);
+ return num;
+ }
+
+ public FloatBuffer[] writeTangentArray(FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {
+ if (!isLoaded()) {
+ throw new NullPointerException();
+ }
+
+ if (tangentStore != null) {
+ if (tangentStore.remaining() < getWidth() * getHeight() * 3) {
+ throw new BufferUnderflowException();
+ }
+ } else {
+ tangentStore = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
+ }
+ tangentStore.rewind();
+
+ if (binormalStore != null) {
+ if (binormalStore.remaining() < getWidth() * getHeight() * 3) {
+ throw new BufferUnderflowException();
+ }
+ } else {
+ binormalStore = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
+ }
+ binormalStore.rewind();
+
+ Vector3f tangent = new Vector3f();
+ Vector3f binormal = new Vector3f();
+ Vector3f v1 = new Vector3f();
+ Vector3f v2 = new Vector3f();
+ Vector3f v3 = new Vector3f();
+ Vector2f t1 = new Vector2f();
+ Vector2f t2 = new Vector2f();
+ Vector2f t3 = new Vector2f();
+
+ //scale = Vector3f.UNIT_XYZ;
+
+ for (int r = 0; r < getHeight(); r++) {
+ for (int c = 0; c < getWidth(); c++) {
+
+ int texIdx = ((getHeight() - 1 - r) * getWidth() + c) * 2; // pull from the end
+ int texIdxAbove = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end
+ int texIdxNext = ((getHeight() - 1 - (r + 1)) * getWidth() + c) * 2; // pull from the end
+
+ v1.set(c, getValue(c, r), r);
+ t1.set(textureBuffer.get(texIdx), textureBuffer.get(texIdx + 1));
+
+ // below
+ if (r == getHeight()-1) { // last row
+ v3.set(c, getValue(c, r), r + 1);
+ float u = textureBuffer.get(texIdx) - textureBuffer.get(texIdxAbove);
+ u += textureBuffer.get(texIdx);
+ float v = textureBuffer.get(texIdx + 1) - textureBuffer.get(texIdxAbove + 1);
+ v += textureBuffer.get(texIdx + 1);
+ t3.set(u, v);
+ } else {
+ v3.set(c, getValue(c, r + 1), r + 1);
+ t3.set(textureBuffer.get(texIdxNext), textureBuffer.get(texIdxNext + 1));
+ }
+
+ //right
+ if (c == getWidth()-1) { // last column
+ v2.set(c + 1, getValue(c, r), r);
+ float u = textureBuffer.get(texIdx) - textureBuffer.get(texIdx - 2);
+ u += textureBuffer.get(texIdx);
+ float v = textureBuffer.get(texIdx + 1) - textureBuffer.get(texIdx - 1);
+ v += textureBuffer.get(texIdx - 1);
+ t2.set(u, v);
+ } else {
+ v2.set(c + 1, getValue(c + 1, r), r); // one to the right
+ t2.set(textureBuffer.get(texIdx + 2), textureBuffer.get(texIdx + 3));
+ }
+
+ calculateTangent(new Vector3f[]{v1.mult(scale), v2.mult(scale), v3.mult(scale)}, new Vector2f[]{t1, t2, t3}, tangent, binormal);
+ BufferUtils.setInBuffer(tangent, tangentStore, (r * getWidth() + c)); // save the tangent
+ BufferUtils.setInBuffer(binormal, binormalStore, (r * getWidth() + c)); // save the binormal
+ }
+ }
+
+ return new FloatBuffer[]{tangentStore, binormalStore};
+ }
+
+ /**
+ *
+ * @param v Takes 3 vertices: root, right, bottom
+ * @param t Takes 3 tex coords: root, right, bottom
+ * @param tangent that will store the result
+ * @return the tangent store
+ */
+ public static Vector3f calculateTangent(Vector3f[] v, Vector2f[] t, Vector3f tangent, Vector3f binormal) {
+ Vector3f edge1 = new Vector3f(); // y=0
+ Vector3f edge2 = new Vector3f(); // x=0
+ Vector2f edge1uv = new Vector2f(); // y=0
+ Vector2f edge2uv = new Vector2f(); // x=0
+
+ t[2].subtract(t[0], edge2uv);
+ t[1].subtract(t[0], edge1uv);
+
+ float det = edge1uv.x * edge2uv.y;// - edge1uv.y*edge2uv.x; = 0
+
+ boolean normalize = true;
+ if (Math.abs(det) < 0.0000001f) {
+ det = 1;
+ normalize = true;
+ }
+
+ v[1].subtract(v[0], edge1);
+ v[2].subtract(v[0], edge2);
+
+ tangent.set(edge1);
+ tangent.normalizeLocal();
+ binormal.set(edge2);
+ binormal.normalizeLocal();
+
+ float factor = 1 / det;
+ tangent.x = (edge2uv.y * edge1.x) * factor;
+ tangent.y = 0;
+ tangent.z = (edge2uv.y * edge1.z) * factor;
+ if (normalize) {
+ tangent.normalizeLocal();
+ }
+
+ binormal.x = 0;
+ binormal.y = (edge1uv.x * edge2.y) * factor;
+ binormal.z = (edge1uv.x * edge2.z) * factor;
+ if (normalize) {
+ binormal.normalizeLocal();
+ }
+
+ return tangent;
+ }
+
+ @Override
+ public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) {
+ if (!isLoaded()) {
+ throw new NullPointerException();
+ }
+
+ if (store != null) {
+ if (store.remaining() < getWidth() * getHeight() * 3) {
+ throw new BufferUnderflowException();
+ }
+ } else {
+ store = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
+ }
+ store.rewind();
+
+ TempVars vars = TempVars.get();
+
+ Vector3f rootPoint = vars.vect1;
+ Vector3f rightPoint = vars.vect2;
+ Vector3f leftPoint = vars.vect3;
+ Vector3f topPoint = vars.vect4;
+ Vector3f bottomPoint = vars.vect5;
+
+ Vector3f tmp1 = vars.vect6;
+
+ // calculate normals for each polygon
+ for (int r = 0; r < getHeight(); r++) {
+ for (int c = 0; c < getWidth(); c++) {
+
+ rootPoint.set(c, getValue(c, r), r);
+ Vector3f normal = vars.vect8;
+
+ if (r == 0) { // first row
+ if (c == 0) { // first column
+ rightPoint.set(c + 1, getValue(c + 1, r), r);
+ bottomPoint.set(c, getValue(c, r + 1), r + 1);
+ getNormal(bottomPoint, rootPoint, rightPoint, scale, normal);
+ } else if (c == getWidth() - 1) { // last column
+ leftPoint.set(c - 1, getValue(c - 1, r), r);
+ bottomPoint.set(c, getValue(c, r + 1), r + 1);
+ getNormal(leftPoint, rootPoint, bottomPoint, scale, normal);
+ } else { // all middle columns
+ leftPoint.set(c - 1, getValue(c - 1, r), r);
+ rightPoint.set(c + 1, getValue(c + 1, r), r);
+ bottomPoint.set(c, getValue(c, r + 1), r + 1);
+
+ normal.set( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
+ normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
+ normal.normalizeLocal();
+ }
+ } else if (r == getHeight() - 1) { // last row
+ if (c == 0) { // first column
+ topPoint.set(c, getValue(c, r - 1), r - 1);
+ rightPoint.set(c + 1, getValue(c + 1, r), r);
+ getNormal(rightPoint, rootPoint, topPoint, scale, normal);
+ } else if (c == getWidth() - 1) { // last column
+ topPoint.set(c, getValue(c, r - 1), r - 1);
+ leftPoint.set(c - 1, getValue(c - 1, r), r);
+ getNormal(topPoint, rootPoint, leftPoint, scale, normal);
+ } else { // all middle columns
+ topPoint.set(c, getValue(c, r - 1), r - 1);
+ leftPoint.set(c - 1, getValue(c - 1, r), r);
+ rightPoint.set(c + 1, getValue(c + 1, r), r);
+
+ normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );
+ normal.add( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
+ normal.normalizeLocal();
+ }
+ } else { // all middle rows
+ if (c == 0) { // first column
+ topPoint.set(c, getValue(c, r - 1), r - 1);
+ rightPoint.set(c + 1, getValue(c + 1, r), r);
+ bottomPoint.set(c, getValue(c, r + 1), r + 1);
+
+ normal.set( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
+ normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
+ normal.normalizeLocal();
+ } else if (c == getWidth() - 1) { // last column
+ topPoint.set(c, getValue(c, r - 1), r - 1);
+ leftPoint.set(c - 1, getValue(c - 1, r), r);
+ bottomPoint.set(c, getValue(c, r + 1), r + 1); //XXX wrong
+
+ normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );
+ normal.add( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
+ normal.normalizeLocal();
+ } else { // all middle columns
+ topPoint.set(c, getValue(c, r - 1), r - 1);
+ leftPoint.set(c - 1, getValue(c - 1, r), r);
+ rightPoint.set(c + 1, getValue(c + 1, r), r);
+ bottomPoint.set(c, getValue(c, r + 1), r + 1);
+
+ normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1 ) );
+ normal.add( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
+ normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
+ normal.add( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
+ normal.normalizeLocal();
+ }
+ }
+
+ BufferUtils.setInBuffer(normal, store, (r * getWidth() + c)); // save the normal
+ }
+ }
+ vars.release();
+
+ return store;
+ }
+
+ private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale, Vector3f store) {
+ float x1 = firstPoint.x - rootPoint.x;
+ float y1 = firstPoint.y - rootPoint.y;
+ float z1 = firstPoint.z - rootPoint.z;
+ x1 *= scale.x;
+ y1 *= scale.y;
+ z1 *= scale.z;
+ float x2 = secondPoint.x - rootPoint.x;
+ float y2 = secondPoint.y - rootPoint.y;
+ float z2 = secondPoint.z - rootPoint.z;
+ x2 *= scale.x;
+ y2 *= scale.y;
+ z2 *= scale.z;
+ float x3 = (y1 * z2) - (z1 * y2);
+ float y3 = (z1 * x2) - (x1 * z2);
+ float z3 = (x1 * y2) - (y1 * x2);
+
+ float inv = 1.0f / FastMath.sqrt(x3 * x3 + y3 * y3 + z3 * z3);
+ store.x = x3 * inv;
+ store.y = y3 * inv;
+ store.z = z3 * inv;
+ return store;
+
+ /*store.set( firstPoint.subtractLocal(rootPoint).multLocal(scale).crossLocal(secondPoint.subtractLocal(rootPoint).multLocal(scale)).normalizeLocal() );
+ return store;*/
+
+ }
+
+ /**
+ * Keeps a count of the number of indexes, good for debugging
+ */
+ public class VerboseIntBuffer {
+
+ private IntBuffer delegate;
+ int count = 0;
+
+ public VerboseIntBuffer(IntBuffer d) {
+ delegate = d;
+ }
+
+ public void put(int value) {
+ try {
+ delegate.put(value);
+ count++;
+ } catch (BufferOverflowException e) {
+ //System.out.println("err buffer size: "+delegate.capacity());
+ }
+ }
+
+ public int getCount() {
+ return count;
+ }
+ }
+
+ /**
+ * Get a representation of the underlying triangle at the given point,
+ * translated to world coordinates.
+ *
+ * @param x local x coordinate
+ * @param z local z coordinate
+ * @return a triangle in world space not local space
+ */
+ protected Triangle getTriangleAtPoint(float x, float z, Vector3f scale, Vector3f translation) {
+ Triangle tri = getTriangleAtPoint(x, z);
+ if (tri != null) {
+ tri.get1().multLocal(scale).addLocal(translation);
+ tri.get2().multLocal(scale).addLocal(translation);
+ tri.get3().multLocal(scale).addLocal(translation);
+ }
+ return tri;
+ }
+
+ /**
+ * Get the two triangles that make up the grid section at the specified point,
+ * translated to world coordinates.
+ *
+ * @param x local x coordinate
+ * @param z local z coordinate
+ * @param scale
+ * @param translation
+ * @return two triangles in world space not local space
+ */
+ protected Triangle[] getGridTrianglesAtPoint(float x, float z, Vector3f scale, Vector3f translation) {
+ Triangle[] tris = getGridTrianglesAtPoint(x, z);
+ if (tris != null) {
+ tris[0].get1().multLocal(scale).addLocal(translation);
+ tris[0].get2().multLocal(scale).addLocal(translation);
+ tris[0].get3().multLocal(scale).addLocal(translation);
+ tris[1].get1().multLocal(scale).addLocal(translation);
+ tris[1].get2().multLocal(scale).addLocal(translation);
+ tris[1].get3().multLocal(scale).addLocal(translation);
+ }
+ return tris;
+ }
+
+ /**
+ * Get the two triangles that make up the grid section at the specified point.
+ *
+ * For every grid space there are two triangles oriented like this:
+ * *----*
+ * |a / |
+ * | / b|
+ * *----*
+ * The corners of the mesh have differently oriented triangles. The two
+ * corners that we have to special-case are the top left and bottom right
+ * corners. They are oriented inversely:
+ * *----*
+ * | \ b|
+ * |a \ |
+ * *----*
+ *
+ * @param x local x coordinate
+ * @param z local z coordinate
+ * @param scale
+ * @param translation
+ * @return
+ */
+ protected Triangle[] getGridTrianglesAtPoint(float x, float z) {
+ int gridX = (int) x;
+ int gridY = (int) z;
+
+ int index = findClosestHeightIndex(gridX, gridY);
+ if (index < 0) {
+ return null;
+ }
+
+ Triangle t = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
+ Triangle t2 = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
+
+ float h1 = hdata[index]; // top left
+ float h2 = hdata[index + 1]; // top right
+ float h3 = hdata[index + width]; // bottom left
+ float h4 = hdata[index + width + 1]; // bottom right
+
+
+ if ((gridX == 0 && gridY == 0) || (gridX == width - 1 && gridY == width - 1)) {
+ // top left or bottom right grid point
+ t.get(0).x = (gridX);
+ t.get(0).y = (h1);
+ t.get(0).z = (gridY);
+
+ t.get(1).x = (gridX);
+ t.get(1).y = (h3);
+ t.get(1).z = (gridY + 1);
+
+ t.get(2).x = (gridX + 1);
+ t.get(2).y = (h4);
+ t.get(2).z = (gridY + 1);
+
+ t2.get(0).x = (gridX);
+ t2.get(0).y = (h1);
+ t2.get(0).z = (gridY);
+
+ t2.get(1).x = (gridX + 1);
+ t2.get(1).y = (h4);
+ t2.get(1).z = (gridY + 1);
+
+ t2.get(2).x = (gridX + 1);
+ t2.get(2).y = (h2);
+ t2.get(2).z = (gridY);
+ } else {
+ // all other grid points
+ t.get(0).x = (gridX);
+ t.get(0).y = (h1);
+ t.get(0).z = (gridY);
+
+ t.get(1).x = (gridX);
+ t.get(1).y = (h3);
+ t.get(1).z = (gridY + 1);
+
+ t.get(2).x = (gridX + 1);
+ t.get(2).y = (h2);
+ t.get(2).z = (gridY);
+
+ t2.get(0).x = (gridX + 1);
+ t2.get(0).y = (h2);
+ t2.get(0).z = (gridY);
+
+ t2.get(1).x = (gridX);
+ t2.get(1).y = (h3);
+ t2.get(1).z = (gridY + 1);
+
+ t2.get(2).x = (gridX + 1);
+ t2.get(2).y = (h4);
+ t2.get(2).z = (gridY + 1);
+ }
+
+ return new Triangle[]{t, t2};
+ }
+
+ /**
+ * Get the triangle that the point is on.
+ *
+ * @param x coordinate in local space to the geomap
+ * @param z coordinate in local space to the geomap
+ * @return triangle in local space to the geomap
+ */
+ protected Triangle getTriangleAtPoint(float x, float z) {
+ Triangle[] triangles = getGridTrianglesAtPoint(x, z);
+ if (triangles == null) {
+ //System.out.println("x,z: " + x + "," + z);
+ return null;
+ }
+ Vector2f point = new Vector2f(x, z);
+ Vector2f t1 = new Vector2f(triangles[0].get1().x, triangles[0].get1().z);
+ Vector2f t2 = new Vector2f(triangles[0].get2().x, triangles[0].get2().z);
+ Vector2f t3 = new Vector2f(triangles[0].get3().x, triangles[0].get3().z);
+
+ if (0 != FastMath.pointInsideTriangle(t1, t2, t3, point)) {
+ return triangles[0];
+ }
+
+ t1.set(triangles[1].get1().x, triangles[1].get1().z);
+ t1.set(triangles[1].get2().x, triangles[1].get2().z);
+ t1.set(triangles[1].get3().x, triangles[1].get3().z);
+
+ if (0 != FastMath.pointInsideTriangle(t1, t2, t3, point)) {
+ return triangles[1];
+ }
+
+ return null;
+ }
+
+ protected int findClosestHeightIndex(int x, int z) {
+
+ if (x < 0 || x >= width - 1) {
+ return -1;
+ }
+ if (z < 0 || z >= width - 1) {
+ return -1;
+ }
+
+ return z * width + x;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java
new file mode 100644
index 0000000..5d2fde3
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java
@@ -0,0 +1,122 @@
+package com.jme3.terrain.geomipmap;
+
+// Copyright 2007 Christian d'Heureuse, Inventec Informatik AG, Zurich,
+// Switzerland
+// www.source-code.biz, www.inventec.ch/chdh
+//
+// This module is multi-licensed and may be used under the terms
+// of any of the following licenses:
+//
+// EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
+// LGPL, GNU Lesser General Public License, V2 or later,
+// http://www.gnu.org/licenses/lgpl.html
+// GPL, GNU General Public License, V2 or later,
+// http://www.gnu.org/licenses/gpl.html
+// AL, Apache License, V2.0 or later, http://www.apache.org/licenses
+// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
+//
+// Please contact the author if you need another license.
+// This module is provided "as is", without warranties of any kind.
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An LRU cache, based on <code>LinkedHashMap</code>.
+ *
+ * <p>
+ * This cache has a fixed maximum number of elements (<code>cacheSize</code>).
+ * If the cache is full and another entry is added, the LRU (least recently
+ * used) entry is dropped.
+ *
+ * <p>
+ * This class is thread-safe. All methods of this class are synchronized.
+ *
+ * <p>
+ * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
+ * Multi-licensed: EPL / LGPL / GPL / AL / BSD.
+ */
+public class LRUCache<K, V> {
+
+ private static final float hashTableLoadFactor = 0.75f;
+ private LinkedHashMap<K, V> map;
+ private int cacheSize;
+
+ /**
+ * Creates a new LRU cache.
+ *
+ * @param cacheSize
+ * the maximum number of entries that will be kept in this cache.
+ */
+ public LRUCache(int cacheSize) {
+ this.cacheSize = cacheSize;
+ int hashTableCapacity = (int) Math.ceil(cacheSize / LRUCache.hashTableLoadFactor) + 1;
+ this.map = new LinkedHashMap<K, V>(hashTableCapacity, LRUCache.hashTableLoadFactor, true) {
+ // (an anonymous inner class)
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+ return this.size() > LRUCache.this.cacheSize;
+ }
+ };
+ }
+
+ /**
+ * Retrieves an entry from the cache.<br>
+ * The retrieved entry becomes the MRU (most recently used) entry.
+ *
+ * @param key
+ * the key whose associated value is to be returned.
+ * @return the value associated to this key, or null if no value with this
+ * key exists in the cache.
+ */
+ public synchronized V get(K key) {
+ return this.map.get(key);
+ }
+
+ /**
+ * Adds an entry to this cache.
+ * The new entry becomes the MRU (most recently used) entry.
+ * If an entry with the specified key already exists in the cache, it is
+ * replaced by the new entry.
+ * If the cache is full, the LRU (least recently used) entry is removed from
+ * the cache.
+ *
+ * @param key
+ * the key with which the specified value is to be associated.
+ * @param value
+ * a value to be associated with the specified key.
+ */
+ public synchronized void put(K key, V value) {
+ this.map.put(key, value);
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public synchronized void clear() {
+ this.map.clear();
+ }
+
+ /**
+ * Returns the number of used entries in the cache.
+ *
+ * @return the number of entries currently in the cache.
+ */
+ public synchronized int usedEntries() {
+ return this.map.size();
+ }
+
+ /**
+ * Returns a <code>Collection</code> that contains a copy of all cache
+ * entries.
+ *
+ * @return a <code>Collection</code> with a copy of the cache content.
+ */
+ public synchronized Collection<Map.Entry<K, V>> getAll() {
+ return new ArrayList<Map.Entry<K, V>>(this.map.entrySet());
+ }
+} // end class LRUCache
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/NormalRecalcControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/NormalRecalcControl.java
new file mode 100644
index 0000000..ba13e52
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/NormalRecalcControl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import java.io.IOException;
+
+
+/**
+ * Handles the normal vector updates when the terrain changes heights.
+ * @author bowens
+ */
+public class NormalRecalcControl extends AbstractControl {
+
+ private TerrainQuad terrain;
+
+ public NormalRecalcControl(){}
+
+ public NormalRecalcControl(TerrainQuad terrain) {
+ this.terrain = terrain;
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ terrain.updateNormals();
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ NormalRecalcControl control = new NormalRecalcControl(terrain);
+ control.setSpatial(spatial);
+ control.setEnabled(true);
+ return control;
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ super.setSpatial(spatial);
+ if (spatial instanceof TerrainQuad)
+ this.terrain = (TerrainQuad)spatial;
+ }
+
+ public TerrainQuad getTerrain() {
+ return terrain;
+ }
+
+ public void setTerrain(TerrainQuad terrain) {
+ this.terrain = terrain;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(terrain, "terrain", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ terrain = (TerrainQuad) ic.readSavable("terrain", null);
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
new file mode 100644
index 0000000..502702e
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.UpdateControl;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
+import com.jme3.terrain.heightmap.HeightMap;
+import com.jme3.terrain.heightmap.HeightMapGrid;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * TerrainGrid itself is an actual TerrainQuad. Its four children are the visible four tiles.
+ *
+ * The grid is indexed by cells. Each cell has an integer XZ coordinate originating at 0,0.
+ * TerrainGrid will piggyback on the TerrainLodControl so it can use the camera for its
+ * updates as well. It does this in the overwritten update() method.
+ *
+ * It uses an LRU (Least Recently Used) cache of 16 terrain tiles (full TerrainQuadTrees). The
+ * center 4 are the ones that are visible. As the camera moves, it checks what camera cell it is in
+ * and will attach the now visible tiles.
+ *
+ * The 'quadIndex' variable is a 4x4 array that represents the tiles. The center
+ * four (index numbers: 5, 6, 9, 10) are what is visible. Each quadIndex value is an
+ * offset vector. The vector contains whole numbers and represents how many tiles in offset
+ * this location is from the center of the map. So for example the index 11 [Vector3f(2, 0, 1)]
+ * is located 2*terrainSize in X axis and 1*terrainSize in Z axis.
+ *
+ * As the camera moves, it tests what cameraCell it is in. Each camera cell covers four quad tiles
+ * and is half way inside each one.
+ *
+ * +-------+-------+
+ * | 1 | 4 | Four terrainQuads that make up the grid
+ * | *..|..* | with the cameraCell in the middle, covering
+ * |----|--|--|----| all four quads.
+ * | *..|..* |
+ * | 2 | 3 |
+ * +-------+-------+
+ *
+ * This results in the effect of when the camera gets half way across one of the sides of a quad to
+ * an empty (non-loaded) area, it will trigger the system to load in the next tiles.
+ *
+ * The tile loading is done on a background thread, and once the tile is loaded, then it is
+ * attached to the qrid quad tree, back on the OGL thread. It will grab the terrain quad from
+ * the LRU cache if it exists. If it does not exist, it will load in the new TerrainQuad tile.
+ *
+ * The loading of new tiles triggers events for any TerrainGridListeners. The events are:
+ * -tile Attached
+ * -tile Detached
+ * -grid moved.
+ *
+ * These allow physics to update, and other operation (often needed for loading the terrain) to occur
+ * at the right time.
+ *
+ * @author Anthyon
+ */
+public class TerrainGrid extends TerrainQuad {
+
+ protected static final Logger log = Logger.getLogger(TerrainGrid.class.getCanonicalName());
+ protected Vector3f currentCamCell = Vector3f.ZERO;
+ protected int quarterSize; // half of quadSize
+ protected int quadSize;
+ protected HeightMapGrid heightMapGrid;
+ private TerrainGridTileLoader gridTileLoader;
+ protected Vector3f[] quadIndex;
+ protected Set<TerrainGridListener> listeners = new HashSet<TerrainGridListener>();
+ protected Material material;
+ protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
+ private int cellsLoaded = 0;
+ private int[] gridOffset;
+ private boolean runOnce = false;
+
+ protected class UpdateQuadCache implements Runnable {
+
+ protected final Vector3f location;
+
+ public UpdateQuadCache(Vector3f location) {
+ this.location = location;
+ }
+
+ /**
+ * This is executed if the camera has moved into a new CameraCell and will load in
+ * the new TerrainQuad tiles to be children of this TerrainGrid parent.
+ * It will first check the LRU cache to see if the terrain tile is already there,
+ * if it is not there, it will load it in and then cache that tile.
+ * The terrain tiles get added to the quad tree back on the OGL thread using the
+ * attachQuadAt() method. It also resets any cached values in TerrainQuad (such as
+ * neighbours).
+ */
+ public void run() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ int quadIdx = i * 4 + j;
+ final Vector3f quadCell = location.add(quadIndex[quadIdx]);
+ TerrainQuad q = cache.get(quadCell);
+ if (q == null) {
+ if (heightMapGrid != null) {
+ // create the new Quad since it doesn't exist
+ HeightMap heightMapAt = heightMapGrid.getHeightMapAt(quadCell);
+ q = new TerrainQuad(getName() + "Quad" + quadCell, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
+ q.setMaterial(material.clone());
+ log.log(Level.FINE, "Loaded TerrainQuad {0} from HeightMapGrid", q.getName());
+ } else if (gridTileLoader != null) {
+ q = gridTileLoader.getTerrainQuadAt(quadCell);
+ // only clone the material to the quad if it doesn't have a material of its own
+ if(q.getMaterial()==null) q.setMaterial(material.clone());
+ log.log(Level.FINE, "Loaded TerrainQuad {0} from TerrainQuadGrid", q.getName());
+ }
+ }
+ cache.put(quadCell, q);
+
+ if (isCenter(quadIdx)) {
+ // if it should be attached as a child right now, attach it
+ final int quadrant = getQuadrant(quadIdx);
+ final TerrainQuad newQuad = q;
+ // back on the OpenGL thread:
+ getControl(UpdateControl.class).enqueue(new Callable() {
+
+ public Object call() throws Exception {
+ attachQuadAt(newQuad, quadrant, quadCell);
+ //newQuad.resetCachedNeighbours();
+ return null;
+ }
+ });
+ }
+ }
+ }
+
+ }
+ }
+
+ protected boolean isCenter(int quadIndex) {
+ return quadIndex == 9 || quadIndex == 5 || quadIndex == 10 || quadIndex == 6;
+ }
+
+ protected int getQuadrant(int quadIndex) {
+ if (quadIndex == 5) {
+ return 1;
+ } else if (quadIndex == 9) {
+ return 2;
+ } else if (quadIndex == 6) {
+ return 3;
+ } else if (quadIndex == 10) {
+ return 4;
+ }
+ return 0; // error
+ }
+
+ public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, TerrainGridTileLoader terrainQuadGrid,
+ Vector2f offset, float offsetAmount) {
+ this.name = name;
+ this.patchSize = patchSize;
+ this.size = maxVisibleSize;
+ this.stepScale = scale;
+ this.offset = offset;
+ this.offsetAmount = offsetAmount;
+ initData();
+ this.gridTileLoader = terrainQuadGrid;
+ terrainQuadGrid.setPatchSize(this.patchSize);
+ terrainQuadGrid.setQuadSize(this.quadSize);
+ addControl(new UpdateControl());
+ }
+
+ public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, TerrainGridTileLoader terrainQuadGrid) {
+ this(name, patchSize, maxVisibleSize, scale, terrainQuadGrid, new Vector2f(), 0);
+ }
+
+ public TerrainGrid(String name, int patchSize, int maxVisibleSize, TerrainGridTileLoader terrainQuadGrid) {
+ this(name, patchSize, maxVisibleSize, Vector3f.UNIT_XYZ, terrainQuadGrid);
+ }
+
+ @Deprecated
+ public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid,
+ Vector2f offset, float offsetAmount) {
+ this.name = name;
+ this.patchSize = patchSize;
+ this.size = maxVisibleSize;
+ this.stepScale = scale;
+ this.offset = offset;
+ this.offsetAmount = offsetAmount;
+ initData();
+ this.heightMapGrid = heightMapGrid;
+ heightMapGrid.setSize(this.quadSize);
+ addControl(new UpdateControl());
+ }
+
+ @Deprecated
+ public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid) {
+ this(name, patchSize, maxVisibleSize, scale, heightMapGrid, new Vector2f(), 0);
+ }
+
+ @Deprecated
+ public TerrainGrid(String name, int patchSize, int maxVisibleSize, HeightMapGrid heightMapGrid) {
+ this(name, patchSize, maxVisibleSize, Vector3f.UNIT_XYZ, heightMapGrid);
+ }
+
+ public TerrainGrid() {
+ }
+
+ private void initData() {
+ int maxVisibleSize = size;
+ this.quarterSize = maxVisibleSize >> 2;
+ this.quadSize = (maxVisibleSize + 1) >> 1;
+ this.totalSize = maxVisibleSize;
+ this.gridOffset = new int[]{0, 0};
+
+ /*
+ * -z
+ * |
+ * 1|3
+ * -x ----+---- x
+ * 2|4
+ * |
+ * z
+ */
+ this.quadIndex = new Vector3f[]{
+ new Vector3f(-1, 0, -1), new Vector3f(0, 0, -1), new Vector3f(1, 0, -1), new Vector3f(2, 0, -1),
+ new Vector3f(-1, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 0, 0), new Vector3f(2, 0, 0),
+ new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1),
+ new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2)};
+
+ }
+
+ /**
+ * @deprecated not needed to be called any more, handled automatically
+ */
+ public void initialize(Vector3f location) {
+ if (this.material == null) {
+ throw new RuntimeException("Material must be set prior to call of initialize");
+ }
+ Vector3f camCell = this.getCamCell(location);
+ this.updateChildren(camCell);
+ for (TerrainGridListener l : this.listeners) {
+ l.gridMoved(camCell);
+ }
+ }
+
+ @Override
+ public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
+ // for now, only the first camera is handled.
+ // to accept more, there are two ways:
+ // 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed
+ // 2: grids are associated with locations, and no incremental update is done, we load new grids for new locations, and unload those that are not needed anymore
+ Vector3f cam = locations.isEmpty() ? Vector3f.ZERO.clone() : locations.get(0);
+ Vector3f camCell = this.getCamCell(cam); // get the grid index value of where the camera is (ie. 2,1)
+ if (cellsLoaded > 1) { // Check if cells are updated before updating gridoffset.
+ gridOffset[0] = Math.round(camCell.x * (size / 2));
+ gridOffset[1] = Math.round(camCell.z * (size / 2));
+ cellsLoaded = 0;
+ }
+ if (camCell.x != this.currentCamCell.x || camCell.z != currentCamCell.z || !runOnce) {
+ // if the camera has moved into a new cell, load new terrain into the visible 4 center quads
+ this.updateChildren(camCell);
+ for (TerrainGridListener l : this.listeners) {
+ l.gridMoved(camCell);
+ }
+ }
+ runOnce = true;
+ super.update(locations, lodCalculator);
+ }
+
+ public Vector3f getCamCell(Vector3f location) {
+ Vector3f tile = getTileCell(location);
+ Vector3f offsetHalf = new Vector3f(-0.5f, 0, -0.5f);
+ Vector3f shifted = tile.subtract(offsetHalf);
+ return new Vector3f(FastMath.floor(shifted.x), 0, FastMath.floor(shifted.z));
+ }
+
+ /**
+ * Centered at 0,0.
+ * Get the tile index location in integer form:
+ * @param location world coordinate
+ */
+ public Vector3f getTileCell(Vector3f location) {
+ Vector3f tileLoc = location.divide(this.getWorldScale().mult(this.quadSize));
+ return tileLoc;
+ }
+
+ public TerrainGridTileLoader getGridTileLoader() {
+ return gridTileLoader;
+ }
+
+ protected void removeQuad(int idx) {
+ if (this.getQuad(idx) != null) {
+ for (TerrainGridListener l : listeners) {
+ l.tileDetached(getTileCell(this.getQuad(idx).getWorldTranslation()), this.getQuad(idx));
+ }
+ this.detachChild(this.getQuad(idx));
+ cellsLoaded++; // For gridoffset calc., maybe the run() method is a better location for this.
+ }
+ }
+
+ /**
+ * Runs on the rendering thread
+ */
+ protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f quadCell) {
+ this.removeQuad(quadrant);
+
+ q.setQuadrant((short) quadrant);
+ this.attachChild(q);
+
+ Vector3f loc = quadCell.mult(this.quadSize - 1).subtract(quarterSize, 0, quarterSize);// quadrant location handled TerrainQuad automatically now
+ q.setLocalTranslation(loc);
+
+ for (TerrainGridListener l : listeners) {
+ l.tileAttached(quadCell, q);
+ }
+ updateModelBound();
+
+ for (Spatial s : getChildren()) {
+ if (s instanceof TerrainQuad) {
+ TerrainQuad tq = (TerrainQuad)s;
+ tq.resetCachedNeighbours();
+ tq.fixNormalEdges(new BoundingBox(tq.getWorldTranslation(), totalSize*2, Float.MAX_VALUE, totalSize*2));
+ }
+ }
+ }
+
+ @Deprecated
+ /**
+ * @Deprecated, use updateChildren
+ */
+ protected void updateChildrens(Vector3f camCell) {
+ updateChildren(camCell);
+ }
+
+ /**
+ * Called when the camera has moved into a new cell. We need to
+ * update what quads are in the scene now.
+ *
+ * Step 1: touch cache
+ * LRU cache is used, so elements that need to remain
+ * should be touched.
+ *
+ * Step 2: load new quads in background thread
+ * if the camera has moved into a new cell, we load in new quads
+ * @param camCell the cell the camera is in
+ */
+ protected void updateChildren(Vector3f camCell) {
+
+ int dx = 0;
+ int dy = 0;
+ if (currentCamCell != null) {
+ dx = (int) (camCell.x - currentCamCell.x);
+ dy = (int) (camCell.z - currentCamCell.z);
+ }
+
+ int xMin = 0;
+ int xMax = 4;
+ int yMin = 0;
+ int yMax = 4;
+ if (dx == -1) { // camera moved to -X direction
+ xMax = 3;
+ } else if (dx == 1) { // camera moved to +X direction
+ xMin = 1;
+ }
+
+ if (dy == -1) { // camera moved to -Y direction
+ yMax = 3;
+ } else if (dy == 1) { // camera moved to +Y direction
+ yMin = 1;
+ }
+
+ // Touch the items in the cache that we are and will be interested in.
+ // We activate cells in the direction we are moving. If we didn't move
+ // either way in one of the axes (say X or Y axis) then they are all touched.
+ for (int i = yMin; i < yMax; i++) {
+ for (int j = xMin; j < xMax; j++) {
+ cache.get(camCell.add(quadIndex[i * 4 + j]));
+ }
+ }
+ // ---------------------------------------------------
+ // ---------------------------------------------------
+
+ if (executor == null) {
+ // use the same executor as the LODControl
+ executor = createExecutorService();
+ }
+
+ executor.submit(new UpdateQuadCache(camCell));
+
+ this.currentCamCell = camCell;
+ }
+
+ public void addListener(TerrainGridListener listener) {
+ this.listeners.add(listener);
+ }
+
+ public Vector3f getCurrentCell() {
+ return this.currentCamCell;
+ }
+
+ public void removeListener(TerrainGridListener listener) {
+ this.listeners.remove(listener);
+ }
+
+ @Override
+ public void setMaterial(Material mat) {
+ this.material = mat;
+ super.setMaterial(mat);
+ }
+
+ public void setQuadSize(int quadSize) {
+ this.quadSize = quadSize;
+ }
+
+ @Override
+ public void adjustHeight(List<Vector2f> xz, List<Float> height) {
+ Vector3f currentGridLocation = getCurrentCell().mult(getLocalScale()).multLocal(quadSize - 1);
+ for (Vector2f vect : xz) {
+ vect.x -= currentGridLocation.x;
+ vect.y -= currentGridLocation.z;
+ }
+ super.adjustHeight(xz, height);
+ }
+
+ @Override
+ protected float getHeightmapHeight(int x, int z) {
+ return super.getHeightmapHeight(x - gridOffset[0], z - gridOffset[1]);
+ }
+
+ @Override
+ public int getNumMajorSubdivisions() {
+ return 2;
+ }
+
+ @Override
+ public Material getMaterial(Vector3f worldLocation) {
+ if (worldLocation == null)
+ return null;
+ Vector3f tileCell = getTileCell(worldLocation);
+ Terrain terrain = cache.get(tileCell);
+ if (terrain == null)
+ return null; // terrain not loaded for that cell yet!
+ return terrain.getMaterial(worldLocation);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule c = im.getCapsule(this);
+ name = c.readString("name", null);
+ size = c.readInt("size", 0);
+ patchSize = c.readInt("patchSize", 0);
+ stepScale = (Vector3f) c.readSavable("stepScale", null);
+ offset = (Vector2f) c.readSavable("offset", null);
+ offsetAmount = c.readFloat("offsetAmount", 0);
+ gridTileLoader = (TerrainGridTileLoader) c.readSavable("terrainQuadGrid", null);
+ material = (Material) c.readSavable("material", null);
+ initData();
+ if (gridTileLoader != null) {
+ gridTileLoader.setPatchSize(this.patchSize);
+ gridTileLoader.setQuadSize(this.quadSize);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule c = ex.getCapsule(this);
+ c.write(gridTileLoader, "terrainQuadGrid", null);
+ c.write(size, "size", 0);
+ c.write(patchSize, "patchSize", 0);
+ c.write(stepScale, "stepScale", null);
+ c.write(offset, "offset", null);
+ c.write(offsetAmount, "offsetAmount", 0);
+ c.write(material, "material", null);
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java
new file mode 100644
index 0000000..e972ee0
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.math.Vector3f;
+
+/**
+ *
+ * @author Anthyon
+ */
+public interface TerrainGridListener {
+
+ public void gridMoved(Vector3f newCenter);
+
+ public void tileAttached( Vector3f cell, TerrainQuad quad );
+
+ public void tileDetached( Vector3f cell, TerrainQuad quad );
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java
new file mode 100644
index 0000000..485f51f
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java
@@ -0,0 +1,21 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+
+/**
+ *
+ * @author normenhansen
+ */
+public interface TerrainGridTileLoader extends Savable {
+
+ public TerrainQuad getTerrainQuadAt(Vector3f location);
+
+ public void setPatchSize(int patchSize);
+
+ public void setQuadSize(int quadSize);
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
new file mode 100644
index 0000000..aedd744
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tells the terrain to update its Level of Detail.
+ * It needs the cameras to do this, and there could possibly
+ * be several cameras in the scene, so it accepts a list
+ * of cameras.
+ * NOTE: right now it just uses the first camera passed in,
+ * in the future it will use all of them to determine what
+ * LOD to set.
+ *
+ * This control serializes, but it does not save the Camera reference.
+ * This camera reference has to be manually added in when you load the
+ * terrain to the scene!
+ *
+ * @author Brent Owens
+ */
+public class TerrainLodControl extends AbstractControl {
+
+ private Terrain terrain;
+ private List<Camera> cameras;
+ private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();
+ private LodCalculator lodCalculator;
+ private boolean hasResetLod = false; // used when enabled is set to false
+
+ public TerrainLodControl() {
+ }
+
+ public TerrainLodControl(Terrain terrain, Camera camera) {
+ List<Camera> cams = new ArrayList<Camera>();
+ cams.add(camera);
+ this.terrain = terrain;
+ this.cameras = cams;
+ lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator
+ }
+
+ /**
+ * Only uses the first camera right now.
+ * @param terrain to act upon (must be a Spatial)
+ * @param cameras one or more cameras to reference for LOD calc
+ */
+ public TerrainLodControl(Terrain terrain, List<Camera> cameras) {
+ this.terrain = terrain;
+ this.cameras = cameras;
+ lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+
+ @Override
+ public void update(float tpf) {
+ controlUpdate(tpf);
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ //list of cameras for when terrain supports multiple cameras (ie split screen)
+
+ if (lodCalculator == null)
+ return;
+
+ if (!enabled) {
+ if (!hasResetLod) {
+ // this will get run once
+ hasResetLod = true;
+ lodCalculator.turnOffLod();
+ }
+ }
+
+ if (cameras != null) {
+ if (cameraLocations.isEmpty() && !cameras.isEmpty()) {
+ for (Camera c : cameras) // populate them
+ {
+ cameraLocations.add(c.getLocation());
+ }
+ }
+ terrain.update(cameraLocations, lodCalculator);
+ }
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ if (spatial instanceof Terrain) {
+ List<Camera> cameraClone = new ArrayList<Camera>();
+ if (cameras != null) {
+ for (Camera c : cameras) {
+ cameraClone.add(c);
+ }
+ }
+ TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameraClone);
+ cloned.setLodCalculator(lodCalculator.clone());
+ return cloned;
+ }
+ return null;
+ }
+
+ public void setCamera(Camera camera) {
+ List<Camera> cams = new ArrayList<Camera>();
+ cams.add(camera);
+ setCameras(cams);
+ }
+
+ public void setCameras(List<Camera> cameras) {
+ this.cameras = cameras;
+ cameraLocations.clear();
+ for (Camera c : cameras) {
+ cameraLocations.add(c.getLocation());
+ }
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ super.setSpatial(spatial);
+ if (spatial instanceof Terrain) {
+ this.terrain = (Terrain) spatial;
+ }
+ }
+
+ public void setTerrain(Terrain terrain) {
+ this.terrain = terrain;
+ }
+
+ public LodCalculator getLodCalculator() {
+ return lodCalculator;
+ }
+
+ public void setLodCalculator(LodCalculator lodCalculator) {
+ this.lodCalculator = lodCalculator;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (!enabled) {
+ // reset the lod levels to max detail for the terrain
+ hasResetLod = false;
+ } else {
+ hasResetLod = true;
+ lodCalculator.turnOnLod();
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write((Node)terrain, "terrain", null);
+ oc.write(lodCalculator, "lodCalculator", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ terrain = (Terrain) ic.readSavable("terrain", null);
+ lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
new file mode 100644
index 0000000..40cf190
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
@@ -0,0 +1,983 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.*;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;
+import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.HashMap;
+import java.util.List;
+
+
+/**
+ * A terrain patch is a leaf in the terrain quad tree. It has a mesh that can change levels of detail (LOD)
+ * whenever the view point, or camera, changes. The actual terrain mesh is created by the LODGeomap class.
+ * That uses a geo-mipmapping algorithm to change the index buffer of the mesh.
+ * The mesh is a triangle strip. In wireframe mode you might notice some strange lines, these are degenerate
+ * triangles generated by the geoMipMap algorithm and can be ignored. The video card removes them at almost no cost.
+ *
+ * Each patch needs to know its neighbour's LOD so it can seam its edges with them, in case the neighbour has a different
+ * LOD. If this doesn't happen, you will see gaps.
+ *
+ * The LOD value is most detailed at zero. It gets less detailed the higher the LOD value until you reach maxLod, which
+ * is a mathematical limit on the number of times the 'size' of the patch can be divided by two. However there is a -1 to that
+ * for now until I add in a custom index buffer calculation for that max level, the current algorithm does not go that far.
+ *
+ * You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change
+ * in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65,
+ * then the LOD changes every 130 units away.
+ *
+ * @author Brent Owens
+ */
+public class TerrainPatch extends Geometry {
+
+ protected LODGeomap geomap;
+ protected int lod = -1; // this terrain patch's LOD
+ private int maxLod = -1;
+ protected int previousLod = -1;
+ protected int lodLeft, lodTop, lodRight, lodBottom; // it's neighbour's LODs
+
+ protected int size;
+
+ protected int totalSize;
+
+ protected short quadrant = 1;
+
+ // x/z step
+ protected Vector3f stepScale;
+
+ // center of the patch in relation to (0,0,0)
+ protected Vector2f offset;
+
+ // amount the patch has been shifted.
+ protected float offsetAmount;
+
+ //protected LodCalculator lodCalculator;
+ //protected LodCalculatorFactory lodCalculatorFactory;
+
+ protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
+ protected boolean searchedForNeighboursAlready = false;
+
+
+ protected float[] lodEntropy;
+
+ public TerrainPatch() {
+ super("TerrainPatch");
+ }
+
+ public TerrainPatch(String name) {
+ super(name);
+ }
+
+ public TerrainPatch(String name, int size) {
+ this(name, size, new Vector3f(1,1,1), null, new Vector3f(0,0,0));
+ }
+
+ /**
+ * Constructor instantiates a new <code>TerrainPatch</code> object. The
+ * parameters and heightmap data are then processed to generate a
+ * <code>TriMesh</code> object for rendering.
+ *
+ * @param name
+ * the name of the terrain patch.
+ * @param size
+ * the size of the heightmap.
+ * @param stepScale
+ * the scale for the axes.
+ * @param heightMap
+ * the height data.
+ * @param origin
+ * the origin offset of the patch.
+ */
+ public TerrainPatch(String name, int size, Vector3f stepScale,
+ float[] heightMap, Vector3f origin) {
+ this(name, size, stepScale, heightMap, origin, size, new Vector2f(), 0);
+ }
+
+ /**
+ * Constructor instantiates a new <code>TerrainPatch</code> object. The
+ * parameters and heightmap data are then processed to generate a
+ * <code>TriMesh</code> object for renderering.
+ *
+ * @param name
+ * the name of the terrain patch.
+ * @param size
+ * the size of the patch.
+ * @param stepScale
+ * the scale for the axes.
+ * @param heightMap
+ * the height data.
+ * @param origin
+ * the origin offset of the patch.
+ * @param totalSize
+ * the total size of the terrain. (Higher if the patch is part of
+ * a <code>TerrainQuad</code> tree.
+ * @param offset
+ * the offset for texture coordinates.
+ * @param offsetAmount
+ * the total offset amount. Used for texture coordinates.
+ */
+ public TerrainPatch(String name, int size, Vector3f stepScale,
+ float[] heightMap, Vector3f origin, int totalSize,
+ Vector2f offset, float offsetAmount) {
+ super(name);
+ this.size = size;
+ this.stepScale = stepScale;
+ this.totalSize = totalSize;
+ this.offsetAmount = offsetAmount;
+ this.offset = offset;
+
+ setLocalTranslation(origin);
+
+ geomap = new LODGeomap(size, heightMap);
+ Mesh m = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
+ setMesh(m);
+
+ }
+
+ /**
+ * This calculation is slow, so don't use it often.
+ */
+ public void generateLodEntropies() {
+ float[] entropies = new float[getMaxLod()+1];
+ for (int i = 0; i <= getMaxLod(); i++){
+ int curLod = (int) Math.pow(2, i);
+ IntBuffer buf = geomap.writeIndexArrayLodDiff(null, curLod, false, false, false, false);
+ entropies[i] = EntropyComputeUtil.computeLodEntropy(mesh, buf);
+ }
+
+ lodEntropy = entropies;
+ }
+
+ public float[] getLodEntropies(){
+ if (lodEntropy == null){
+ generateLodEntropies();
+ }
+ return lodEntropy;
+ }
+
+ @Deprecated
+ public FloatBuffer getHeightmap() {
+ return BufferUtils.createFloatBuffer(geomap.getHeightArray());
+ }
+
+ public float[] getHeightMap() {
+ return geomap.getHeightArray();
+ }
+
+ /**
+ * The maximum lod supported by this terrain patch.
+ * If the patch size is 32 then the returned value would be log2(32)-2 = 3
+ * You can then use that value, 3, to see how many times you can divide 32 by 2
+ * before the terrain gets too un-detailed (can't stitch it any further).
+ * @return
+ */
+ public int getMaxLod() {
+ if (maxLod < 0)
+ maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
+
+ return maxLod;
+ }
+
+ protected void reIndexGeometry(HashMap<String,UpdatedTerrainPatch> updated, boolean useVariableLod) {
+
+ UpdatedTerrainPatch utp = updated.get(getName());
+
+ if (utp != null && (utp.isReIndexNeeded() || utp.isFixEdges()) ) {
+ int pow = (int) Math.pow(2, utp.getNewLod());
+ boolean left = utp.getLeftLod() > utp.getNewLod();
+ boolean top = utp.getTopLod() > utp.getNewLod();
+ boolean right = utp.getRightLod() > utp.getNewLod();
+ boolean bottom = utp.getBottomLod() > utp.getNewLod();
+
+ IntBuffer ib = null;
+ if (useVariableLod)
+ ib = geomap.writeIndexArrayLodVariable(null, pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()));
+ else
+ ib = geomap.writeIndexArrayLodDiff(null, pow, right, top, left, bottom);
+ utp.setNewIndexBuffer(ib);
+ }
+
+ }
+
+
+ public Vector2f getTex(float x, float z, Vector2f store) {
+ if (x < 0 || z < 0 || x >= size || z >= size) {
+ store.set(Vector2f.ZERO);
+ return store;
+ }
+ int idx = (int) (z * size + x);
+ return store.set(getMesh().getFloatBuffer(Type.TexCoord).get(idx*2),
+ getMesh().getFloatBuffer(Type.TexCoord).get(idx*2+1) );
+ }
+
+ public float getHeightmapHeight(float x, float z) {
+ if (x < 0 || z < 0 || x >= size || z >= size)
+ return 0;
+ int idx = (int) (z * size + x);
+ return getMesh().getFloatBuffer(Type.Position).get(idx*3+1); // 3 floats per entry (x,y,z), the +1 is to get the Y
+ }
+
+ /**
+ * Get the triangle of this geometry at the specified local coordinate.
+ * @param x local to the terrain patch
+ * @param z local to the terrain patch
+ * @return the triangle in world coordinates, or null if the point does intersect this patch on the XZ axis
+ */
+ public Triangle getTriangle(float x, float z) {
+ return geomap.getTriangleAtPoint(x, z, getWorldScale() , getWorldTranslation());
+ }
+
+ /**
+ * Get the triangles at the specified grid point. Probably only 2 triangles
+ * @param x local to the terrain patch
+ * @param z local to the terrain patch
+ * @return the triangles in world coordinates, or null if the point does intersect this patch on the XZ axis
+ */
+ public Triangle[] getGridTriangles(float x, float z) {
+ return geomap.getGridTrianglesAtPoint(x, z, getWorldScale() , getWorldTranslation());
+ }
+
+ protected void setHeight(List<LocationHeight> locationHeights, boolean overrideHeight) {
+
+ for (LocationHeight lh : locationHeights) {
+ if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size)
+ continue;
+ int idx = lh.z * size + lh.x;
+ if (overrideHeight) {
+ geomap.getHeightArray()[idx] = lh.h;
+ } else {
+ float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
+ geomap.getHeightArray()[idx] = h+lh.h;
+ }
+
+ }
+
+ FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false);
+ getMesh().clearBuffer(Type.Position);
+ getMesh().setBuffer(Type.Position, 3, newVertexBuffer);
+ }
+
+ /**
+ * recalculate all of the normal vectors in this terrain patch
+ */
+ protected void updateNormals() {
+ FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, getWorldScale());
+ getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);
+ FloatBuffer newTangentBuffer = null;
+ FloatBuffer newBinormalBuffer = null;
+ FloatBuffer[] tb = geomap.writeTangentArray(newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());
+ newTangentBuffer = tb[0];
+ newBinormalBuffer = tb[1];
+ getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);
+ getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer);
+ }
+
+ /**
+ * Matches the normals along the edge of the patch with the neighbours.
+ * Computes the normals for the right, bottom, left, and top edges of the
+ * patch, and saves those normals in the neighbour's edges too.
+ *
+ * Takes 4 points (if has neighbour on that side) for each
+ * point on the edge of the patch:
+ * *
+ * |
+ * *---x---*
+ * |
+ * *
+ * It works across the right side of the patch, from the top down to
+ * the bottom. Then it works on the bottom side of the patch, from the
+ * left to the right.
+ */
+ protected void fixNormalEdges(TerrainPatch right,
+ TerrainPatch bottom,
+ TerrainPatch top,
+ TerrainPatch left,
+ TerrainPatch bottomRight,
+ TerrainPatch bottomLeft,
+ TerrainPatch topRight,
+ TerrainPatch topLeft)
+ {
+ Vector3f rootPoint = new Vector3f();
+ Vector3f rightPoint = new Vector3f();
+ Vector3f leftPoint = new Vector3f();
+ Vector3f topPoint = new Vector3f();
+
+ Vector3f bottomPoint = new Vector3f();
+
+ Vector3f tangent = new Vector3f();
+ Vector3f binormal = new Vector3f();
+ Vector3f normal = new Vector3f();
+
+ int s = this.getSize()-1;
+
+ if (right != null) { // right side, works its way down
+ for (int i=0; i<s+1; i++) {
+ rootPoint.set(s, this.getHeightmapHeight(s,i), i);
+ leftPoint.set(s-1, this.getHeightmapHeight(s-1,i), i);
+ rightPoint.set(s+1, right.getHeightmapHeight(1,i), i);
+
+ if (i == 0) { // top point
+ if (top == null) {
+ bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);
+
+ averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);
+ } else {
+ topPoint.set(s, top.getHeightmapHeight(s,s-1), i-1);
+ bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);
+
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);
+
+ if (topRight != null) {
+ VertexBuffer topRightNB = topRight.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)topRightNB.getData(), (s+1)*s);
+ topRightNB.setUpdateNeeded();
+ }
+ }
+ } else if (i == s) { // bottom point
+ if (bottom == null) {
+ topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);
+
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(s));
+ } else {
+ topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);
+ bottomPoint.set(s, bottom.getHeightmapHeight(s,1), i+1);
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
+ VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
+
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s+1)-1);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*s);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), s);
+
+ if (bottomRight != null) {
+ VertexBuffer bottomRightNB = bottomRight.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)bottomRightNB.getData(), 0);
+ bottomRightNB.setUpdateNeeded();
+ }
+ downNB.setUpdateNeeded();
+ }
+ } else { // all in the middle
+ topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);
+ bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));
+ }
+ }
+ right.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
+ }
+
+ if (left != null) { // left side, works its way down
+ for (int i=0; i<s+1; i++) {
+ rootPoint.set(0, this.getHeightmapHeight(0,i), i);
+ leftPoint.set(-1, left.getHeightmapHeight(s-1,i), i);
+ rightPoint.set(1, this.getHeightmapHeight(1,i), i);
+
+ if (i == 0) { // top point
+ if (top == null) {
+ bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);
+ averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);
+ } else {
+ topPoint.set(0, top.getHeightmapHeight(0,s-1), i-1);
+ bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);
+
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);
+
+ if (topLeft != null) {
+ VertexBuffer topLeftNB = topLeft.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)topLeftNB.getData(), (s+1)*(s+1)-1);
+ topLeftNB.setUpdateNeeded();
+ }
+ }
+ } else if (i == s) { // bottom point
+ if (bottom == null) {
+ topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);
+
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s));
+ BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);
+ } else {
+ topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);
+ bottomPoint.set(0, bottom.getHeightmapHeight(0,1), i+1);
+
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
+ VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
+
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s));
+ BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);
+
+ if (bottomLeft != null) {
+ VertexBuffer bottomLeftNB = bottomLeft.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)bottomLeftNB.getData(), s);
+ bottomLeftNB.setUpdateNeeded();
+ }
+ downNB.setUpdateNeeded();
+ }
+ } else { // all in the middle
+ topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);
+ bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);
+
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i));
+ BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);
+ }
+ }
+ left.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
+ }
+
+ if (top != null) { // top side, works its way right
+ for (int i=0; i<s+1; i++) {
+ rootPoint.set(i, this.getHeightmapHeight(i,0), 0);
+ topPoint.set(i, top.getHeightmapHeight(i,s-1), -1);
+ bottomPoint.set(i, this.getHeightmapHeight(i,1), 1);
+
+ if (i == 0) { // left corner
+ // handled by left side pass
+
+ } else if (i == s) { // right corner
+
+ // handled by this patch when it does its right side
+
+ } else { // all in the middle
+ leftPoint.set(i-1, this.getHeightmapHeight(i-1,0), 0);
+ rightPoint.set(i+1, this.getHeightmapHeight(i+1,0), 0);
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), i);
+ VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s)+i);
+ }
+ }
+ top.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
+
+ }
+
+ if (bottom != null) { // bottom side, works its way right
+ for (int i=0; i<s+1; i++) {
+ rootPoint.set(i, this.getHeightmapHeight(i,s), s);
+ topPoint.set(i, this.getHeightmapHeight(i,s-1), s-1);
+ bottomPoint.set(i, bottom.getHeightmapHeight(i,1), s+1);
+
+ if (i == 0) { // left
+ // handled by the left side pass
+
+ } else if (i == s) { // right
+
+ // handled by this patch when it does its right side
+
+ } else { // all in the middle
+ leftPoint.set(i-1, this.getHeightmapHeight(i-1,s), s);
+ rightPoint.set(i+1, this.getHeightmapHeight(i+1,s), s);
+ averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);
+ VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)+i);
+ VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);
+ BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), i);
+ }
+ }
+ bottom.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
+
+ }
+
+ this.getMesh().getBuffer(Type.Normal).setUpdateNeeded();
+ this.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();
+ this.getMesh().getBuffer(Type.Binormal).setUpdateNeeded();
+ }
+
+ protected void averageNormalsTangents(
+ Vector3f topPoint,
+ Vector3f rootPoint,
+ Vector3f leftPoint,
+ Vector3f bottomPoint,
+ Vector3f rightPoint,
+ Vector2f topTex,
+ Vector2f rootTex,
+ Vector2f leftTex,
+ Vector2f bottomTex,
+ Vector2f rightTex,
+ Vector3f normal,
+ Vector3f tangent,
+ Vector3f binormal)
+ {
+ Vector3f scale = getWorldScale();
+
+ Vector3f n1 = Vector3f.ZERO;
+ if (topPoint != null && leftPoint != null) {
+ n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));
+ }
+ Vector3f n2 = Vector3f.ZERO;
+ if (leftPoint != null && bottomPoint != null) {
+ n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));
+ }
+ Vector3f n3 = Vector3f.ZERO;
+ if (rightPoint != null && bottomPoint != null) {
+ n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));
+ }
+ Vector3f n4 = Vector3f.ZERO;
+ if (rightPoint != null && topPoint != null) {
+ n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));
+ }
+
+ if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)
+ LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);
+
+ normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());
+ }
+
+ private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {
+ Vector3f normal = new Vector3f();
+ normal.set(firstPoint).subtractLocal(rootPoint)
+ .crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal();
+ return normal;
+ }
+
+ protected Vector3f getMeshNormal(int x, int z) {
+ if (x >= size || z >= size)
+ return null; // out of range
+
+ int index = (z*size+x)*3;
+ FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData();
+ Vector3f normal = new Vector3f();
+ normal.x = nb.get(index);
+ normal.y = nb.get(index+1);
+ normal.z = nb.get(index+2);
+ return normal;
+ }
+
+ /**
+ * Locks the mesh (sets it static) to improve performance.
+ * But it it not editable then. Set unlock to make it editable.
+ */
+ public void lockMesh() {
+ getMesh().setStatic();
+ }
+
+ /**
+ * Unlocks the mesh (sets it dynamic) to make it editable.
+ * It will be editable but performance will be reduced.
+ * Call lockMesh to improve performance.
+ */
+ public void unlockMesh() {
+ getMesh().setDynamic();
+ }
+
+ /**
+ * Returns the offset amount this terrain patch uses for textures.
+ *
+ * @return The current offset amount.
+ */
+ public float getOffsetAmount() {
+ return offsetAmount;
+ }
+
+ /**
+ * Returns the step scale that stretches the height map.
+ *
+ * @return The current step scale.
+ */
+ public Vector3f getStepScale() {
+ return stepScale;
+ }
+
+ /**
+ * Returns the total size of the terrain.
+ *
+ * @return The terrain's total size.
+ */
+ public int getTotalSize() {
+ return totalSize;
+ }
+
+ /**
+ * Returns the size of this terrain patch.
+ *
+ * @return The current patch size.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the current offset amount. This is used when building texture
+ * coordinates.
+ *
+ * @return The current offset amount.
+ */
+ public Vector2f getOffset() {
+ return offset;
+ }
+
+ /**
+ * Sets the value for the current offset amount to use when building texture
+ * coordinates. Note that this does <b>NOT </b> rebuild the terrain at all.
+ * This is mostly used for outside constructors of terrain patches.
+ *
+ * @param offset
+ * The new texture offset.
+ */
+ public void setOffset(Vector2f offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Sets the size of this terrain patch. Note that this does <b>NOT </b>
+ * rebuild the terrain at all. This is mostly used for outside constructors
+ * of terrain patches.
+ *
+ * @param size
+ * The new size.
+ */
+ public void setSize(int size) {
+ this.size = size;
+
+ maxLod = -1; // reset it
+ }
+
+ /**
+ * Sets the total size of the terrain . Note that this does <b>NOT </b>
+ * rebuild the terrain at all. This is mostly used for outside constructors
+ * of terrain patches.
+ *
+ * @param totalSize
+ * The new total size.
+ */
+ public void setTotalSize(int totalSize) {
+ this.totalSize = totalSize;
+ }
+
+ /**
+ * Sets the step scale of this terrain patch's height map. Note that this
+ * does <b>NOT </b> rebuild the terrain at all. This is mostly used for
+ * outside constructors of terrain patches.
+ *
+ * @param stepScale
+ * The new step scale.
+ */
+ public void setStepScale(Vector3f stepScale) {
+ this.stepScale = stepScale;
+ }
+
+ /**
+ * Sets the offset of this terrain texture map. Note that this does <b>NOT
+ * </b> rebuild the terrain at all. This is mostly used for outside
+ * constructors of terrain patches.
+ *
+ * @param offsetAmount
+ * The new texture offset.
+ */
+ public void setOffsetAmount(float offsetAmount) {
+ this.offsetAmount = offsetAmount;
+ }
+
+ /**
+ * @return Returns the quadrant.
+ */
+ public short getQuadrant() {
+ return quadrant;
+ }
+
+ /**
+ * @param quadrant
+ * The quadrant to set.
+ */
+ public void setQuadrant(short quadrant) {
+ this.quadrant = quadrant;
+ }
+
+ public int getLod() {
+ return lod;
+ }
+
+ public void setLod(int lod) {
+ this.lod = lod;
+ }
+
+ public int getPreviousLod() {
+ return previousLod;
+ }
+
+ public void setPreviousLod(int previousLod) {
+ this.previousLod = previousLod;
+ }
+
+ protected int getLodLeft() {
+ return lodLeft;
+ }
+
+ protected void setLodLeft(int lodLeft) {
+ this.lodLeft = lodLeft;
+ }
+
+ protected int getLodTop() {
+ return lodTop;
+ }
+
+ protected void setLodTop(int lodTop) {
+ this.lodTop = lodTop;
+ }
+
+ protected int getLodRight() {
+ return lodRight;
+ }
+
+ protected void setLodRight(int lodRight) {
+ this.lodRight = lodRight;
+ }
+
+ protected int getLodBottom() {
+ return lodBottom;
+ }
+
+ protected void setLodBottom(int lodBottom) {
+ this.lodBottom = lodBottom;
+ }
+
+ /*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
+ this.lodCalculatorFactory = lodCalculatorFactory;
+ setLodCalculator(lodCalculatorFactory.createCalculator(this));
+ }*/
+
+ @Override
+ public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
+ if (refreshFlags != 0)
+ throw new IllegalStateException("Scene graph must be updated" +
+ " before checking collision");
+
+ if (other instanceof BoundingVolume)
+ if (!getWorldBound().intersects((BoundingVolume)other))
+ return 0;
+
+ if(other instanceof Ray)
+ return collideWithRay((Ray)other, results);
+ else if (other instanceof BoundingVolume)
+ return collideWithBoundingVolume((BoundingVolume)other, results);
+ else {
+ throw new UnsupportedCollisionException("TerrainPatch cannnot collide with "+other.getClass().getName());
+ }
+ }
+
+
+ private int collideWithRay(Ray ray, CollisionResults results) {
+ // This should be handled in the root terrain quad
+ return 0;
+ }
+
+ private int collideWithBoundingVolume(BoundingVolume boundingVolume, CollisionResults results) {
+ if (boundingVolume instanceof BoundingBox)
+ return collideWithBoundingBox((BoundingBox)boundingVolume, results);
+ else if(boundingVolume instanceof BoundingSphere) {
+ BoundingSphere sphere = (BoundingSphere) boundingVolume;
+ BoundingBox bbox = new BoundingBox(boundingVolume.getCenter().clone(), sphere.getRadius(),
+ sphere.getRadius(),
+ sphere.getRadius());
+ return collideWithBoundingBox(bbox, results);
+ }
+ return 0;
+ }
+
+ protected Vector3f worldCoordinateToLocal(Vector3f loc) {
+ Vector3f translated = new Vector3f();
+ translated.x = loc.x/getWorldScale().x - getWorldTranslation().x;
+ translated.y = loc.y/getWorldScale().y - getWorldTranslation().y;
+ translated.z = loc.z/getWorldScale().z - getWorldTranslation().z;
+ return translated;
+ }
+
+ /**
+ * This most definitely is not optimized.
+ */
+ private int collideWithBoundingBox(BoundingBox bbox, CollisionResults results) {
+
+ // test the four corners, for cases where the bbox dimensions are less than the terrain grid size, which is probably most of the time
+ Vector3f topLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
+ Vector3f topRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
+ Vector3f bottomLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z+bbox.getZExtent()));
+ Vector3f bottomRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z+bbox.getZExtent()));
+
+ Triangle t = getTriangle(topLeft.x, topLeft.z);
+ if (t != null && bbox.collideWith(t, results) > 0)
+ return 1;
+ t = getTriangle(topRight.x, topRight.z);
+ if (t != null && bbox.collideWith(t, results) > 0)
+ return 1;
+ t = getTriangle(bottomLeft.x, bottomLeft.z);
+ if (t != null && bbox.collideWith(t, results) > 0)
+ return 1;
+ t = getTriangle(bottomRight.x, bottomRight.z);
+ if (t != null && bbox.collideWith(t, results) > 0)
+ return 1;
+
+ // box is larger than the points on the terrain, so test against the points
+ for (float z=topLeft.z; z<bottomLeft.z; z+=1) {
+ for (float x=topLeft.x; x<topRight.x; x+=1) {
+
+ if (x < 0 || z < 0 || x >= size || z >= size)
+ continue;
+ t = getTriangle(x,z);
+ if (t != null && bbox.collideWith(t, results) > 0)
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ // the mesh is removed, and reloaded when read() is called
+ // this reduces the save size to 10% by not saving the mesh
+ Mesh temp = getMesh();
+ mesh = null;
+
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(size, "size", 16);
+ oc.write(totalSize, "totalSize", 16);
+ oc.write(quadrant, "quadrant", (short)0);
+ oc.write(stepScale, "stepScale", Vector3f.UNIT_XYZ);
+ oc.write(offset, "offset", Vector3f.UNIT_XYZ);
+ oc.write(offsetAmount, "offsetAmount", 0);
+ //oc.write(lodCalculator, "lodCalculator", null);
+ //oc.write(lodCalculatorFactory, "lodCalculatorFactory", null);
+ oc.write(lodEntropy, "lodEntropy", null);
+ oc.write(geomap, "geomap", null);
+
+ setMesh(temp);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ size = ic.readInt("size", 16);
+ totalSize = ic.readInt("totalSize", 16);
+ quadrant = ic.readShort("quadrant", (short)0);
+ stepScale = (Vector3f) ic.readSavable("stepScale", Vector3f.UNIT_XYZ);
+ offset = (Vector2f) ic.readSavable("offset", Vector3f.UNIT_XYZ);
+ offsetAmount = ic.readFloat("offsetAmount", 0);
+ //lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
+ //lodCalculator.setTerrainPatch(this);
+ //lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null);
+ lodEntropy = ic.readFloatArray("lodEntropy", null);
+ geomap = (LODGeomap) ic.readSavable("geomap", null);
+
+ Mesh regen = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
+ setMesh(regen);
+ //TangentBinormalGenerator.generate(this); // note that this will be removed
+ ensurePositiveVolumeBBox();
+ }
+
+ @Override
+ public TerrainPatch clone() {
+ TerrainPatch clone = new TerrainPatch();
+ clone.name = name.toString();
+ clone.size = size;
+ clone.totalSize = totalSize;
+ clone.quadrant = quadrant;
+ clone.stepScale = stepScale.clone();
+ clone.offset = offset.clone();
+ clone.offsetAmount = offsetAmount;
+ //clone.lodCalculator = lodCalculator.clone();
+ //clone.lodCalculator.setTerrainPatch(clone);
+ //clone.setLodCalculator(lodCalculatorFactory.clone());
+ clone.geomap = new LODGeomap(size, geomap.getHeightArray());
+ clone.setLocalTranslation(getLocalTranslation().clone());
+ Mesh m = clone.geomap.createMesh(clone.stepScale, Vector2f.UNIT_XY, clone.offset, clone.offsetAmount, clone.totalSize, false);
+ clone.setMesh(m);
+ clone.setMaterial(material.clone());
+ return clone;
+ }
+
+ protected void ensurePositiveVolumeBBox() {
+ if (getModelBound() instanceof BoundingBox) {
+ if (((BoundingBox)getModelBound()).getYExtent() < 0.001f) {
+ // a correction so the box always has a volume
+ ((BoundingBox)getModelBound()).setYExtent(0.001f);
+ updateWorldBound();
+ }
+ }
+ }
+
+
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
new file mode 100644
index 0000000..0b9b218
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
@@ -0,0 +1,1862 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireBox;
+import com.jme3.terrain.ProgressMonitor;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
+import com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker;
+import com.jme3.terrain.geomipmap.picking.TerrainPickData;
+import com.jme3.terrain.geomipmap.picking.TerrainPicker;
+import com.jme3.util.TangentBinormalGenerator;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A terrain quad is a node in the quad tree of the terrain system.
+ * The root terrain quad will be the only one that receives the update() call every frame
+ * and it will determine if there has been any LOD change.
+ *
+ * The leaves of the terrain quad tree are Terrain Patches. These have the real geometry mesh.
+ *
+ *
+ * Heightmap coordinates start from the bottom left of the world and work towards the
+ * top right.
+ *
+ * +x
+ * ^
+ * | ......N = length of heightmap
+ * | : :
+ * | : :
+ * | 0.....:
+ * +---------> +z
+ * (world coordinates)
+ *
+ * @author Brent Owens
+ */
+public class TerrainQuad extends Node implements Terrain {
+
+ protected Vector2f offset;
+
+ protected int totalSize; // the size of this entire terrain tree (on one side)
+
+ protected int size; // size of this quad, can be between totalSize and patchSize
+
+ protected int patchSize; // size of the individual patches
+
+ protected Vector3f stepScale;
+
+ protected float offsetAmount;
+
+ protected int quadrant = 0; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
+
+ //protected LodCalculatorFactory lodCalculatorFactory;
+ //protected LodCalculator lodCalculator;
+
+ protected List<Vector3f> lastCameraLocations; // used for LOD calc
+ private boolean lodCalcRunning = false;
+ private int lodOffCount = 0;
+ private int maxLod = -1;
+ private HashMap<String,UpdatedTerrainPatch> updatedPatches;
+ private final Object updatePatchesLock = new Object();
+ private BoundingBox affectedAreaBBox; // only set in the root quad
+
+ private TerrainPicker picker;
+ private Vector3f lastScale = Vector3f.UNIT_XYZ;
+
+ protected ExecutorService executor;
+
+ protected ExecutorService createExecutorService() {
+ return Executors.newSingleThreadExecutor(new ThreadFactory() {
+ public Thread newThread(Runnable r) {
+ Thread th = new Thread(r);
+ th.setName("jME Terrain Thread");
+ th.setDaemon(true);
+ return th;
+ }
+ });
+ }
+
+ public TerrainQuad() {
+ super("Terrain");
+ }
+
+ /**
+ *
+ * @param name the name of the scene element. This is required for
+ * identification and comparison purposes.
+ * @param patchSize size of the individual patches
+ * @param totalSize the size of this entire terrain tree (on one side)
+ * @param heightMap The height map to generate the terrain from (a flat
+ * height map will be generated if this is null)
+ */
+ public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
+ this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);
+ }
+
+ /**
+ *
+ * @param name the name of the scene element. This is required for
+ * identification and comparison purposes.
+ * @param patchSize size of the individual patches
+ * @param quadSize
+ * @param totalSize the size of this entire terrain tree (on one side)
+ * @param heightMap The height map to generate the terrain from (a flat
+ * height map will be generated if this is null)
+ */
+ public TerrainQuad(String name, int patchSize, int quadSize, int totalSize, float[] heightMap) {
+ this(name, patchSize, totalSize, quadSize, Vector3f.UNIT_XYZ, heightMap);
+ }
+
+ /**
+ *
+ * @param name the name of the scene element. This is required for
+ * identification and comparison purposes.
+ * @param patchSize size of the individual patches
+ * @param size size of this quad, can be between totalSize and patchSize
+ * @param scale
+ * @param heightMap The height map to generate the terrain from (a flat
+ * height map will be generated if this is null)
+ */
+ public TerrainQuad(String name, int patchSize, int size, Vector3f scale, float[] heightMap) {
+ this(name, patchSize, size, scale, heightMap, size, new Vector2f(), 0);
+ affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
+ fixNormalEdges(affectedAreaBBox);
+ addControl(new NormalRecalcControl(this));
+ }
+
+ /**
+ *
+ * @param name the name of the scene element. This is required for
+ * identification and comparison purposes.
+ * @param patchSize size of the individual patches
+ * @param totalSize the size of this entire terrain tree (on one side)
+ * @param quadSize
+ * @param scale
+ * @param heightMap The height map to generate the terrain from (a flat
+ * height map will be generated if this is null)
+ */
+ public TerrainQuad(String name, int patchSize, int totalSize, int quadSize, Vector3f scale, float[] heightMap) {
+ this(name, patchSize, quadSize, scale, heightMap, totalSize, new Vector2f(), 0);
+ affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);
+ fixNormalEdges(affectedAreaBBox);
+ addControl(new NormalRecalcControl(this));
+ }
+
+ protected TerrainQuad(String name, int patchSize, int quadSize,
+ Vector3f scale, float[] heightMap, int totalSize,
+ Vector2f offset, float offsetAmount)
+ {
+ super(name);
+
+ if (heightMap == null)
+ heightMap = generateDefaultHeightMap(quadSize);
+
+ if (!FastMath.isPowerOfTwo(quadSize - 1)) {
+ throw new RuntimeException("size given: " + quadSize + " Terrain quad sizes may only be (2^N + 1)");
+ }
+ if (FastMath.sqrt(heightMap.length) > quadSize) {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Heightmap size is larger than the terrain size. Make sure your heightmap image is the same size as the terrain!");
+ }
+
+ this.offset = offset;
+ this.offsetAmount = offsetAmount;
+ this.totalSize = totalSize;
+ this.size = quadSize;
+ this.patchSize = patchSize;
+ this.stepScale = scale;
+ //this.lodCalculatorFactory = lodCalculatorFactory;
+ //this.lodCalculator = lodCalculator;
+ split(patchSize, heightMap);
+ }
+
+ /*public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).setLodCalculatorFactory(lodCalculatorFactory);
+ } else if (child instanceof TerrainPatch) {
+ ((TerrainPatch) child).setLodCalculator(lodCalculatorFactory.createCalculator((TerrainPatch) child));
+ }
+ }
+ }
+ }*/
+
+
+ /**
+ * Create just a flat heightmap
+ */
+ private float[] generateDefaultHeightMap(int size) {
+ float[] heightMap = new float[size*size];
+ return heightMap;
+ }
+
+ /**
+ * Call from the update() method of a terrain controller to update
+ * the LOD values of each patch.
+ * This will perform the geometry calculation in a background thread and
+ * do the actual update on the opengl thread.
+ */
+ public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
+ updateLOD(locations, lodCalculator);
+ }
+
+ /**
+ * update the normals if there were any height changes recently.
+ * Should only be called on the root quad
+ */
+ protected void updateNormals() {
+
+ if (needToRecalculateNormals()) {
+ //TODO background-thread this if it ends up being expensive
+ fixNormals(affectedAreaBBox); // the affected patches
+ fixNormalEdges(affectedAreaBBox); // the edges between the patches
+
+ setNormalRecalcNeeded(null); // set to false
+ }
+ }
+
+ // do all of the LOD calculations
+ protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
+ // update any existing ones that need updating
+ updateQuadLODs();
+
+ if (lodCalculator.isLodOff()) {
+ // we want to calculate the base lod at least once
+ if (lodOffCount == 1)
+ return;
+ else
+ lodOffCount++;
+ } else
+ lodOffCount = 0;
+
+ if (lastCameraLocations != null) {
+ if (lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())
+ return; // don't update if in same spot
+ else
+ lastCameraLocations = cloneVectorList(locations);
+ }
+ else {
+ lastCameraLocations = cloneVectorList(locations);
+ return;
+ }
+
+ if (isLodCalcRunning()) {
+ return;
+ }
+
+ if (getParent() instanceof TerrainQuad) {
+ return; // we just want the root quad to perform this.
+ }
+
+ if (executor == null)
+ executor = createExecutorService();
+
+ UpdateLOD updateLodThread = new UpdateLOD(locations, lodCalculator);
+ executor.execute(updateLodThread);
+ }
+
+ private synchronized boolean isLodCalcRunning() {
+ return lodCalcRunning;
+ }
+
+ private synchronized void setLodCalcRunning(boolean running) {
+ lodCalcRunning = running;
+ }
+
+ private List<Vector3f> cloneVectorList(List<Vector3f> locations) {
+ List<Vector3f> cloned = new ArrayList<Vector3f>();
+ for(Vector3f l : locations)
+ cloned.add(l.clone());
+ return cloned;
+ }
+
+ private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {
+ boolean theSame = true;
+ for (Vector3f l : locations) {
+ for (Vector3f v : lastCameraLocations) {
+ if (!v.equals(l) ) {
+ theSame = false;
+ return false;
+ }
+ }
+ }
+ return theSame;
+ }
+
+ private int collideWithRay(Ray ray, CollisionResults results) {
+ if (picker == null)
+ picker = new BresenhamTerrainPicker(this);
+
+ Vector3f intersection = picker.getTerrainIntersection(ray, results);
+ if (intersection != null)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Generate the entropy values for the terrain for the "perspective" LOD
+ * calculator. This routine can take a long time to run!
+ * @param progressMonitor optional
+ */
+ public void generateEntropy(ProgressMonitor progressMonitor) {
+ // only check this on the root quad
+ if (isRootQuad())
+ if (progressMonitor != null) {
+ int numCalc = (totalSize-1)/(patchSize-1); // make it an even number
+ progressMonitor.setMonitorMax(numCalc*numCalc);
+ }
+
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).generateEntropy(progressMonitor);
+ } else if (child instanceof TerrainPatch) {
+ ((TerrainPatch) child).generateLodEntropies();
+ if (progressMonitor != null)
+ progressMonitor.incrementProgress(1);
+ }
+ }
+ }
+
+ // only do this on the root quad
+ if (isRootQuad())
+ if (progressMonitor != null)
+ progressMonitor.progressComplete();
+ }
+
+ protected boolean isRootQuad() {
+ return (getParent() != null && !(getParent() instanceof TerrainQuad) );
+ }
+
+ public Material getMaterial() {
+ return getMaterial(null);
+ }
+
+ public Material getMaterial(Vector3f worldLocation) {
+ // get the material from one of the children. They all share the same material
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ return ((TerrainQuad)child).getMaterial(worldLocation);
+ } else if (child instanceof TerrainPatch) {
+ return ((TerrainPatch)child).getMaterial();
+ }
+ }
+ }
+ return null;
+ }
+
+ //public float getTextureCoordinateScale() {
+ // return 1f/(float)totalSize;
+ //}
+ public int getNumMajorSubdivisions() {
+ return 1;
+ }
+
+ /**
+ * Calculates the LOD of all child terrain patches.
+ */
+ private class UpdateLOD implements Runnable {
+ private List<Vector3f> camLocations;
+ private LodCalculator lodCalculator;
+
+ UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
+ this.camLocations = camLocations;
+ this.lodCalculator = lodCalculator;
+ }
+
+ public void run() {
+ long start = System.currentTimeMillis();
+ if (isLodCalcRunning()) {
+ //System.out.println("thread already running");
+ return;
+ }
+ //System.out.println("spawned thread "+toString());
+ setLodCalcRunning(true);
+
+ // go through each patch and calculate its LOD based on camera distance
+ HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
+ boolean lodChanged = calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
+
+ if (!lodChanged) {
+ // not worth updating anything else since no one's LOD changed
+ setLodCalcRunning(false);
+ return;
+ }
+ // then calculate its neighbour LOD values for seaming in the shader
+ findNeighboursLod(updated);
+
+ fixEdges(updated); // 'updated' can get added to here
+
+ reIndexPages(updated, lodCalculator.usesVariableLod());
+
+ setUpdateQuadLODs(updated); // set back to main ogl thread
+
+ setLodCalcRunning(false);
+ //double duration = (System.currentTimeMillis()-start);
+ //System.out.println("terminated in "+duration);
+ }
+ }
+
+ private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {
+ synchronized (updatePatchesLock) {
+ updatedPatches = updated;
+ }
+ }
+
+ /**
+ * Back on the ogl thread: update the terrain patch geometries
+ * @param updatedPatches to be updated
+ */
+ private void updateQuadLODs() {
+ synchronized (updatePatchesLock) {
+
+ if (updatedPatches == null || updatedPatches.isEmpty())
+ return;
+
+ // do the actual geometry update here
+ for (UpdatedTerrainPatch utp : updatedPatches.values()) {
+ utp.updateAll();
+ }
+
+ updatedPatches.clear();
+ }
+ }
+
+ public boolean hasPatchesToUpdate() {
+ return updatedPatches != null && !updatedPatches.isEmpty();
+ }
+
+ protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {
+
+ boolean lodChanged = false;
+
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ boolean b = ((TerrainQuad) child).calculateLod(location, updates, lodCalculator);
+ if (b)
+ lodChanged = true;
+ } else if (child instanceof TerrainPatch) {
+ boolean b = lodCalculator.calculateLod((TerrainPatch) child, location, updates);
+ if (b)
+ lodChanged = true;
+ }
+ }
+ }
+
+ return lodChanged;
+ }
+
+ protected synchronized void findNeighboursLod(HashMap<String,UpdatedTerrainPatch> updated) {
+ if (children != null) {
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).findNeighboursLod(updated);
+ } else if (child instanceof TerrainPatch) {
+
+ TerrainPatch patch = (TerrainPatch) child;
+ if (!patch.searchedForNeighboursAlready) {
+ // set the references to the neighbours
+ patch.rightNeighbour = findRightPatch(patch);
+ patch.bottomNeighbour = findDownPatch(patch);
+ patch.leftNeighbour = findLeftPatch(patch);
+ patch.topNeighbour = findTopPatch(patch);
+ patch.searchedForNeighboursAlready = true;
+ }
+ TerrainPatch right = patch.rightNeighbour;
+ TerrainPatch down = patch.bottomNeighbour;
+
+ UpdatedTerrainPatch utp = updated.get(patch.getName());
+ if (utp == null) {
+ utp = new UpdatedTerrainPatch(patch, patch.lod);
+ updated.put(utp.getName(), utp);
+ }
+
+ if (right != null) {
+ UpdatedTerrainPatch utpR = updated.get(right.getName());
+ if (utpR == null) {
+ utpR = new UpdatedTerrainPatch(right, right.lod);
+ updated.put(utpR.getName(), utpR);
+ }
+
+ utp.setRightLod(utpR.getNewLod());
+ utpR.setLeftLod(utp.getNewLod());
+ }
+ if (down != null) {
+ UpdatedTerrainPatch utpD = updated.get(down.getName());
+ if (utpD == null) {
+ utpD = new UpdatedTerrainPatch(down, down.lod);
+ updated.put(utpD.getName(), utpD);
+ }
+
+ utp.setBottomLod(utpD.getNewLod());
+ utpD.setTopLod(utp.getNewLod());
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * TerrainQuad caches neighbours for faster LOD checks.
+ * Sometimes you might want to reset this cache (for instance in TerrainGrid)
+ */
+ protected void resetCachedNeighbours() {
+ if (children != null) {
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).resetCachedNeighbours();
+ } else if (child instanceof TerrainPatch) {
+ TerrainPatch patch = (TerrainPatch) child;
+ patch.searchedForNeighboursAlready = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Find any neighbours that should have their edges seamed because another neighbour
+ * changed its LOD to a greater value (less detailed)
+ */
+ protected synchronized void fixEdges(HashMap<String,UpdatedTerrainPatch> updated) {
+ if (children != null) {
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).fixEdges(updated);
+ } else if (child instanceof TerrainPatch) {
+ TerrainPatch patch = (TerrainPatch) child;
+ UpdatedTerrainPatch utp = updated.get(patch.getName());
+
+ if(utp != null && utp.lodChanged()) {
+ if (!patch.searchedForNeighboursAlready) {
+ // set the references to the neighbours
+ patch.rightNeighbour = findRightPatch(patch);
+ patch.bottomNeighbour = findDownPatch(patch);
+ patch.leftNeighbour = findLeftPatch(patch);
+ patch.topNeighbour = findTopPatch(patch);
+ patch.searchedForNeighboursAlready = true;
+ }
+ TerrainPatch right = patch.rightNeighbour;
+ TerrainPatch down = patch.bottomNeighbour;
+ TerrainPatch top = patch.topNeighbour;
+ TerrainPatch left = patch.leftNeighbour;
+ if (right != null) {
+ UpdatedTerrainPatch utpR = updated.get(right.getName());
+ if (utpR == null) {
+ utpR = new UpdatedTerrainPatch(right, right.lod);
+ updated.put(utpR.getName(), utpR);
+ }
+ utpR.setFixEdges(true);
+ }
+ if (down != null) {
+ UpdatedTerrainPatch utpD = updated.get(down.getName());
+ if (utpD == null) {
+ utpD = new UpdatedTerrainPatch(down, down.lod);
+ updated.put(utpD.getName(), utpD);
+ }
+ utpD.setFixEdges(true);
+ }
+ if (top != null){
+ UpdatedTerrainPatch utpT = updated.get(top.getName());
+ if (utpT == null) {
+ utpT = new UpdatedTerrainPatch(top, top.lod);
+ updated.put(utpT.getName(), utpT);
+ }
+ utpT.setFixEdges(true);
+ }
+ if (left != null){
+ UpdatedTerrainPatch utpL = updated.get(left.getName());
+ if (utpL == null) {
+ utpL = new UpdatedTerrainPatch(left, left.lod);
+ updated.put(utpL.getName(), utpL);
+ }
+ utpL.setFixEdges(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected synchronized void reIndexPages(HashMap<String,UpdatedTerrainPatch> updated, boolean usesVariableLod) {
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).reIndexPages(updated, usesVariableLod);
+ } else if (child instanceof TerrainPatch) {
+ ((TerrainPatch) child).reIndexGeometry(updated, usesVariableLod);
+ }
+ }
+ }
+ }
+
+ /**
+ * <code>split</code> divides the heightmap data for four children. The
+ * children are either quads or patches. This is dependent on the size of the
+ * children. If the child's size is less than or equal to the set block
+ * size, then patches are created, otherwise, quads are created.
+ *
+ * @param blockSize
+ * the blocks size to test against.
+ * @param heightMap
+ * the height data.
+ */
+ protected void split(int blockSize, float[] heightMap) {
+ if ((size >> 1) + 1 <= blockSize) {
+ createQuadPatch(heightMap);
+ } else {
+ createQuad(blockSize, heightMap);
+ }
+
+ }
+
+ /**
+ * Quadrants, world coordinates, and heightmap coordinates (Y-up):
+ *
+ * -z
+ * -u |
+ * -v 1|3
+ * -x ----+---- x
+ * 2|4 u
+ * | v
+ * z
+ * <code>createQuad</code> generates four new quads from this quad.
+ * The heightmap's top left (0,0) coordinate is at the bottom, -x,-z
+ * coordinate of the terrain, so it grows in the positive x.z direction.
+ */
+ protected void createQuad(int blockSize, float[] heightMap) {
+ // create 4 terrain quads
+ int quarterSize = size >> 2;
+
+ int split = (size + 1) >> 1;
+
+ Vector2f tempOffset = new Vector2f();
+ offsetAmount += quarterSize;
+
+ //if (lodCalculator == null)
+ // lodCalculator = createDefaultLodCalculator(); // set a default one
+
+ // 1 upper left of heightmap, upper left quad
+ float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
+
+ Vector3f origin1 = new Vector3f(-quarterSize * stepScale.x, 0,
+ -quarterSize * stepScale.z);
+
+ tempOffset.x = offset.x;
+ tempOffset.y = offset.y;
+ tempOffset.x += origin1.x;
+ tempOffset.y += origin1.z;
+
+ TerrainQuad quad1 = new TerrainQuad(getName() + "Quad1", blockSize,
+ split, stepScale, heightBlock1, totalSize, tempOffset,
+ offsetAmount);
+ quad1.setLocalTranslation(origin1);
+ quad1.quadrant = 1;
+ this.attachChild(quad1);
+
+ // 2 lower left of heightmap, lower left quad
+ float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
+ split);
+
+ Vector3f origin2 = new Vector3f(-quarterSize * stepScale.x, 0,
+ quarterSize * stepScale.z);
+
+ tempOffset = new Vector2f();
+ tempOffset.x = offset.x;
+ tempOffset.y = offset.y;
+ tempOffset.x += origin2.x;
+ tempOffset.y += origin2.z;
+
+ TerrainQuad quad2 = new TerrainQuad(getName() + "Quad2", blockSize,
+ split, stepScale, heightBlock2, totalSize, tempOffset,
+ offsetAmount);
+ quad2.setLocalTranslation(origin2);
+ quad2.quadrant = 2;
+ this.attachChild(quad2);
+
+ // 3 upper right of heightmap, upper right quad
+ float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
+ split);
+
+ Vector3f origin3 = new Vector3f(quarterSize * stepScale.x, 0,
+ -quarterSize * stepScale.z);
+
+ tempOffset = new Vector2f();
+ tempOffset.x = offset.x;
+ tempOffset.y = offset.y;
+ tempOffset.x += origin3.x;
+ tempOffset.y += origin3.z;
+
+ TerrainQuad quad3 = new TerrainQuad(getName() + "Quad3", blockSize,
+ split, stepScale, heightBlock3, totalSize, tempOffset,
+ offsetAmount);
+ quad3.setLocalTranslation(origin3);
+ quad3.quadrant = 3;
+ this.attachChild(quad3);
+
+ // 4 lower right of heightmap, lower right quad
+ float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
+ split - 1, split);
+
+ Vector3f origin4 = new Vector3f(quarterSize * stepScale.x, 0,
+ quarterSize * stepScale.z);
+
+ tempOffset = new Vector2f();
+ tempOffset.x = offset.x;
+ tempOffset.y = offset.y;
+ tempOffset.x += origin4.x;
+ tempOffset.y += origin4.z;
+
+ TerrainQuad quad4 = new TerrainQuad(getName() + "Quad4", blockSize,
+ split, stepScale, heightBlock4, totalSize, tempOffset,
+ offsetAmount);
+ quad4.setLocalTranslation(origin4);
+ quad4.quadrant = 4;
+ this.attachChild(quad4);
+
+ }
+
+ public void generateDebugTangents(Material mat) {
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad)child).generateDebugTangents(mat);
+ } else if (child instanceof TerrainPatch) {
+ Geometry debug = new Geometry( "Debug " + name,
+ TangentBinormalGenerator.genTbnLines( ((TerrainPatch)child).getMesh(), 0.8f));
+ attachChild(debug);
+ debug.setLocalTranslation(child.getLocalTranslation());
+ debug.setCullHint(CullHint.Never);
+ debug.setMaterial(mat);
+ }
+ }
+ }
+
+ /**
+ * <code>createQuadPatch</code> creates four child patches from this quad.
+ */
+ protected void createQuadPatch(float[] heightMap) {
+ // create 4 terrain patches
+ int quarterSize = size >> 2;
+ int halfSize = size >> 1;
+ int split = (size + 1) >> 1;
+
+ //if (lodCalculator == null)
+ // lodCalculator = createDefaultLodCalculator(); // set a default one
+
+ offsetAmount += quarterSize;
+
+ // 1 lower left
+ float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
+
+ Vector3f origin1 = new Vector3f(-halfSize * stepScale.x, 0, -halfSize
+ * stepScale.z);
+
+ Vector2f tempOffset1 = new Vector2f();
+ tempOffset1.x = offset.x;
+ tempOffset1.y = offset.y;
+ tempOffset1.x += origin1.x / 2;
+ tempOffset1.y += origin1.z / 2;
+
+ TerrainPatch patch1 = new TerrainPatch(getName() + "Patch1", split,
+ stepScale, heightBlock1, origin1, totalSize, tempOffset1,
+ offsetAmount);
+ patch1.setQuadrant((short) 1);
+ this.attachChild(patch1);
+ patch1.setModelBound(new BoundingBox());
+ patch1.updateModelBound();
+ //patch1.setLodCalculator(lodCalculator);
+ //TangentBinormalGenerator.generate(patch1);
+
+ // 2 upper left
+ float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
+ split);
+
+ Vector3f origin2 = new Vector3f(-halfSize * stepScale.x, 0, 0);
+
+ Vector2f tempOffset2 = new Vector2f();
+ tempOffset2.x = offset.x;
+ tempOffset2.y = offset.y;
+ tempOffset2.x += origin1.x / 2;
+ tempOffset2.y += quarterSize * stepScale.z;
+
+ TerrainPatch patch2 = new TerrainPatch(getName() + "Patch2", split,
+ stepScale, heightBlock2, origin2, totalSize, tempOffset2,
+ offsetAmount);
+ patch2.setQuadrant((short) 2);
+ this.attachChild(patch2);
+ patch2.setModelBound(new BoundingBox());
+ patch2.updateModelBound();
+ //patch2.setLodCalculator(lodCalculator);
+ //TangentBinormalGenerator.generate(patch2);
+
+ // 3 lower right
+ float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
+ split);
+
+ Vector3f origin3 = new Vector3f(0, 0, -halfSize * stepScale.z);
+
+ Vector2f tempOffset3 = new Vector2f();
+ tempOffset3.x = offset.x;
+ tempOffset3.y = offset.y;
+ tempOffset3.x += quarterSize * stepScale.x;
+ tempOffset3.y += origin3.z / 2;
+
+ TerrainPatch patch3 = new TerrainPatch(getName() + "Patch3", split,
+ stepScale, heightBlock3, origin3, totalSize, tempOffset3,
+ offsetAmount);
+ patch3.setQuadrant((short) 3);
+ this.attachChild(patch3);
+ patch3.setModelBound(new BoundingBox());
+ patch3.updateModelBound();
+ //patch3.setLodCalculator(lodCalculator);
+ //TangentBinormalGenerator.generate(patch3);
+
+ // 4 upper right
+ float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
+ split - 1, split);
+
+ Vector3f origin4 = new Vector3f(0, 0, 0);
+
+ Vector2f tempOffset4 = new Vector2f();
+ tempOffset4.x = offset.x;
+ tempOffset4.y = offset.y;
+ tempOffset4.x += quarterSize * stepScale.x;
+ tempOffset4.y += quarterSize * stepScale.z;
+
+ TerrainPatch patch4 = new TerrainPatch(getName() + "Patch4", split,
+ stepScale, heightBlock4, origin4, totalSize, tempOffset4,
+ offsetAmount);
+ patch4.setQuadrant((short) 4);
+ this.attachChild(patch4);
+ patch4.setModelBound(new BoundingBox());
+ patch4.updateModelBound();
+ //patch4.setLodCalculator(lodCalculator);
+ //TangentBinormalGenerator.generate(patch4);
+ }
+
+ public float[] createHeightSubBlock(float[] heightMap, int x,
+ int y, int side) {
+ float[] rVal = new float[side * side];
+ int bsize = (int) FastMath.sqrt(heightMap.length);
+ int count = 0;
+ for (int i = y; i < side + y; i++) {
+ for (int j = x; j < side + x; j++) {
+ if (j < bsize && i < bsize)
+ rVal[count] = heightMap[j + (i * bsize)];
+ count++;
+ }
+ }
+ return rVal;
+ }
+
+ /**
+ * A handy method that will attach all bounding boxes of this terrain
+ * to the node you supply.
+ * Useful to visualize the bounding boxes when debugging.
+ *
+ * @param parent that will get the bounding box shapes of the terrain attached to
+ */
+ public void attachBoundChildren(Node parent) {
+ for (int i = 0; i < this.getQuantity(); i++) {
+ if (this.getChild(i) instanceof TerrainQuad) {
+ ((TerrainQuad) getChild(i)).attachBoundChildren(parent);
+ } else if (this.getChild(i) instanceof TerrainPatch) {
+ BoundingVolume bv = getChild(i).getWorldBound();
+ if (bv instanceof BoundingBox) {
+ attachBoundingBox((BoundingBox)bv, parent);
+ }
+ }
+ }
+ BoundingVolume bv = getWorldBound();
+ if (bv instanceof BoundingBox) {
+ attachBoundingBox((BoundingBox)bv, parent);
+ }
+ }
+
+ /**
+ * used by attachBoundChildren()
+ */
+ private void attachBoundingBox(BoundingBox bb, Node parent) {
+ WireBox wb = new WireBox(bb.getXExtent(), bb.getYExtent(), bb.getZExtent());
+ Geometry g = new Geometry();
+ g.setMesh(wb);
+ g.setLocalTranslation(bb.getCenter());
+ parent.attachChild(g);
+ }
+
+ /**
+ * Signal if the normal vectors for the terrain need to be recalculated.
+ * Does this by looking at the affectedAreaBBox bounding box. If the bbox
+ * exists already, then it will grow the box to fit the new changedPoint.
+ * If the affectedAreaBBox is null, then it will create one of unit size.
+ *
+ * @param needToRecalculateNormals if null, will cause needToRecalculateNormals() to return false
+ */
+ protected void setNormalRecalcNeeded(Vector2f changedPoint) {
+ if (changedPoint == null) { // set needToRecalculateNormals() to false
+ affectedAreaBBox = null;
+ return;
+ }
+
+ if (affectedAreaBBox == null) {
+ affectedAreaBBox = new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 1f, Float.MAX_VALUE, 1f); // unit length
+ } else {
+ // adjust size of box to be larger
+ affectedAreaBBox.mergeLocal(new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 1f, Float.MAX_VALUE, 1f));
+ }
+ }
+
+ protected boolean needToRecalculateNormals() {
+ if (affectedAreaBBox != null)
+ return true;
+ if (!lastScale.equals(getWorldScale())) {
+ affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size);
+ lastScale = getWorldScale();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This will cause all normals for this terrain quad to be recalculated
+ */
+ protected void setNeedToRecalculateNormals() {
+ affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
+ }
+
+ public float getHeightmapHeight(Vector2f xz) {
+ // offset
+ int halfSize = totalSize / 2;
+ int x = Math.round((xz.x / getWorldScale().x) + halfSize);
+ int z = Math.round((xz.y / getWorldScale().z) + halfSize);
+
+ return getHeightmapHeight(x, z);
+ }
+
+ /**
+ * This will just get the heightmap value at the supplied point,
+ * not an interpolated (actual) height value.
+ */
+ protected float getHeightmapHeight(int x, int z) {
+ int quad = findQuadrant(x, z);
+ int split = (size + 1) >> 1;
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial spat = children.get(i);
+ int col = x;
+ int row = z;
+ boolean match = false;
+
+ // get the childs quadrant
+ int childQuadrant = 0;
+ if (spat instanceof TerrainQuad) {
+ childQuadrant = ((TerrainQuad) spat).getQuadrant();
+ } else if (spat instanceof TerrainPatch) {
+ childQuadrant = ((TerrainPatch) spat).getQuadrant();
+ }
+
+ if (childQuadrant == 1 && (quad & 1) != 0) {
+ match = true;
+ } else if (childQuadrant == 2 && (quad & 2) != 0) {
+ row = z - split + 1;
+ match = true;
+ } else if (childQuadrant == 3 && (quad & 4) != 0) {
+ col = x - split + 1;
+ match = true;
+ } else if (childQuadrant == 4 && (quad & 8) != 0) {
+ col = x - split + 1;
+ row = z - split + 1;
+ match = true;
+ }
+
+ if (match) {
+ if (spat instanceof TerrainQuad) {
+ return ((TerrainQuad) spat).getHeightmapHeight(col, row);
+ } else if (spat instanceof TerrainPatch) {
+ return ((TerrainPatch) spat).getHeightmapHeight(col, row);
+ }
+ }
+
+ }
+ }
+ return Float.NaN;
+ }
+
+ protected Vector3f getMeshNormal(int x, int z) {
+ int quad = findQuadrant(x, z);
+ int split = (size + 1) >> 1;
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial spat = children.get(i);
+ int col = x;
+ int row = z;
+ boolean match = false;
+
+ // get the childs quadrant
+ int childQuadrant = 0;
+ if (spat instanceof TerrainQuad) {
+ childQuadrant = ((TerrainQuad) spat).getQuadrant();
+ } else if (spat instanceof TerrainPatch) {
+ childQuadrant = ((TerrainPatch) spat).getQuadrant();
+ }
+
+ if (childQuadrant == 1 && (quad & 1) != 0) {
+ match = true;
+ } else if (childQuadrant == 2 && (quad & 2) != 0) {
+ row = z - split + 1;
+ match = true;
+ } else if (childQuadrant == 3 && (quad & 4) != 0) {
+ col = x - split + 1;
+ match = true;
+ } else if (childQuadrant == 4 && (quad & 8) != 0) {
+ col = x - split + 1;
+ row = z - split + 1;
+ match = true;
+ }
+
+ if (match) {
+ if (spat instanceof TerrainQuad) {
+ return ((TerrainQuad) spat).getMeshNormal(col, row);
+ } else if (spat instanceof TerrainPatch) {
+ return ((TerrainPatch) spat).getMeshNormal(col, row);
+ }
+ }
+
+ }
+ }
+ return null;
+ }
+
+ public float getHeight(Vector2f xz) {
+ // offset
+ float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)totalSize / 2f);
+ float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);
+ float height = getHeight(x, z);
+ height *= getWorldScale().y;
+ return height;
+ }
+
+ /*
+ * gets an interpolated value at the specified point
+ * @param x coordinate translated into actual (positive) terrain grid coordinates
+ * @param y coordinate translated into actual (positive) terrain grid coordinates
+ */
+ protected float getHeight(float x, float z) {
+ x-=0.5f;
+ z-=0.5f;
+ float col = FastMath.floor(x);
+ float row = FastMath.floor(z);
+ boolean onX = false;
+ if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on
+ onX = true;
+ // v1--v2 ^
+ // | / | |
+ // | / | |
+ // v3--v4 | Z
+ // |
+ // <-------Y
+ // X
+ float v1 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.ceil(z));
+ float v2 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.ceil(z));
+ float v3 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.floor(z));
+ float v4 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.floor(z));
+ if (onX) {
+ return ((x - col) + (z - row) - 1f)*v1 + (1f - (x - col))*v2 + (1f - (z - row))*v3;
+ } else {
+ return (1f - (x - col) - (z - row))*v4 + (z - row)*v2 + (x - col)*v3;
+ }
+ }
+
+ public Vector3f getNormal(Vector2f xz) {
+ // offset
+ float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)totalSize / 2f);
+ float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);
+ Vector3f normal = getNormal(x, z, xz);
+
+ return normal;
+ }
+
+ protected Vector3f getNormal(float x, float z, Vector2f xz) {
+ x-=0.5f;
+ z-=0.5f;
+ float col = FastMath.floor(x);
+ float row = FastMath.floor(z);
+ boolean onX = false;
+ if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on
+ onX = true;
+ // v1--v2 ^
+ // | / | |
+ // | / | |
+ // v3--v4 | Z
+ // |
+ // <-------Y
+ // X
+ Vector3f n1 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.ceil(z));
+ Vector3f n2 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.ceil(z));
+ Vector3f n3 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.floor(z));
+ Vector3f n4 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.floor(z));
+
+ return n1.add(n2).add(n3).add(n4).normalize();
+ }
+
+ public void setHeight(Vector2f xz, float height) {
+ List<Vector2f> coord = new ArrayList<Vector2f>();
+ coord.add(xz);
+ List<Float> h = new ArrayList<Float>();
+ h.add(height);
+
+ setHeight(coord, h);
+ }
+
+ public void adjustHeight(Vector2f xz, float delta) {
+ List<Vector2f> coord = new ArrayList<Vector2f>();
+ coord.add(xz);
+ List<Float> h = new ArrayList<Float>();
+ h.add(delta);
+
+ adjustHeight(coord, h);
+ }
+
+ public void setHeight(List<Vector2f> xz, List<Float> height) {
+ setHeight(xz, height, true);
+ }
+
+ public void adjustHeight(List<Vector2f> xz, List<Float> height) {
+ setHeight(xz, height, false);
+ }
+
+ protected void setHeight(List<Vector2f> xz, List<Float> height, boolean overrideHeight) {
+ if (xz.size() != height.size())
+ throw new IllegalArgumentException("Both lists must be the same length!");
+
+ int halfSize = totalSize / 2;
+
+ List<LocationHeight> locations = new ArrayList<LocationHeight>();
+
+ // offset
+ for (int i=0; i<xz.size(); i++) {
+ int x = Math.round((xz.get(i).x / getWorldScale().x) + halfSize);
+ int z = Math.round((xz.get(i).y / getWorldScale().z) + halfSize);
+ locations.add(new LocationHeight(x,z,height.get(i)));
+ }
+
+ setHeight(locations, overrideHeight); // adjust height of the actual mesh
+
+ // signal that the normals need updating
+ for (int i=0; i<xz.size(); i++)
+ setNormalRecalcNeeded(xz.get(i) );
+ }
+
+ protected class LocationHeight {
+ int x;
+ int z;
+ float h;
+
+ LocationHeight(){}
+
+ LocationHeight(int x, int z, float h){
+ this.x = x;
+ this.z = z;
+ this.h = h;
+ }
+ }
+
+ protected void setHeight(List<LocationHeight> locations, boolean overrideHeight) {
+ if (children == null)
+ return;
+
+ List<LocationHeight> quadLH1 = new ArrayList<LocationHeight>();
+ List<LocationHeight> quadLH2 = new ArrayList<LocationHeight>();
+ List<LocationHeight> quadLH3 = new ArrayList<LocationHeight>();
+ List<LocationHeight> quadLH4 = new ArrayList<LocationHeight>();
+ Spatial quad1 = null;
+ Spatial quad2 = null;
+ Spatial quad3 = null;
+ Spatial quad4 = null;
+
+ // get the child quadrants
+ for (int i = children.size(); --i >= 0;) {
+ Spatial spat = children.get(i);
+ int childQuadrant = 0;
+ if (spat instanceof TerrainQuad) {
+ childQuadrant = ((TerrainQuad) spat).getQuadrant();
+ } else if (spat instanceof TerrainPatch) {
+ childQuadrant = ((TerrainPatch) spat).getQuadrant();
+ }
+
+ if (childQuadrant == 1)
+ quad1 = spat;
+ else if (childQuadrant == 2)
+ quad2 = spat;
+ else if (childQuadrant == 3)
+ quad3 = spat;
+ else if (childQuadrant == 4)
+ quad4 = spat;
+ }
+
+ int split = (size + 1) >> 1;
+
+ // distribute each locationHeight into the quadrant it intersects
+ for (LocationHeight lh : locations) {
+ int quad = findQuadrant(lh.x, lh.z);
+
+ int col = lh.x;
+ int row = lh.z;
+
+ if ((quad & 1) != 0) {
+ quadLH1.add(lh);
+ }
+ if ((quad & 2) != 0) {
+ row = lh.z - split + 1;
+ quadLH2.add(new LocationHeight(lh.x, row, lh.h));
+ }
+ if ((quad & 4) != 0) {
+ col = lh.x - split + 1;
+ quadLH3.add(new LocationHeight(col, lh.z, lh.h));
+ }
+ if ((quad & 8) != 0) {
+ col = lh.x - split + 1;
+ row = lh.z - split + 1;
+ quadLH4.add(new LocationHeight(col, row, lh.h));
+ }
+ }
+
+ // send the locations to the children
+ if (!quadLH1.isEmpty()) {
+ if (quad1 instanceof TerrainQuad)
+ ((TerrainQuad)quad1).setHeight(quadLH1, overrideHeight);
+ else if(quad1 instanceof TerrainPatch)
+ ((TerrainPatch)quad1).setHeight(quadLH1, overrideHeight);
+ }
+
+ if (!quadLH2.isEmpty()) {
+ if (quad2 instanceof TerrainQuad)
+ ((TerrainQuad)quad2).setHeight(quadLH2, overrideHeight);
+ else if(quad2 instanceof TerrainPatch)
+ ((TerrainPatch)quad2).setHeight(quadLH2, overrideHeight);
+ }
+
+ if (!quadLH3.isEmpty()) {
+ if (quad3 instanceof TerrainQuad)
+ ((TerrainQuad)quad3).setHeight(quadLH3, overrideHeight);
+ else if(quad3 instanceof TerrainPatch)
+ ((TerrainPatch)quad3).setHeight(quadLH3, overrideHeight);
+ }
+
+ if (!quadLH4.isEmpty()) {
+ if (quad4 instanceof TerrainQuad)
+ ((TerrainQuad)quad4).setHeight(quadLH4, overrideHeight);
+ else if(quad4 instanceof TerrainPatch)
+ ((TerrainPatch)quad4).setHeight(quadLH4, overrideHeight);
+ }
+ }
+
+ protected boolean isPointOnTerrain(int x, int z) {
+ return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize);
+ }
+
+
+ public int getTerrainSize() {
+ return totalSize;
+ }
+
+
+ // a position can be in multiple quadrants, so use a bit anded value.
+ private int findQuadrant(int x, int y) {
+ int split = (size + 1) >> 1;
+ int quads = 0;
+ if (x < split && y < split)
+ quads |= 1;
+ if (x < split && y >= split - 1)
+ quads |= 2;
+ if (x >= split - 1 && y < split)
+ quads |= 4;
+ if (x >= split - 1 && y >= split - 1)
+ quads |= 8;
+ return quads;
+ }
+
+ /**
+ * lock or unlock the meshes of this terrain.
+ * Locked meshes are uneditable but have better performance.
+ * @param locked or unlocked
+ */
+ public void setLocked(boolean locked) {
+ for (int i = 0; i < this.getQuantity(); i++) {
+ if (this.getChild(i) instanceof TerrainQuad) {
+ ((TerrainQuad) getChild(i)).setLocked(locked);
+ } else if (this.getChild(i) instanceof TerrainPatch) {
+ if (locked)
+ ((TerrainPatch) getChild(i)).lockMesh();
+ else
+ ((TerrainPatch) getChild(i)).unlockMesh();
+ }
+ }
+ }
+
+
+ public int getQuadrant() {
+ return quadrant;
+ }
+
+ public void setQuadrant(short quadrant) {
+ this.quadrant = quadrant;
+ }
+
+
+ protected TerrainPatch getPatch(int quad) {
+ if (children != null)
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainPatch) {
+ TerrainPatch tb = (TerrainPatch) child;
+ if (tb.getQuadrant() == quad)
+ return tb;
+ }
+ }
+ return null;
+ }
+
+ protected TerrainQuad getQuad(int quad) {
+ if (children != null)
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ TerrainQuad tq = (TerrainQuad) child;
+ if (tq.getQuadrant() == quad)
+ return tq;
+ }
+ }
+ return null;
+ }
+
+ protected TerrainPatch findRightPatch(TerrainPatch tp) {
+ if (tp.getQuadrant() == 1)
+ return getPatch(3);
+ else if (tp.getQuadrant() == 2)
+ return getPatch(4);
+ else if (tp.getQuadrant() == 3) {
+ // find the patch to the right and ask it for child 1.
+ TerrainQuad quad = findRightQuad();
+ if (quad != null)
+ return quad.getPatch(1);
+ } else if (tp.getQuadrant() == 4) {
+ // find the patch to the right and ask it for child 2.
+ TerrainQuad quad = findRightQuad();
+ if (quad != null)
+ return quad.getPatch(2);
+ }
+
+ return null;
+ }
+
+ protected TerrainPatch findDownPatch(TerrainPatch tp) {
+ if (tp.getQuadrant() == 1)
+ return getPatch(2);
+ else if (tp.getQuadrant() == 3)
+ return getPatch(4);
+ else if (tp.getQuadrant() == 2) {
+ // find the patch below and ask it for child 1.
+ TerrainQuad quad = findDownQuad();
+ if (quad != null)
+ return quad.getPatch(1);
+ } else if (tp.getQuadrant() == 4) {
+ TerrainQuad quad = findDownQuad();
+ if (quad != null)
+ return quad.getPatch(3);
+ }
+
+ return null;
+ }
+
+
+ protected TerrainPatch findTopPatch(TerrainPatch tp) {
+ if (tp.getQuadrant() == 2)
+ return getPatch(1);
+ else if (tp.getQuadrant() == 4)
+ return getPatch(3);
+ else if (tp.getQuadrant() == 1) {
+ // find the patch above and ask it for child 2.
+ TerrainQuad quad = findTopQuad();
+ if (quad != null)
+ return quad.getPatch(2);
+ } else if (tp.getQuadrant() == 3) {
+ TerrainQuad quad = findTopQuad();
+ if (quad != null)
+ return quad.getPatch(4);
+ }
+
+ return null;
+ }
+
+ protected TerrainPatch findLeftPatch(TerrainPatch tp) {
+ if (tp.getQuadrant() == 3)
+ return getPatch(1);
+ else if (tp.getQuadrant() == 4)
+ return getPatch(2);
+ else if (tp.getQuadrant() == 1) {
+ // find the patch above and ask it for child 2.
+ TerrainQuad quad = findLeftQuad();
+ if (quad != null)
+ return quad.getPatch(3);
+ } else if (tp.getQuadrant() == 2) {
+ TerrainQuad quad = findLeftQuad();
+ if (quad != null)
+ return quad.getPatch(4);
+ }
+
+ return null;
+ }
+
+ protected TerrainQuad findRightQuad() {
+ if (getParent() == null || !(getParent() instanceof TerrainQuad))
+ return null;
+
+ TerrainQuad pQuad = (TerrainQuad) getParent();
+
+ if (quadrant == 1)
+ return pQuad.getQuad(3);
+ else if (quadrant == 2)
+ return pQuad.getQuad(4);
+ else if (quadrant == 3) {
+ TerrainQuad quad = pQuad.findRightQuad();
+ if (quad != null)
+ return quad.getQuad(1);
+ } else if (quadrant == 4) {
+ TerrainQuad quad = pQuad.findRightQuad();
+ if (quad != null)
+ return quad.getQuad(2);
+ }
+
+ return null;
+ }
+
+ protected TerrainQuad findDownQuad() {
+ if (getParent() == null || !(getParent() instanceof TerrainQuad))
+ return null;
+
+ TerrainQuad pQuad = (TerrainQuad) getParent();
+
+ if (quadrant == 1)
+ return pQuad.getQuad(2);
+ else if (quadrant == 3)
+ return pQuad.getQuad(4);
+ else if (quadrant == 2) {
+ TerrainQuad quad = pQuad.findDownQuad();
+ if (quad != null)
+ return quad.getQuad(1);
+ } else if (quadrant == 4) {
+ TerrainQuad quad = pQuad.findDownQuad();
+ if (quad != null)
+ return quad.getQuad(3);
+ }
+
+ return null;
+ }
+
+ protected TerrainQuad findTopQuad() {
+ if (getParent() == null || !(getParent() instanceof TerrainQuad))
+ return null;
+
+ TerrainQuad pQuad = (TerrainQuad) getParent();
+
+ if (quadrant == 2)
+ return pQuad.getQuad(1);
+ else if (quadrant == 4)
+ return pQuad.getQuad(3);
+ else if (quadrant == 1) {
+ TerrainQuad quad = pQuad.findTopQuad();
+ if (quad != null)
+ return quad.getQuad(2);
+ } else if (quadrant == 3) {
+ TerrainQuad quad = pQuad.findTopQuad();
+ if (quad != null)
+ return quad.getQuad(4);
+ }
+
+ return null;
+ }
+
+ protected TerrainQuad findLeftQuad() {
+ if (getParent() == null || !(getParent() instanceof TerrainQuad))
+ return null;
+
+ TerrainQuad pQuad = (TerrainQuad) getParent();
+
+ if (quadrant == 3)
+ return pQuad.getQuad(1);
+ else if (quadrant == 4)
+ return pQuad.getQuad(2);
+ else if (quadrant == 1) {
+ TerrainQuad quad = pQuad.findLeftQuad();
+ if (quad != null)
+ return quad.getQuad(3);
+ } else if (quadrant == 2) {
+ TerrainQuad quad = pQuad.findLeftQuad();
+ if (quad != null)
+ return quad.getQuad(4);
+ }
+
+ return null;
+ }
+
+ /**
+ * Find what terrain patches need normal recalculations and update
+ * their normals;
+ */
+ protected void fixNormals(BoundingBox affectedArea) {
+ if (children == null)
+ return;
+
+ // go through the children and see if they collide with the affectedAreaBBox
+ // if they do, then update their normals
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ if (affectedArea != null && affectedArea.intersects(((TerrainQuad) child).getWorldBound()) )
+ ((TerrainQuad) child).fixNormals(affectedArea);
+ } else if (child instanceof TerrainPatch) {
+ if (affectedArea != null && affectedArea.intersects(((TerrainPatch) child).getWorldBound()) )
+ ((TerrainPatch) child).updateNormals(); // recalculate the patch's normals
+ }
+ }
+ }
+
+ /**
+ * fix the normals on the edge of the terrain patches.
+ */
+ protected void fixNormalEdges(BoundingBox affectedArea) {
+ if (children == null)
+ return;
+
+ for (int x = children.size(); --x >= 0;) {
+ Spatial child = children.get(x);
+ if (child instanceof TerrainQuad) {
+ if (affectedArea != null && affectedArea.intersects(((TerrainQuad) child).getWorldBound()) )
+ ((TerrainQuad) child).fixNormalEdges(affectedArea);
+ } else if (child instanceof TerrainPatch) {
+ if (affectedArea != null && !affectedArea.intersects(((TerrainPatch) child).getWorldBound()) ) // if doesn't intersect, continue
+ continue;
+
+ TerrainPatch tp = (TerrainPatch) child;
+ TerrainPatch right = findRightPatch(tp);
+ TerrainPatch bottom = findDownPatch(tp);
+ TerrainPatch top = findTopPatch(tp);
+ TerrainPatch left = findLeftPatch(tp);
+ TerrainPatch topLeft = null;
+ if (top != null)
+ topLeft = findLeftPatch(top);
+ TerrainPatch bottomRight = null;
+ if (right != null)
+ bottomRight = findDownPatch(right);
+ TerrainPatch topRight = null;
+ if (top != null)
+ topRight = findRightPatch(top);
+ TerrainPatch bottomLeft = null;
+ if (left != null)
+ bottomLeft = findDownPatch(left);
+
+ tp.fixNormalEdges(right, bottom, top, left, bottomRight, bottomLeft, topRight, topLeft);
+
+ }
+ } // for each child
+
+ }
+
+
+
+ @Override
+ public int collideWith(Collidable other, CollisionResults results){
+ int total = 0;
+
+ if (other instanceof Ray)
+ return collideWithRay((Ray)other, results);
+
+ // if it didn't collide with this bbox, return
+ if (other instanceof BoundingVolume)
+ if (!this.getWorldBound().intersects((BoundingVolume)other))
+ return total;
+
+ for (Spatial child : children){
+ total += child.collideWith(other, results);
+ }
+ return total;
+ }
+
+ /**
+ * Gather the terrain patches that intersect the given ray (toTest).
+ * This only tests the bounding boxes
+ * @param toTest
+ * @param results
+ */
+ public void findPick(Ray toTest, List<TerrainPickData> results) {
+
+ if (getWorldBound() != null) {
+ if (getWorldBound().intersects(toTest)) {
+ // further checking needed.
+ for (int i = 0; i < getQuantity(); i++) {
+ if (children.get(i) instanceof TerrainPatch) {
+ TerrainPatch tp = (TerrainPatch) children.get(i);
+ tp.ensurePositiveVolumeBBox();
+ if (tp.getWorldBound().intersects(toTest)) {
+ CollisionResults cr = new CollisionResults();
+ toTest.collideWith(tp.getWorldBound(), cr);
+ if (cr != null && cr.getClosestCollision() != null) {
+ cr.getClosestCollision().getDistance();
+ results.add(new TerrainPickData(tp, cr.getClosestCollision()));
+ }
+ }
+ }
+ else
+ ((TerrainQuad) children.get(i)).findPick(toTest, results);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Retrieve all Terrain Patches from all children and store them
+ * in the 'holder' list
+ * @param holder must not be null, will be populated when returns
+ */
+ public void getAllTerrainPatches(List<TerrainPatch> holder) {
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).getAllTerrainPatches(holder);
+ } else if (child instanceof TerrainPatch) {
+ holder.add((TerrainPatch)child);
+ }
+ }
+ }
+ }
+
+ public void getAllTerrainPatchesWithTranslation(Map<TerrainPatch,Vector3f> holder, Vector3f translation) {
+ if (children != null) {
+ for (int i = children.size(); --i >= 0;) {
+ Spatial child = children.get(i);
+ if (child instanceof TerrainQuad) {
+ ((TerrainQuad) child).getAllTerrainPatchesWithTranslation(holder, translation.clone().add(child.getLocalTranslation()));
+ } else if (child instanceof TerrainPatch) {
+ //if (holder.size() < 4)
+ holder.put((TerrainPatch)child, translation.clone().add(child.getLocalTranslation()));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule c = e.getCapsule(this);
+ size = c.readInt("size", 0);
+ stepScale = (Vector3f) c.readSavable("stepScale", null);
+ offset = (Vector2f) c.readSavable("offset", new Vector2f(0,0));
+ offsetAmount = c.readFloat("offsetAmount", 0);
+ quadrant = c.readInt("quadrant", 0);
+ totalSize = c.readInt("totalSize", 0);
+ //lodCalculator = (LodCalculator) c.readSavable("lodCalculator", createDefaultLodCalculator());
+ //lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
+
+ if ( !(getParent() instanceof TerrainQuad) ) {
+ BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize);
+ affectedAreaBBox = all;
+ updateNormals();
+ }
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule c = e.getCapsule(this);
+ c.write(size, "size", 0);
+ c.write(totalSize, "totalSize", 0);
+ c.write(stepScale, "stepScale", null);
+ c.write(offset, "offset", new Vector2f(0,0));
+ c.write(offsetAmount, "offsetAmount", 0);
+ c.write(quadrant, "quadrant", 0);
+ //c.write(lodCalculatorFactory, "lodCalculatorFactory", null);
+ //c.write(lodCalculator, "lodCalculator", null);
+ }
+
+ @Override
+ public TerrainQuad clone() {
+ return this.clone(true);
+ }
+
+ @Override
+ public TerrainQuad clone(boolean cloneMaterials) {
+ TerrainQuad quadClone = (TerrainQuad) super.clone(cloneMaterials);
+ quadClone.name = name.toString();
+ quadClone.size = size;
+ quadClone.totalSize = totalSize;
+ if (stepScale != null) {
+ quadClone.stepScale = stepScale.clone();
+ }
+ if (offset != null) {
+ quadClone.offset = offset.clone();
+ }
+ quadClone.offsetAmount = offsetAmount;
+ quadClone.quadrant = quadrant;
+ //quadClone.lodCalculatorFactory = lodCalculatorFactory.clone();
+ //quadClone.lodCalculator = lodCalculator.clone();
+
+ TerrainLodControl lodControlCloned = this.getControl(TerrainLodControl.class);
+ TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class);
+
+ if (lodControlCloned != null && !(getParent() instanceof TerrainQuad)) {
+ //lodControlCloned.setLodCalculator(lodControl.getLodCalculator().clone());
+ }
+ NormalRecalcControl normalControl = getControl(NormalRecalcControl.class);
+ if (normalControl != null)
+ normalControl.setTerrain(this);
+
+ return quadClone;
+ }
+
+ public int getMaxLod() {
+ if (maxLod < 0)
+ maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
+
+ return maxLod;
+ }
+
+ public int getPatchSize() {
+ return patchSize;
+ }
+
+ public int getTotalSize() {
+ return totalSize;
+ }
+
+ public float[] getHeightMap() {
+
+ float[] hm = null;
+ int length = ((size-1)/2)+1;
+ int area = size*size;
+ hm = new float[area];
+
+ if (getChildren() != null && !getChildren().isEmpty()) {
+ float[] ul=null, ur=null, bl=null, br=null;
+ // get the child heightmaps
+ if (getChild(0) instanceof TerrainPatch) {
+ for (Spatial s : getChildren()) {
+ if ( ((TerrainPatch)s).getQuadrant() == 1)
+ ul = ((TerrainPatch)s).getHeightMap();
+ else if(((TerrainPatch) s).getQuadrant() == 2)
+ bl = ((TerrainPatch)s).getHeightMap();
+ else if(((TerrainPatch) s).getQuadrant() == 3)
+ ur = ((TerrainPatch)s).getHeightMap();
+ else if(((TerrainPatch) s).getQuadrant() == 4)
+ br = ((TerrainPatch)s).getHeightMap();
+ }
+ }
+ else {
+ ul = getQuad(1).getHeightMap();
+ bl = getQuad(2).getHeightMap();
+ ur = getQuad(3).getHeightMap();
+ br = getQuad(4).getHeightMap();
+ }
+
+ // combine them into a single heightmap
+
+
+ // first upper blocks
+ for (int y=0; y<length; y++) { // rows
+ for (int x1=0; x1<length; x1++) {
+ int row = y*size;
+ hm[row+x1] = ul[y*length+x1];
+ }
+ for (int x2=1; x2<length; x2++) {
+ int row = y*size + length;
+ hm[row+x2-1] = ur[y*length + x2];
+ }
+ }
+ // second lower blocks
+ int rowOffset = size*length;
+ for (int y=1; y<length; y++) { // rows
+ for (int x1=0; x1<length; x1++) {
+ int row = (y-1)*size;
+ hm[rowOffset+row+x1] = bl[y*length+x1];
+ }
+ for (int x2=1; x2<length; x2++) {
+ int row = (y-1)*size + length;
+ hm[rowOffset+row+x2-1] = br[y*length + x2];
+ }
+ }
+ }
+
+ return hm;
+ }
+}
+
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java
new file mode 100644
index 0000000..7a335d0
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.scene.VertexBuffer.Type;
+import java.nio.IntBuffer;
+
+/**
+ * Stores a terrain patch's details so the LOD background thread can update
+ * the actual terrain patch back on the ogl thread.
+ *
+ * @author Brent Owens
+ *
+ */
+public class UpdatedTerrainPatch {
+
+ private TerrainPatch updatedPatch;
+ private int newLod;
+ private int previousLod;
+ private int rightLod,topLod,leftLod,bottomLod;
+ private IntBuffer newIndexBuffer;
+ private boolean reIndexNeeded = false;
+ private boolean fixEdges = false;
+
+ public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {
+ this.updatedPatch = updatedPatch;
+ this.newLod = newLod;
+ }
+
+ public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
+ this.updatedPatch = updatedPatch;
+ this.newLod = newLod;
+ this.previousLod = prevLOD;
+ this.reIndexNeeded = reIndexNeeded;
+ if (this.newLod <= 0)
+ throw new IllegalArgumentException();
+ }
+
+ public String getName() {
+ return updatedPatch.getName();
+ }
+
+ protected boolean lodChanged() {
+ if (reIndexNeeded && previousLod != newLod)
+ return true;
+ else
+ return false;
+ }
+
+ protected TerrainPatch getUpdatedPatch() {
+ return updatedPatch;
+ }
+
+ protected void setUpdatedPatch(TerrainPatch updatedPatch) {
+ this.updatedPatch = updatedPatch;
+ }
+
+ protected int getNewLod() {
+ return newLod;
+ }
+
+ public void setNewLod(int newLod) {
+ this.newLod = newLod;
+ if (this.newLod < 0)
+ throw new IllegalArgumentException();
+ }
+
+ protected IntBuffer getNewIndexBuffer() {
+ return newIndexBuffer;
+ }
+
+ protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {
+ this.newIndexBuffer = newIndexBuffer;
+ }
+
+
+ protected int getRightLod() {
+ return rightLod;
+ }
+
+
+ protected void setRightLod(int rightLod) {
+ this.rightLod = rightLod;
+ }
+
+
+ protected int getTopLod() {
+ return topLod;
+ }
+
+
+ protected void setTopLod(int topLod) {
+ this.topLod = topLod;
+ }
+
+
+ protected int getLeftLod() {
+ return leftLod;
+ }
+
+
+ protected void setLeftLod(int leftLod) {
+ this.leftLod = leftLod;
+ }
+
+
+ protected int getBottomLod() {
+ return bottomLod;
+ }
+
+
+ protected void setBottomLod(int bottomLod) {
+ this.bottomLod = bottomLod;
+ }
+
+ public boolean isReIndexNeeded() {
+ return reIndexNeeded;
+ }
+
+ public void setReIndexNeeded(boolean reIndexNeeded) {
+ this.reIndexNeeded = reIndexNeeded;
+ }
+
+ public boolean isFixEdges() {
+ return fixEdges;
+ }
+
+ public void setFixEdges(boolean fixEdges) {
+ this.fixEdges = fixEdges;
+ }
+
+ public int getPreviousLod() {
+ return previousLod;
+ }
+
+ public void setPreviousLod(int previousLod) {
+ this.previousLod = previousLod;
+ }
+
+ public void updateAll() {
+ updatedPatch.setLod(newLod);
+ updatedPatch.setLodRight(rightLod);
+ updatedPatch.setLodTop(topLod);
+ updatedPatch.setLodLeft(leftLod);
+ updatedPatch.setLodBottom(bottomLod);
+ if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
+ updatedPatch.setPreviousLod(previousLod);
+ updatedPatch.getMesh().clearBuffer(Type.Index);
+ updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);
+ }
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java
new file mode 100644
index 0000000..0c61e75
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java
@@ -0,0 +1,92 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.geomipmap.grid;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGridTileLoader;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class AssetTileLoader implements TerrainGridTileLoader {
+
+ private AssetManager manager;
+ private String assetPath;
+ private String name;
+ private int size;
+ private int patchSize;
+ private int quadSize;
+
+ public AssetTileLoader() {
+ }
+
+ public AssetTileLoader(AssetManager manager, String name, String assetPath) {
+ this.manager = manager;
+ this.name = name;
+ this.assetPath = assetPath;
+ }
+
+ public TerrainQuad getTerrainQuadAt(Vector3f location) {
+ String modelName = assetPath + "/" + name + "_" + Math.round(location.x) + "_" + Math.round(location.y) + "_" + Math.round(location.z) + ".j3o";
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Load terrain grid tile: {0}", modelName);
+ TerrainQuad quad = null;
+ try {
+ quad = (TerrainQuad) manager.loadModel(modelName);
+ } catch (Exception e) {
+// e.printStackTrace();
+ }
+ if (quad == null) {
+ Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Could not load terrain grid tile: {0}", modelName);
+ quad = createNewQuad(location);
+ } else {
+ Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Loaded terrain grid tile: {0}", modelName);
+ }
+ return quad;
+ }
+
+ public String getAssetPath() {
+ return assetPath;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setPatchSize(int patchSize) {
+ this.patchSize = patchSize;
+ }
+
+ public void setQuadSize(int quadSize) {
+ this.quadSize = quadSize;
+ }
+
+ private TerrainQuad createNewQuad(Vector3f location) {
+ TerrainQuad q = new TerrainQuad("Quad" + location, patchSize, quadSize, null);
+ return q;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule c = ex.getCapsule(this);
+ c.write(assetPath, "assetPath", null);
+ c.write(name, "name", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule c = im.getCapsule(this);
+ manager = im.getAssetManager();
+ assetPath = c.readString("assetPath", null);
+ name = c.readString("name", null);
+ }
+} \ No newline at end of file
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java
new file mode 100644
index 0000000..9eaa9a2
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java
@@ -0,0 +1,87 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.geomipmap.grid;
+
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGridTileLoader;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.HeightMap;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import com.jme3.terrain.noise.Basis;
+
+/**
+ *
+ * @author Anthyon, normenhansen
+ */
+public class FractalTileLoader implements TerrainGridTileLoader{
+
+ public class FloatBufferHeightMap extends AbstractHeightMap {
+
+ private final FloatBuffer buffer;
+
+ public FloatBufferHeightMap(FloatBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ @Override
+ public boolean load() {
+ this.heightData = this.buffer.array();
+ return true;
+ }
+
+ }
+
+ private int patchSize;
+ private int quadSize;
+ private final Basis base;
+ private final float heightScale;
+
+ public FractalTileLoader(Basis base, float heightScale) {
+ this.base = base;
+ this.heightScale = heightScale;
+ }
+
+ private HeightMap getHeightMapAt(Vector3f location) {
+ AbstractHeightMap heightmap = null;
+
+ FloatBuffer buffer = this.base.getBuffer(location.x * (this.quadSize - 1), location.z * (this.quadSize - 1), 0, this.quadSize);
+
+ float[] arr = buffer.array();
+ for (int i = 0; i < arr.length; i++) {
+ arr[i] = arr[i] * this.heightScale;
+ }
+ heightmap = new FloatBufferHeightMap(buffer);
+ heightmap.load();
+ return heightmap;
+ }
+
+ public TerrainQuad getTerrainQuadAt(Vector3f location) {
+ HeightMap heightMapAt = getHeightMapAt(location);
+ TerrainQuad q = new TerrainQuad("Quad" + location, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
+ return q;
+ }
+
+ public void setPatchSize(int patchSize) {
+ this.patchSize = patchSize;
+ }
+
+ public void setQuadSize(int quadSize) {
+ this.quadSize = quadSize;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ //TODO: serialization
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ //TODO: serialization
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java
new file mode 100644
index 0000000..cef6a72
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java
@@ -0,0 +1,153 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.geomipmap.grid;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGridTileLoader;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.*;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Anthyon, normenhansen
+ */
+public class ImageTileLoader implements TerrainGridTileLoader{
+ private static final Logger logger = Logger.getLogger(ImageTileLoader.class.getName());
+ private final AssetManager assetManager;
+ private final Namer namer;
+ private int patchSize;
+ private int quadSize;
+ private float heightScale = 1;
+ //private int imageType = BufferedImage.TYPE_USHORT_GRAY; // 16 bit grayscale
+ //private ImageHeightmap customImageHeightmap;
+
+ public ImageTileLoader(final String textureBase, final String textureExt, AssetManager assetManager) {
+ this(assetManager, new Namer() {
+
+ public String getName(int x, int y) {
+ return textureBase + "_" + x + "_" + y + "." + textureExt;
+ }
+ });
+ }
+
+ public ImageTileLoader(AssetManager assetManager, Namer namer) {
+ this.assetManager = assetManager;
+ this.namer = namer;
+ }
+
+ /**
+ * Effects vertical scale of the height of the terrain when loaded.
+ */
+ public void setHeightScale(float heightScale) {
+ this.heightScale = heightScale;
+ }
+
+
+ /**
+ * Lets you specify the type of images that are being loaded. All images
+ * must be the same type.
+ * @param imageType eg. BufferedImage.TYPE_USHORT_GRAY
+ */
+ /*public void setImageType(int imageType) {
+ this.imageType = imageType;
+ }*/
+
+ /**
+ * The ImageHeightmap that will parse the image type that you
+ * specify with setImageType().
+ * @param customImageHeightmap must extend AbstractHeightmap
+ */
+ /*public void setCustomImageHeightmap(ImageHeightmap customImageHeightmap) {
+ if (!(customImageHeightmap instanceof AbstractHeightMap)) {
+ throw new IllegalArgumentException("customImageHeightmap must be an AbstractHeightMap!");
+ }
+ this.customImageHeightmap = customImageHeightmap;
+ }*/
+
+ private HeightMap getHeightMapAt(Vector3f location) {
+ // HEIGHTMAP image (for the terrain heightmap)
+ int x = (int) location.x;
+ int z = (int) location.z;
+
+ AbstractHeightMap heightmap = null;
+ //BufferedImage im = null;
+
+ String name = null;
+ try {
+ name = namer.getName(x, z);
+ logger.log(Level.INFO, "Loading heightmap from file: {0}", name);
+ final Texture texture = assetManager.loadTexture(new TextureKey(name));
+ heightmap = new ImageBasedHeightMap(texture.getImage());
+ /*if (assetInfo != null){
+ InputStream in = assetInfo.openStream();
+ im = ImageIO.read(in);
+ } else {
+ im = new BufferedImage(patchSize, patchSize, imageType);
+ logger.log(Level.WARNING, "File: {0} not found, loading zero heightmap instead", name);
+ }*/
+ // CREATE HEIGHTMAP
+ /*if (imageType == BufferedImage.TYPE_USHORT_GRAY) {
+ heightmap = new Grayscale16BitHeightMap(im);
+ } else if (imageType == BufferedImage.TYPE_3BYTE_BGR) {
+ heightmap = new ImageBasedHeightMap(im);
+ } else if (customImageHeightmap != null && customImageHeightmap instanceof AbstractHeightMap) {
+ // If it gets here, it means you have specified a different image type, and you must
+ // then also supply a custom image heightmap class that can parse that image into
+ // a heightmap.
+ customImageHeightmap.setImage(im);
+ heightmap = (AbstractHeightMap) customImageHeightmap;
+ } else {
+ // error, no supported image format and no custom image heightmap specified
+ if (customImageHeightmap == null)
+ logger.log(Level.SEVERE, "Custom image type specified [{0}] but no customImageHeightmap declared! Use setCustomImageHeightmap()",imageType);
+ if (!(customImageHeightmap instanceof AbstractHeightMap))
+ logger.severe("customImageHeightmap must be an AbstractHeightMap!");
+ return null;
+ }*/
+ heightmap.setHeightScale(1);
+ heightmap.load();
+ //} catch (IOException e) {
+ // e.printStackTrace();
+ } catch (AssetNotFoundException e) {
+ logger.log(Level.WARNING, "Asset {0} not found, loading zero heightmap instead", name);
+ }
+ return heightmap;
+ }
+
+ public void setSize(int size) {
+ this.patchSize = size - 1;
+ }
+
+ public TerrainQuad getTerrainQuadAt(Vector3f location) {
+ HeightMap heightMapAt = getHeightMapAt(location);
+ TerrainQuad q = new TerrainQuad("Quad" + location, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
+ return q;
+ }
+
+ public void setPatchSize(int patchSize) {
+ this.patchSize = patchSize;
+ }
+
+ public void setQuadSize(int quadSize) {
+ this.quadSize = quadSize;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ //TODO: serialization
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ //TODO: serialization
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java
new file mode 100644
index 0000000..3ab34c9
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import com.jme3.terrain.geomipmap.UpdatedTerrainPatch;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Calculates the LOD of the terrain based on its distance from the
+ * cameras. Taking the minimum distance from all cameras.
+ *
+ * @author bowens
+ */
+public class DistanceLodCalculator implements LodCalculator {
+
+ private int size; // size of a terrain patch
+ private float lodMultiplier = 2;
+ private boolean turnOffLod = false;
+
+ public DistanceLodCalculator() {
+ }
+
+ public DistanceLodCalculator(int patchSize, float multiplier) {
+ this.size = patchSize;
+ this.lodMultiplier = multiplier;
+ }
+
+ public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
+ float distance = getCenterLocation(terrainPatch).distance(locations.get(0));
+
+
+ if (turnOffLod) {
+ // set to full detail
+ int prevLOD = terrainPatch.getLod();
+ UpdatedTerrainPatch utp = updates.get(terrainPatch.getName());
+ if (utp == null) {
+ utp = new UpdatedTerrainPatch(terrainPatch, 0);
+ updates.put(utp.getName(), utp);
+ }
+ utp.setNewLod(0);
+ utp.setPreviousLod(prevLOD);
+ utp.setReIndexNeeded(true);
+ return true;
+ }
+
+ // go through each lod level to find the one we are in
+ for (int i = 0; i <= terrainPatch.getMaxLod(); i++) {
+ if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScale().x || i == terrainPatch.getMaxLod()) {
+ boolean reIndexNeeded = false;
+ if (i != terrainPatch.getLod()) {
+ reIndexNeeded = true;
+ //System.out.println("lod change: "+lod+" > "+i+" dist: "+distance);
+ }
+ int prevLOD = terrainPatch.getLod();
+ //previousLod = lod;
+ //lod = i;
+ UpdatedTerrainPatch utp = updates.get(terrainPatch.getName());
+ if (utp == null) {
+ utp = new UpdatedTerrainPatch(terrainPatch, i);//save in here, do not update actual variables
+ updates.put(utp.getName(), utp);
+ }
+ utp.setPreviousLod(prevLOD);
+ utp.setReIndexNeeded(reIndexNeeded);
+
+ return reIndexNeeded;
+ }
+ }
+
+ return false;
+ }
+
+ protected Vector3f getCenterLocation(TerrainPatch terrainPatch) {
+ Vector3f loc = terrainPatch.getWorldTranslation().clone();
+ loc.x += terrainPatch.getSize()*terrainPatch.getWorldScale().x / 2;
+ loc.z += terrainPatch.getSize()*terrainPatch.getWorldScale().z / 2;
+ return loc;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(size, "patchSize", 32);
+ oc.write(lodMultiplier, "lodMultiplier", 32);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ size = ic.readInt("patchSize", 32);
+ lodMultiplier = ic.readFloat("lodMultiplier", 2.7f);
+ }
+
+ @Override
+ public LodCalculator clone() {
+ DistanceLodCalculator clone = new DistanceLodCalculator(size, lodMultiplier);
+ return clone;
+ }
+
+ @Override
+ public String toString() {
+ return "DistanceLodCalculator "+size+"*"+lodMultiplier;
+ }
+
+ /**
+ * Gets the camera distance where the LOD level will change
+ */
+ protected float getLodDistanceThreshold() {
+ return size*lodMultiplier;
+ }
+
+ /**
+ * Does this calculator require the terrain to have the difference of
+ * LOD levels of neighbours to be more than 1.
+ */
+ public boolean usesVariableLod() {
+ return false;
+ }
+
+ public float getLodMultiplier() {
+ return lodMultiplier;
+ }
+
+ public void setLodMultiplier(float lodMultiplier) {
+ this.lodMultiplier = lodMultiplier;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public void turnOffLod() {
+ turnOffLod = true;
+ }
+
+ public boolean isLodOff() {
+ return turnOffLod;
+ }
+
+ public void turnOnLod() {
+ turnOffLod = false;
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java
new file mode 100644
index 0000000..2d6c364
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import com.jme3.terrain.geomipmap.UpdatedTerrainPatch;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Calculate the Level of Detail of a terrain patch based on the
+ * cameras, or other locations.
+ *
+ * @author Brent Owens
+ */
+public interface LodCalculator extends Savable, Cloneable {
+
+ public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String,UpdatedTerrainPatch> updates);
+
+ public LodCalculator clone();
+
+ public void turnOffLod();
+ public void turnOnLod();
+ public boolean isLodOff();
+
+ /**
+ * If true, then this calculator can cause neighbouring terrain chunks to
+ * have LOD levels that are greater than 1 apart.
+ * Entropy algorithms will want to return true for this. Straight distance
+ * calculations will just want to return false.
+ */
+ public boolean usesVariableLod();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java
new file mode 100644
index 0000000..6d5f5b8
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.Savable;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+
+/**
+ * Creates LOD Calculator objects for the terrain patches.
+ *
+ * @author Brent Owens
+ * @deprecated phasing this out
+ */
+public interface LodCalculatorFactory extends Savable, Cloneable {
+
+ public LodCalculator createCalculator();
+ public LodCalculator createCalculator(TerrainPatch terrainPatch);
+
+ public LodCalculatorFactory clone();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java
new file mode 100644
index 0000000..29d5c25
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import java.io.IOException;
+
+/**
+ *
+ * @author bowens
+ * @deprecated phasing out
+ */
+public class LodDistanceCalculatorFactory implements LodCalculatorFactory {
+
+ private float lodThresholdSize = 2.7f;
+ private LodThreshold lodThreshold = null;
+
+
+ public LodDistanceCalculatorFactory() {
+ }
+
+ public LodDistanceCalculatorFactory(LodThreshold lodThreshold) {
+ this.lodThreshold = lodThreshold;
+ }
+
+ public LodCalculator createCalculator() {
+ return new DistanceLodCalculator();
+ }
+
+ public LodCalculator createCalculator(TerrainPatch terrainPatch) {
+ return new DistanceLodCalculator();
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule c = ex.getCapsule(this);
+ c.write(lodThreshold, "lodThreshold", null);
+ c.write(lodThresholdSize, "lodThresholdSize", 2);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule c = im.getCapsule(this);
+ lodThresholdSize = c.readFloat("lodThresholdSize", 2);
+ lodThreshold = (LodThreshold) c.readSavable("lodThreshold", null);
+ }
+
+ @Override
+ public LodDistanceCalculatorFactory clone() {
+ LodDistanceCalculatorFactory clone = new LodDistanceCalculatorFactory();
+ clone.lodThreshold = lodThreshold.clone();
+ clone.lodThresholdSize = lodThresholdSize;
+ return clone;
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java
new file mode 100644
index 0000000..b453e26
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import java.io.IOException;
+
+/**
+ * TODO: Make it work with multiple cameras
+ * TODO: Fix the cracks when the lod differences are greater than 1
+ * for two adjacent blocks.
+ * @deprecated phasing out
+ */
+public class LodPerspectiveCalculatorFactory implements LodCalculatorFactory {
+
+ private Camera cam;
+ private float pixelError;
+
+ public LodPerspectiveCalculatorFactory(Camera cam, float pixelError){
+ this.cam = cam;
+ this.pixelError = pixelError;
+ }
+
+ public LodCalculator createCalculator() {
+ return new PerspectiveLodCalculator(cam, pixelError);
+ }
+
+ public LodCalculator createCalculator(TerrainPatch terrainPatch) {
+ PerspectiveLodCalculator p = new PerspectiveLodCalculator(cam, pixelError);
+ return p;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ }
+
+ @Override
+ public LodCalculatorFactory clone() {
+ try {
+ return (LodCalculatorFactory) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java
new file mode 100644
index 0000000..73d5c35
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.Savable;
+
+
+/**
+ * Calculates the LOD value based on where the camera is.
+ * This is plugged into the Terrain system and any terrain
+ * using LOD will use this to determine when a patch of the
+ * terrain should switch Levels of Detail.
+ *
+ * @author bowens
+ */
+public interface LodThreshold extends Savable, Cloneable {
+
+ /**
+ * A distance of how far between each LOD threshold.
+ */
+ public float getLodDistanceThreshold();
+
+ public LodThreshold clone();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java
new file mode 100644
index 0000000..312b17d
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import com.jme3.terrain.geomipmap.UpdatedTerrainPatch;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+public class PerspectiveLodCalculator implements LodCalculator {
+
+ private TerrainPatch patch;
+ private Camera cam;
+ private float[] entropyDistances;
+ private float pixelError;
+
+ public PerspectiveLodCalculator() {}
+
+ public PerspectiveLodCalculator(Camera cam, float pixelError){
+ this.cam = cam;
+ this.pixelError = pixelError;
+ }
+
+ /**
+ * This computes the "C" value in the geomipmapping paper.
+ * See section "2.3.1.2 Pre-calculating d"
+ *
+ * @param cam
+ * @param pixelLimit
+ * @return
+ */
+ private float getCameraConstant(Camera cam, float pixelLimit){
+ float n = cam.getFrustumNear();
+ float t = FastMath.abs(cam.getFrustumTop());
+ float A = n / t;
+ float v_res = cam.getHeight();
+ float T = (2f * pixelLimit) / v_res;
+ return A / T;
+ }
+
+ public boolean calculateLod(List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
+ return calculateLod(patch, locations, updates);
+ }
+
+ public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
+ if (entropyDistances == null){
+ // compute entropy distances
+ float[] lodEntropies = patch.getLodEntropies();
+ entropyDistances = new float[lodEntropies.length];
+ float cameraConstant = getCameraConstant(cam, pixelError);
+ for (int i = 0; i < lodEntropies.length; i++){
+ entropyDistances[i] = lodEntropies[i] * cameraConstant;
+ }
+ }
+
+ Vector3f patchPos = getCenterLocation(patch);
+
+ // vector from camera to patch
+ //Vector3f toPatchDir = locations.get(0).subtract(patchPos).normalizeLocal();
+ //float facing = cam.getDirection().dot(toPatchDir);
+ float distance = patchPos.distance(locations.get(0));
+
+ // go through each lod level to find the one we are in
+ for (int i = 0; i <= patch.getMaxLod(); i++) {
+ if (distance < entropyDistances[i] || i == patch.getMaxLod()){
+ boolean reIndexNeeded = false;
+ if (i != patch.getLod()) {
+ reIndexNeeded = true;
+// System.out.println("lod change: "+lod+" > "+i+" dist: "+distance);
+ }
+ int prevLOD = patch.getLod();
+
+ //previousLod = lod;
+ //lod = i;
+ UpdatedTerrainPatch utp = updates.get(patch.getName());
+ if (utp == null) {
+ utp = new UpdatedTerrainPatch(patch, i);//save in here, do not update actual variables
+ updates.put(utp.getName(), utp);
+ }
+ utp.setPreviousLod(prevLOD);
+ utp.setReIndexNeeded(reIndexNeeded);
+ return reIndexNeeded;
+ }
+ }
+
+ return false;
+ }
+
+ public Vector3f getCenterLocation(TerrainPatch patch) {
+ Vector3f loc = patch.getWorldTranslation().clone();
+ loc.x += patch.getSize() / 2;
+ loc.z += patch.getSize() / 2;
+ return loc;
+ }
+
+ @Override
+ public LodCalculator clone() {
+ try {
+ return (LodCalculator) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ }
+
+ public boolean usesVariableLod() {
+ return true;
+ }
+
+ public float getPixelError() {
+ return pixelError;
+ }
+
+ public void setPixelError(float pixelError) {
+ this.pixelError = pixelError;
+ }
+
+ public void setCam(Camera cam) {
+ this.cam = cam;
+ }
+
+ public void turnOffLod() {
+ //TODO
+ }
+
+ public boolean isLodOff() {
+ return false; //TODO
+ }
+
+ public void turnOnLod() {
+ //TODO
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java
new file mode 100644
index 0000000..bd512e5
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.lodcalc;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import java.io.IOException;
+
+
+/**
+ * Just multiplies the terrain patch size by 2. So every two
+ * patches away the camera is, the LOD changes.
+ *
+ * Set it higher to have the LOD change less frequently.
+ *
+ * @author bowens
+ */
+public class SimpleLodThreshold implements LodThreshold {
+
+ private int size; // size of a terrain patch
+ private float lodMultiplier = 2;
+
+
+ public SimpleLodThreshold() {
+ }
+
+ public SimpleLodThreshold(Terrain terrain) {
+ if (terrain instanceof TerrainQuad)
+ this.size = ((TerrainQuad)terrain).getPatchSize();
+ }
+
+ public SimpleLodThreshold(int patchSize, float lodMultiplier) {
+ this.size = patchSize;
+ }
+
+ public float getLodMultiplier() {
+ return lodMultiplier;
+ }
+
+ public void setLodMultiplier(float lodMultiplier) {
+ this.lodMultiplier = lodMultiplier;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+
+ public float getLodDistanceThreshold() {
+ return size*lodMultiplier;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(size, "size", 16);
+ oc.write(lodMultiplier, "lodMultiplier", 2);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ size = ic.readInt("size", 16);
+ lodMultiplier = ic.readInt("lodMultiplier", 2);
+ }
+
+ @Override
+ public LodThreshold clone() {
+ SimpleLodThreshold clone = new SimpleLodThreshold();
+ clone.size = size;
+ clone.lodMultiplier = lodMultiplier;
+
+ return clone;
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleLodThreshold "+size+", "+lodMultiplier;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java
new file mode 100644
index 0000000..c358aae
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java
@@ -0,0 +1,75 @@
+package com.jme3.terrain.geomipmap.lodcalc.util;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.collision.CollisionResults;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Computes the entropy value δ (delta) for a given terrain block and
+ * LOD level.
+ * See the geomipmapping paper section
+ * "2.3.1 Choosing the appropriate GeoMipMap level"
+ *
+ * @author Kirill Vainer
+ */
+public class EntropyComputeUtil {
+
+ public static float computeLodEntropy(Mesh terrainBlock, IntBuffer lodIndices){
+ // Bounding box for the terrain block
+ BoundingBox bbox = (BoundingBox) terrainBlock.getBound();
+
+ // Vertex positions for the block
+ FloatBuffer positions = terrainBlock.getFloatBuffer(Type.Position);
+
+ // Prepare to cast rays
+ Vector3f pos = new Vector3f();
+ Vector3f dir = new Vector3f(0, -1, 0);
+ Ray ray = new Ray(pos, dir);
+
+ // Prepare collision results
+ CollisionResults results = new CollisionResults();
+
+ // Set the LOD indices on the block
+ VertexBuffer originalIndices = terrainBlock.getBuffer(Type.Index);
+
+ terrainBlock.clearBuffer(Type.Index);
+ terrainBlock.setBuffer(Type.Index, 3, lodIndices);
+
+ // Recalculate collision mesh
+ terrainBlock.createCollisionData();
+
+ float entropy = 0;
+ for (int i = 0; i < positions.capacity() / 3; i++){
+ BufferUtils.populateFromBuffer(pos, positions, i);
+
+ float realHeight = pos.y;
+
+ pos.addLocal(0, bbox.getYExtent(), 0);
+ ray.setOrigin(pos);
+
+ results.clear();
+ terrainBlock.collideWith(ray, Matrix4f.IDENTITY, bbox, results);
+
+ if (results.size() > 0){
+ Vector3f contactPoint = results.getClosestCollision().getContactPoint();
+ float delta = Math.abs(realHeight - contactPoint.y);
+ entropy = Math.max(delta, entropy);
+ }
+ }
+
+ // Restore original indices
+ terrainBlock.clearBuffer(Type.Index);
+ terrainBlock.setBuffer(originalIndices);
+
+ return entropy;
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java
new file mode 100644
index 0000000..9f044c6
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.picking;
+
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.math.Ray;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.picking.BresenhamYUpGridTracer.Direction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * It basically works by casting a pick ray
+ * against the bounding volumes of the TerrainQuad and its children, gathering
+ * all of the TerrainPatches hit (in distance order.) The triangles of each patch
+ * are then tested using the BresenhamYUpGridTracer to determine which triangles
+ * to test and in what order. When a hit is found, it is guaranteed to be the
+ * first such hit and can immediately be returned.
+ *
+ * @author Joshua Slack
+ * @author Brent Owens
+ */
+public class BresenhamTerrainPicker implements TerrainPicker {
+
+ private final Triangle gridTriA = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
+ private final Triangle gridTriB = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
+
+ private final Vector3f calcVec1 = new Vector3f();
+ private final Ray workRay = new Ray();
+ private final Ray worldPickRay = new Ray();
+
+ private final TerrainQuad root;
+ private final BresenhamYUpGridTracer tracer = new BresenhamYUpGridTracer();
+
+
+ public BresenhamTerrainPicker(TerrainQuad root) {
+ this.root = root;
+ }
+
+ public Vector3f getTerrainIntersection(Ray worldPick, CollisionResults results) {
+
+ worldPickRay.set(worldPick);
+ List<TerrainPickData> pickData = new ArrayList<TerrainPickData>();
+ root.findPick(worldPick.clone(), pickData);
+ Collections.sort(pickData);
+
+ if (pickData.isEmpty())
+ return null;
+
+ workRay.set(worldPick);
+
+ for (TerrainPickData pd : pickData) {
+ TerrainPatch patch = pd.targetPatch;
+
+
+ tracer.getGridSpacing().set(patch.getWorldScale());
+ tracer.setGridOrigin(patch.getWorldTranslation());
+
+ workRay.getOrigin().set(worldPick.getDirection()).multLocal(pd.cr.getDistance()-.1f).addLocal(worldPick.getOrigin());
+
+ tracer.startWalk(workRay);
+
+ final Vector3f intersection = new Vector3f();
+ final Vector2f loc = tracer.getGridLocation();
+
+ if (tracer.isRayPerpendicularToGrid()) {
+ Triangle hit = new Triangle();
+ checkTriangles(loc.x, loc.y, workRay, intersection, patch, hit);
+ float distance = worldPickRay.origin.distance(intersection);
+ CollisionResult cr = new CollisionResult(intersection, distance);
+ cr.setGeometry(patch);
+ cr.setContactNormal(hit.getNormal());
+ results.addCollision(cr);
+ return intersection;
+ }
+
+
+
+ while (loc.x >= -1 && loc.x <= patch.getSize() &&
+ loc.y >= -1 && loc.y <= patch.getSize()) {
+
+ //System.out.print(loc.x+","+loc.y+" : ");
+ // check the triangles of main square for intersection.
+ Triangle hit = new Triangle();
+ if (checkTriangles(loc.x, loc.y, workRay, intersection, patch, hit)) {
+ // we found an intersection, so return that!
+ float distance = worldPickRay.origin.distance(intersection);
+ CollisionResult cr = new CollisionResult(intersection, distance);
+ cr.setGeometry(patch);
+ results.addCollision(cr);
+ cr.setContactNormal(hit.getNormal());
+ return intersection;
+ }
+
+ // because of how we get our height coords, we will
+ // sometimes be off by a grid spot, so we check the next
+ // grid space up.
+ int dx = 0, dz = 0;
+ Direction d = tracer.getLastStepDirection();
+ switch (d) {
+ case PositiveX:
+ case NegativeX:
+ dx = 0;
+ dz = 1;
+ break;
+ case PositiveZ:
+ case NegativeZ:
+ dx = 1;
+ dz = 0;
+ break;
+ }
+
+ if (checkTriangles(loc.x + dx, loc.y + dz, workRay, intersection, patch, hit)) {
+ // we found an intersection, so return that!
+ float distance = worldPickRay.origin.distance(intersection);
+ CollisionResult cr = new CollisionResult(intersection, distance);
+ results.addCollision(cr);
+ cr.setGeometry(patch);
+ cr.setContactNormal(hit.getNormal());
+ return intersection;
+ }
+
+ tracer.next();
+ }
+ }
+
+ return null;
+ }
+
+ protected boolean checkTriangles(float gridX, float gridY, Ray pick, Vector3f intersection, TerrainPatch patch, Triangle store) {
+ if (!getTriangles(gridX, gridY, patch))
+ return false;
+
+ if (pick.intersectWhere(gridTriA, intersection)) {
+ store.set(gridTriA.get1(), gridTriA.get2(), gridTriA.get3());
+ return true;
+ } else {
+ if (pick.intersectWhere(gridTriB, intersection)) {
+ store.set(gridTriB.get1(), gridTriB.get2(), gridTriB.get3());
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Request the triangles (in world coord space) of a TerrainBlock that
+ * correspond to the given grid location. The triangles are stored in the
+ * class fields _gridTriA and _gridTriB.
+ *
+ * @param gridX
+ * grid row
+ * @param gridY
+ * grid column
+ * @param block
+ * the TerrainBlock we are working with
+ * @return true if the grid point is valid for the given block, false if it
+ * is off the block.
+ */
+ protected boolean getTriangles(float gridX, float gridY, TerrainPatch patch) {
+ calcVec1.set(gridX, 0, gridY);
+ int index = findClosestHeightIndex(calcVec1, patch);
+
+ if (index == -1)
+ return false;
+
+ Triangle[] t = patch.getGridTriangles(gridX, gridY);
+ if (t == null || t.length == 0)
+ return false;
+
+ gridTriA.set1(t[0].get1());
+ gridTriA.set2(t[0].get2());
+ gridTriA.set3(t[0].get3());
+
+ gridTriB.set1(t[1].get1());
+ gridTriB.set2(t[1].get2());
+ gridTriB.set3(t[1].get3());
+
+ return true;
+ }
+
+ /**
+ * Finds the closest height point to a position. Will always be left/above
+ * that position.
+ *
+ * @param position
+ * the position to check at
+ * @param block
+ * the block to get height values from
+ * @return an index to the height position of the given block.
+ */
+ protected int findClosestHeightIndex(Vector3f position, TerrainPatch patch) {
+
+ int x = (int) position.x;
+ int z = (int) position.z;
+
+ if (x < 0 || x >= patch.getSize() - 1) {
+ return -1;
+ }
+ if (z < 0 || z >= patch.getSize() - 1) {
+ return -1;
+ }
+
+ return z * patch.getSize() + x;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamYUpGridTracer.java b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamYUpGridTracer.java
new file mode 100644
index 0000000..8d8a53d
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamYUpGridTracer.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.picking;
+
+import com.jme3.math.Ray;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+
+/**
+ * Works on the XZ plane, with positive Y as up.
+ *
+ * @author Joshua Slack
+ * @author Brent Owens
+ */
+public class BresenhamYUpGridTracer {
+
+ protected Vector3f gridOrigin = new Vector3f();
+ protected Vector3f gridSpacing = new Vector3f();
+ protected Vector2f gridLocation = new Vector2f();
+ protected Vector3f rayLocation = new Vector3f();
+ protected Ray walkRay = new Ray();
+
+ protected Direction stepDirection = Direction.None;
+ protected float rayLength;
+
+ public static enum Direction {
+ None, PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ;
+ };
+
+ // a "near zero" value we will use to determine if the walkRay is
+ // perpendicular to the grid.
+ protected static float TOLERANCE = 0.0000001f;
+
+ private int stepXDirection;
+ private int stepZDirection;
+
+ // from current position along ray
+ private float distToNextXIntersection, distToNextZIntersection;
+ private float distBetweenXIntersections, distBetweenZIntersections;
+
+ public void startWalk(final Ray walkRay) {
+ // store ray
+ this.walkRay.set(walkRay);
+
+ // simplify access to direction
+ Vector3f direction = this.walkRay.getDirection();
+
+ // Move start point to grid space
+ Vector3f start = this.walkRay.getOrigin().subtract(gridOrigin);
+
+ gridLocation.x = (int) (start.x / gridSpacing.x);
+ gridLocation.y = (int) (start.z / gridSpacing.z);
+
+ Vector3f ooDirection = new Vector3f(1.0f / direction.x, 1,1.0f / direction.z);
+
+ // Check which direction on the X world axis we are moving.
+ if (direction.x > TOLERANCE) {
+ distToNextXIntersection = ((gridLocation.x + 1) * gridSpacing.x - start.x) * ooDirection.x;
+ distBetweenXIntersections = gridSpacing.x * ooDirection.x;
+ stepXDirection = 1;
+ } else if (direction.x < -TOLERANCE) {
+ distToNextXIntersection = (start.x - (gridLocation.x * gridSpacing.x)) * -direction.x;
+ distBetweenXIntersections = -gridSpacing.x * ooDirection.x;
+ stepXDirection = -1;
+ } else {
+ distToNextXIntersection = Float.MAX_VALUE;
+ distBetweenXIntersections = Float.MAX_VALUE;
+ stepXDirection = 0;
+ }
+
+ // Check which direction on the Z world axis we are moving.
+ if (direction.z > TOLERANCE) {
+ distToNextZIntersection = ((gridLocation.y + 1) * gridSpacing.z - start.z) * ooDirection.z;
+ distBetweenZIntersections = gridSpacing.z * ooDirection.z;
+ stepZDirection = 1;
+ } else if (direction.z < -TOLERANCE) {
+ distToNextZIntersection = (start.z - (gridLocation.y * gridSpacing.z)) * -direction.z;
+ distBetweenZIntersections = -gridSpacing.z * ooDirection.z;
+ stepZDirection = -1;
+ } else {
+ distToNextZIntersection = Float.MAX_VALUE;
+ distBetweenZIntersections = Float.MAX_VALUE;
+ stepZDirection = 0;
+ }
+
+ // Reset some variables
+ rayLocation.set(start);
+ rayLength = 0.0f;
+ stepDirection = Direction.None;
+ }
+
+ public void next() {
+ // Walk us to our next location based on distances to next X or Z grid
+ // line.
+ if (distToNextXIntersection < distToNextZIntersection) {
+ rayLength = distToNextXIntersection;
+ gridLocation.x += stepXDirection;
+ distToNextXIntersection += distBetweenXIntersections;
+ switch (stepXDirection) {
+ case -1:
+ stepDirection = Direction.NegativeX;
+ break;
+ case 0:
+ stepDirection = Direction.None;
+ break;
+ case 1:
+ stepDirection = Direction.PositiveX;
+ break;
+ }
+ } else {
+ rayLength = distToNextZIntersection;
+ gridLocation.y += stepZDirection;
+ distToNextZIntersection += distBetweenZIntersections;
+ switch (stepZDirection) {
+ case -1:
+ stepDirection = Direction.NegativeZ;
+ break;
+ case 0:
+ stepDirection = Direction.None;
+ break;
+ case 1:
+ stepDirection = Direction.PositiveZ;
+ break;
+ }
+ }
+
+ rayLocation.set(walkRay.direction).multLocal(rayLength).addLocal(walkRay.origin);
+ }
+
+ public Direction getLastStepDirection() {
+ return stepDirection;
+ }
+
+ public boolean isRayPerpendicularToGrid() {
+ return stepXDirection == 0 && stepZDirection == 0;
+ }
+
+
+ public Vector2f getGridLocation() {
+ return gridLocation;
+ }
+
+ public Vector3f getGridOrigin() {
+ return gridOrigin;
+ }
+
+ public Vector3f getGridSpacing() {
+ return gridSpacing;
+ }
+
+
+ public void setGridLocation(Vector2f gridLocation) {
+ this.gridLocation = gridLocation;
+ }
+
+ public void setGridOrigin(Vector3f gridOrigin) {
+ this.gridOrigin = gridOrigin;
+ }
+
+ public void setGridSpacing(Vector3f gridSpacing) {
+ this.gridSpacing = gridSpacing;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPickData.java b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPickData.java
new file mode 100644
index 0000000..bee7623
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPickData.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.picking;
+
+import com.jme3.collision.CollisionResult;
+import com.jme3.terrain.geomipmap.TerrainPatch;
+
+/**
+ * Pick result on a terrain patch with the intersection on the bounding box
+ * of that terrain patch.
+ *
+ * @author Brent Owens
+ */
+public class TerrainPickData implements Comparable {
+
+ protected TerrainPatch targetPatch;
+ protected CollisionResult cr;
+
+ public TerrainPickData() {
+ }
+
+ public TerrainPickData(TerrainPatch patch, CollisionResult cr) {
+ this.targetPatch = patch;
+ this.cr = cr;
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof TerrainPickData) {
+ TerrainPickData tpd = (TerrainPickData) o;
+ if (this.cr.getDistance() < tpd.cr.getDistance())
+ return -1;
+ else if (this.cr.getDistance() == tpd.cr.getDistance())
+ return 0;
+ else
+ return 1;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof TerrainPickData){
+ return ((TerrainPickData)obj).compareTo(this) == 0;
+ }
+ return super.equals(obj);
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPicker.java b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPicker.java
new file mode 100644
index 0000000..67df8e7
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPicker.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.geomipmap.picking;
+
+import com.jme3.collision.CollisionResults;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+
+/**
+ * Pick the location on the terrain from a given ray.
+ *
+ * @author Brent Owens
+ */
+public interface TerrainPicker {
+
+ /**
+ * Ask for the point of intersection between the given ray and the terrain.
+ *
+ * @param worldPick
+ * our pick ray, in world space.
+ * @return null if no pick is found. Otherwise it returns a Vector3f populated with the pick
+ * coordinates.
+ */
+ public Vector3f getTerrainIntersection(final Ray worldPick, CollisionResults results);
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java
new file mode 100644
index 0000000..3193f52
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>AbstractHeightMap</code> provides a base implementation of height
+ * data for terrain rendering. The loading of the data is dependent on the
+ * subclass. The abstract implementation provides a means to retrieve the height
+ * data and to save it.
+ *
+ * It is the general contract that any subclass provide a means of editing
+ * required attributes and calling <code>load</code> again to recreate a
+ * heightfield with these new parameters.
+ *
+ * @author Mark Powell
+ * @version $Id: AbstractHeightMap.java 4133 2009-03-19 20:40:11Z blaine.dev $
+ */
+public abstract class AbstractHeightMap implements HeightMap {
+
+ private static final Logger logger = Logger.getLogger(AbstractHeightMap.class.getName());
+ /** Height data information. */
+ protected float[] heightData = null;
+ /** The size of the height map's width. */
+ protected int size = 0;
+ /** Allows scaling the Y height of the map. */
+ protected float heightScale = 1.0f;
+ /** The filter is used to erode the terrain. */
+ protected float filter = 0.5f;
+ /** The range used to normalize terrain */
+ public static float NORMALIZE_RANGE = 255f;
+
+ /**
+ * <code>unloadHeightMap</code> clears the data of the height map. This
+ * insures it is ready for reloading.
+ */
+ public void unloadHeightMap() {
+ heightData = null;
+ }
+
+ /**
+ * <code>setHeightScale</code> sets the scale of the height values.
+ * Typically, the height is a little too extreme and should be scaled to a
+ * smaller value (i.e. 0.25), to produce cleaner slopes.
+ *
+ * @param scale
+ * the scale to multiply height values by.
+ */
+ public void setHeightScale(float scale) {
+ heightScale = scale;
+ }
+
+ /**
+ * <code>setHeightAtPoint</code> sets the height value for a given
+ * coordinate. It is recommended that the height value be within the 0 - 255
+ * range.
+ *
+ * @param height
+ * the new height for the coordinate.
+ * @param x
+ * the x (east/west) coordinate.
+ * @param z
+ * the z (north/south) coordinate.
+ */
+ public void setHeightAtPoint(float height, int x, int z) {
+ heightData[x + (z * size)] = height;
+ }
+
+ /**
+ * <code>setSize</code> sets the size of the terrain where the area is
+ * size x size.
+ *
+ * @param size
+ * the new size of the terrain.
+ * @throws Exception
+ *
+ * @throws JmeException
+ * if the size is less than or equal to zero.
+ */
+ public void setSize(int size) throws Exception {
+ if (size <= 0) {
+ throw new Exception("size must be greater than zero.");
+ }
+
+ this.size = size;
+ }
+
+ /**
+ * <code>setFilter</code> sets the erosion value for the filter. This
+ * value must be between 0 and 1, where 0.2 - 0.4 produces arguably the best
+ * results.
+ *
+ * @param filter
+ * the erosion value.
+ * @throws Exception
+ * @throws JmeException
+ * if filter is less than 0 or greater than 1.
+ */
+ public void setMagnificationFilter(float filter) throws Exception {
+ if (filter < 0 || filter >= 1) {
+ throw new Exception("filter must be between 0 and 1");
+ }
+ this.filter = filter;
+ }
+
+ /**
+ * <code>getTrueHeightAtPoint</code> returns the non-scaled value at the
+ * point provided.
+ *
+ * @param x
+ * the x (east/west) coordinate.
+ * @param z
+ * the z (north/south) coordinate.
+ * @return the value at (x,z).
+ */
+ public float getTrueHeightAtPoint(int x, int z) {
+ //logger.info( heightData[x + (z*size)]);
+ return heightData[x + (z * size)];
+ }
+
+ /**
+ * <code>getScaledHeightAtPoint</code> returns the scaled value at the
+ * point provided.
+ *
+ * @param x
+ * the x (east/west) coordinate.
+ * @param z
+ * the z (north/south) coordinate.
+ * @return the scaled value at (x, z).
+ */
+ public float getScaledHeightAtPoint(int x, int z) {
+ return ((heightData[x + (z * size)]) * heightScale);
+ }
+
+ /**
+ * <code>getInterpolatedHeight</code> returns the height of a point that
+ * does not fall directly on the height posts.
+ *
+ * @param x
+ * the x coordinate of the point.
+ * @param z
+ * the y coordinate of the point.
+ * @return the interpolated height at this point.
+ */
+ public float getInterpolatedHeight(float x, float z) {
+ float low, highX, highZ;
+ float intX, intZ;
+ float interpolation;
+
+ low = getScaledHeightAtPoint((int) x, (int) z);
+
+ if (x + 1 >= size) {
+ return low;
+ }
+
+ highX = getScaledHeightAtPoint((int) x + 1, (int) z);
+
+ interpolation = x - (int) x;
+ intX = ((highX - low) * interpolation) + low;
+
+ if (z + 1 >= size) {
+ return low;
+ }
+
+ highZ = getScaledHeightAtPoint((int) x, (int) z + 1);
+
+ interpolation = z - (int) z;
+ intZ = ((highZ - low) * interpolation) + low;
+
+ return ((intX + intZ) / 2);
+ }
+
+ /**
+ * <code>getHeightMap</code> returns the entire grid of height data.
+ *
+ * @return the grid of height data.
+ */
+ public float[] getHeightMap() {
+ return heightData;
+ }
+
+ /**
+ * Build a new array of height data with the scaled values.
+ * @return
+ */
+ public float[] getScaledHeightMap() {
+ float[] hm = new float[heightData.length];
+ for (int i=0; i<heightData.length; i++) {
+ hm[i] = heightScale * heightData[i];
+ }
+ return hm;
+ }
+
+ /**
+ * <code>getSize</code> returns the size of one side the height map. Where
+ * the area of the height map is size x size.
+ *
+ * @return the size of a single side.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * <code>save</code> will save the heightmap data into a new RAW file
+ * denoted by the supplied filename.
+ *
+ * @param filename
+ * the file name to save the current data as.
+ * @return true if the save was successful, false otherwise.
+ * @throws Exception
+ *
+ * @throws JmeException
+ * if filename is null.
+ */
+ public boolean save(String filename) throws Exception {
+ if (null == filename) {
+ throw new Exception("Filename must not be null");
+ }
+ //open the streams and send the height data to the file.
+ try {
+ FileOutputStream fos = new FileOutputStream(filename);
+ DataOutputStream dos = new DataOutputStream(fos);
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ dos.write((int) heightData[j + (i * size)]);
+ }
+ }
+
+ fos.close();
+ dos.close();
+ } catch (FileNotFoundException e) {
+ logger.log(Level.WARNING, "Error opening file {0}", filename);
+ return false;
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error writing to file {0}", filename);
+ return false;
+ }
+
+ logger.log(Level.INFO, "Saved terrain to {0}", filename);
+ return true;
+ }
+
+ /**
+ * <code>normalizeTerrain</code> takes the current terrain data and
+ * converts it to values between 0 and <code>value</code>.
+ *
+ * @param value
+ * the value to normalize to.
+ */
+ public void normalizeTerrain(float value) {
+ float currentMin, currentMax;
+ float height;
+
+ currentMin = heightData[0];
+ currentMax = heightData[0];
+
+ //find the min/max values of the height fTemptemptempBuffer
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ if (heightData[i + j * size] > currentMax) {
+ currentMax = heightData[i + j * size];
+ } else if (heightData[i + j * size] < currentMin) {
+ currentMin = heightData[i + j * size];
+ }
+ }
+ }
+
+ //find the range of the altitude
+ if (currentMax <= currentMin) {
+ return;
+ }
+
+ height = currentMax - currentMin;
+
+ //scale the values to a range of 0-255
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ heightData[i + j * size] = ((heightData[i + j * size] - currentMin) / height) * value;
+ }
+ }
+ }
+
+ /**
+ * Find the minimum and maximum height values.
+ * @return a float array with two value: min height, max height
+ */
+ public float[] findMinMaxHeights() {
+ float[] minmax = new float[2];
+
+ float currentMin, currentMax;
+ currentMin = heightData[0];
+ currentMax = heightData[0];
+
+ for (int i = 0; i < heightData.length; i++) {
+ if (heightData[i] > currentMax) {
+ currentMax = heightData[i];
+ } else if (heightData[i] < currentMin) {
+ currentMin = heightData[i];
+ }
+ }
+ minmax[0] = currentMin;
+ minmax[1] = currentMax;
+ return minmax;
+ }
+
+ /**
+ * <code>erodeTerrain</code> is a convenience method that applies the FIR
+ * filter to a given height map. This simulates water errosion.
+ *
+ * @see setFilter
+ */
+ public void erodeTerrain() {
+ //erode left to right
+ float v;
+
+ for (int i = 0; i < size; i++) {
+ v = heightData[i];
+ for (int j = 1; j < size; j++) {
+ heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
+ v = heightData[i + j * size];
+ }
+ }
+
+ //erode right to left
+ for (int i = size - 1; i >= 0; i--) {
+ v = heightData[i];
+ for (int j = 0; j < size; j++) {
+ heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
+ v = heightData[i + j * size];
+ //erodeBand(tempBuffer[size * i + size - 1], -1);
+ }
+ }
+
+ //erode top to bottom
+ for (int i = 0; i < size; i++) {
+ v = heightData[i];
+ for (int j = 0; j < size; j++) {
+ heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
+ v = heightData[i + j * size];
+ }
+ }
+
+ //erode from bottom to top
+ for (int i = size - 1; i >= 0; i--) {
+ v = heightData[i];
+ for (int j = 0; j < size; j++) {
+ heightData[i + j * size] = filter * v + (1 - filter) * heightData[i + j * size];
+ v = heightData[i + j * size];
+ }
+ }
+ }
+
+ /**
+ * Flattens out the valleys. The flatten algorithm makes the valleys more
+ * prominent while keeping the hills mostly intact. This effect is based on
+ * what happens when values below one are squared. The terrain will be
+ * normalized between 0 and 1 for this function to work.
+ *
+ * @param flattening
+ * the power of flattening applied, 1 means none
+ */
+ public void flatten(byte flattening) {
+ // If flattening is one we can skip the calculations
+ // since it wouldn't change anything. (e.g. 2 power 1 = 2)
+ if (flattening <= 1) {
+ return;
+ }
+
+ float[] minmax = findMinMaxHeights();
+
+ normalizeTerrain(1f);
+
+ for (int x = 0; x < size; x++) {
+ for (int y = 0; y < size; y++) {
+ float flat = 1.0f;
+ float original = heightData[x + y * size];
+
+ // Flatten as many times as desired;
+ for (int i = 0; i < flattening; i++) {
+ flat *= original;
+ }
+ heightData[x + y * size] = flat;
+ }
+ }
+
+ // re-normalize back to its oraginal height range
+ float height = minmax[1] - minmax[0];
+ normalizeTerrain(height);
+ }
+
+ /**
+ * Smooth the terrain. For each node, its 8 neighbors heights
+ * are averaged and will participate in the node new height
+ * by a factor <code>np</code> between 0 and 1
+ *
+ * You must first load() the heightmap data before this will have any effect.
+ *
+ * @param np
+ * The factor to what extend the neighbors average has an influence.
+ * Value of 0 will ignore neighbors (no smoothing)
+ * Value of 1 will ignore the node old height.
+ */
+ public void smooth(float np) {
+ smooth(np, 1);
+ }
+
+ /**
+ * Smooth the terrain. For each node, its X(determined by radius) neighbors heights
+ * are averaged and will participate in the node new height
+ * by a factor <code>np</code> between 0 and 1
+ *
+ * You must first load() the heightmap data before this will have any effect.
+ *
+ * @param np
+ * The factor to what extend the neighbors average has an influence.
+ * Value of 0 will ignore neighbors (no smoothing)
+ * Value of 1 will ignore the node old height.
+ */
+ public void smooth(float np, int radius) {
+ if (np < 0 || np > 1) {
+ return;
+ }
+ if (radius == 0)
+ radius = 1;
+
+ for (int x = 0; x < size; x++) {
+ for (int y = 0; y < size; y++) {
+ int neighNumber = 0;
+ float neighAverage = 0;
+ for (int rx = -radius; rx <= radius; rx++) {
+ for (int ry = -radius; ry <= radius; ry++) {
+ if (x+rx < 0 || x+rx >= size) {
+ continue;
+ }
+ if (y+ry < 0 || y+ry >= size) {
+ continue;
+ }
+ neighNumber++;
+ neighAverage += heightData[(x+rx) + (y+ry) * size];
+ }
+ }
+
+ neighAverage /= neighNumber;
+ float cp = 1 - np;
+ heightData[x + y * size] = neighAverage * np + heightData[x + y * size] * cp;
+ }
+ }
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/CombinerHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/CombinerHeightMap.java
new file mode 100644
index 0000000..7a25834
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/CombinerHeightMap.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import java.util.logging.Logger;
+
+/**
+ * <code>CombinerHeightMap</code> generates a new height map based on
+ * two provided height maps. These had maps can either be added together
+ * or substracted from each other. Each heightmap has a weight to
+ * determine how much one will affect the other. By default it is set to
+ * 0.5, 0.5 and meaning the two heightmaps are averaged evenly. This
+ * value can be adjusted at will, as long as the two factors are equal
+ * to 1.0.
+ *
+ * @author Mark Powell
+ * @version $Id$
+ */
+public class CombinerHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(CombinerHeightMap.class.getName());
+ /**
+ * Constant mode to denote adding the two heightmaps.
+ */
+ public static final int ADDITION = 0;
+ /**
+ * Constant mode to denote subtracting the two heightmaps.
+ */
+ public static final int SUBTRACTION = 1;
+ //the two maps.
+ private AbstractHeightMap map1;
+ private AbstractHeightMap map2;
+ //the two factors
+ private float factor1 = 0.5f;
+ private float factor2 = 0.5f;
+ //the combine mode.
+ private int mode;
+
+ /**
+ * Constructor combines two given heightmaps by the specified mode.
+ * The heightmaps will be evenly distributed. The heightmaps
+ * must be of the same size.
+ *
+ * @param map1 the first heightmap to combine.
+ * @param map2 the second heightmap to combine.
+ * @param mode denotes whether to add or subtract the heightmaps, may
+ * be either ADDITION or SUBTRACTION.
+ * @throws JmeException if either map is null, their size
+ * do not match or the mode is invalid.
+ */
+ public CombinerHeightMap(
+ AbstractHeightMap map1,
+ AbstractHeightMap map2,
+ int mode) throws Exception {
+
+
+ //insure all parameters are valid.
+ if (null == map1 || null == map2) {
+ throw new Exception("Height map may not be null");
+ }
+
+
+ if (map1.getSize() != map2.getSize()) {
+ throw new Exception("The two maps must be of the same size");
+ }
+
+
+ if ((factor1 + factor2) != 1.0f) {
+ throw new Exception("factor1 and factor2 must add to 1.0");
+ }
+
+
+ this.size = map1.getSize();
+ this.map1 = map1;
+ this.map2 = map2;
+
+
+ setMode(mode);
+
+ load();
+ }
+
+ /**
+ * Constructor combines two given heightmaps by the specified mode.
+ * The heightmaps will be distributed based on the given factors.
+ * For example, if factor1 is 0.6 and factor2 is 0.4, then 60% of
+ * map1 will be used with 40% of map2. The two factors must add up
+ * to 1.0. The heightmaps must also be of the same size.
+ *
+ * @param map1 the first heightmap to combine.
+ * @param factor1 the factor for map1.
+ * @param map2 the second heightmap to combine.
+ * @param factor2 the factor for map2.
+ * @param mode denotes whether to add or subtract the heightmaps, may
+ * be either ADDITION or SUBTRACTION.
+ * @throws JmeException if either map is null, their size
+ * do not match, the mode is invalid, or the factors do not add
+ * to 1.0.
+ */
+ public CombinerHeightMap(
+ AbstractHeightMap map1,
+ float factor1,
+ AbstractHeightMap map2,
+ float factor2,
+ int mode) throws Exception {
+
+
+ //insure all parameters are valid.
+ if (null == map1 || null == map2) {
+ throw new Exception("Height map may not be null");
+ }
+
+
+ if (map1.getSize() != map2.getSize()) {
+ throw new Exception("The two maps must be of the same size");
+ }
+
+
+ if ((factor1 + factor2) != 1.0f) {
+ throw new Exception("factor1 and factor2 must add to 1.0");
+ }
+
+
+ setMode(mode);
+
+
+ this.size = map1.getSize();
+ this.map1 = map1;
+ this.map2 = map2;
+ this.factor1 = factor1;
+ this.factor2 = factor2;
+
+
+ this.mode = mode;
+
+
+ load();
+ }
+
+ /**
+ * <code>setFactors</code> sets the distribution of heightmaps.
+ * For example, if factor1 is 0.6 and factor2 is 0.4, then 60% of
+ * map1 will be used with 40% of map2. The two factors must add up
+ * to 1.0.
+ * @param factor1 the factor for map1.
+ * @param factor2 the factor for map2.
+ * @throws JmeException if the factors do not add to 1.0.
+ */
+ public void setFactors(float factor1, float factor2) throws Exception {
+ if ((factor1 + factor2) != 1.0f) {
+ throw new Exception("factor1 and factor2 must add to 1.0");
+ }
+
+
+ this.factor1 = factor1;
+ this.factor2 = factor2;
+ }
+
+ /**
+ * <code>setHeightMaps</code> sets the height maps to combine.
+ * The size of the height maps must be the same.
+ * @param map1 the first height map.
+ * @param map2 the second height map.
+ * @throws JmeException if the either heightmap is null, or their
+ * sizes do not match.
+ */
+ public void setHeightMaps(AbstractHeightMap map1, AbstractHeightMap map2) throws Exception {
+ if (null == map1 || null == map2) {
+ throw new Exception("Height map may not be null");
+ }
+
+
+ if (map1.getSize() != map2.getSize()) {
+ throw new Exception("The two maps must be of the same size");
+ }
+
+
+ this.size = map1.getSize();
+ this.map1 = map1;
+ this.map2 = map2;
+ }
+
+ /**
+ * <code>setMode</code> sets the mode of the combiner. This may either
+ * be ADDITION or SUBTRACTION.
+ * @param mode the mode of the combiner.
+ * @throws JmeException if mode is not ADDITION or SUBTRACTION.
+ */
+ public void setMode(int mode) throws Exception {
+ if (mode != ADDITION && mode != SUBTRACTION) {
+ throw new Exception("Invalid mode");
+ }
+ this.mode = mode;
+ }
+
+ /**
+ * <code>load</code> builds a new heightmap based on the combination of
+ * two other heightmaps. The conditions of the combiner determine the
+ * final outcome of the heightmap.
+ *
+ * @return boolean if the heightmap was successfully created.
+ */
+ public boolean load() {
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+
+
+ heightData = new float[size * size];
+
+
+ float[] temp1 = map1.getHeightMap();
+ float[] temp2 = map2.getHeightMap();
+
+
+ if (mode == ADDITION) {
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ heightData[i + (j * size)] =
+ (int) (temp1[i + (j * size)] * factor1
+ + temp2[i + (j * size)] * factor2);
+ }
+ }
+ } else if (mode == SUBTRACTION) {
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ heightData[i + (j * size)] =
+ (int) (temp1[i + (j * size)] * factor1
+ - temp2[i + (j * size)] * factor2);
+ }
+ }
+ }
+
+
+ logger.info("Created heightmap using Combiner");
+
+
+ return true;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/FaultHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/FaultHeightMap.java
new file mode 100644
index 0000000..194486a
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/FaultHeightMap.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import com.jme3.math.FastMath;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Creates an heightmap based on the fault algorithm. Each iteration, a random line
+ * crossing the map is generated. On one side height values are raised, on the other side
+ * lowered.
+ * @author cghislai
+ */
+public class FaultHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(FaultHeightMap.class.getName());
+ /**
+ * Values on one side are lowered, on the other side increased,
+ * creating a step at the fault line
+ */
+ public static final int FAULTTYPE_STEP = 0;
+ /**
+ * Values on one side are lowered, then increase lineary while crossing
+ * the fault line to the other side. The fault line will be a inclined
+ * plane
+ */
+ public static final int FAULTTYPE_LINEAR = 1;
+ /**
+ * Values are lowered on one side, increased on the other, creating a
+ * cosine curve on the fault line
+ */
+ public static final int FAULTTYPE_COSINE = 2;
+ /**
+ * Value are lowered on both side, but increased on the fault line
+ * creating a smooth ridge on the fault line.
+ */
+ public static final int FAULTTYPE_SINE = 3;
+ /**
+ * A linear fault is created
+ */
+ public static final int FAULTSHAPE_LINE = 10;
+ /**
+ * A circular fault is created.
+ */
+ public static final int FAULTSHAPE_CIRCLE = 11;
+ private long seed; // A seed to feed the random
+ private int iterations; // iterations to perform
+ private float minFaultHeight; // the height modification applied
+ private float maxFaultHeight; // the height modification applied
+ private float minRange; // The range for linear and trigo faults
+ private float maxRange; // The range for linear and trigo faults
+ private float minRadius; // radii for circular fault
+ private float maxRadius;
+ private int faultType; // The type of fault
+ private int faultShape; // The type of fault
+
+ /**
+ * Constructor creates the fault. For faulttype other than STEP, a range can
+ * be provided. For faultshape circle, min and max radii can be provided.
+ * Don't forget to reload the map if you have set parameters after the constructor
+ * call.
+ * @param size The size of the heightmap
+ * @param iterations Iterations to perform
+ * @param faultType Type of fault
+ * @param faultShape Shape of the fault -line or circle
+ * @param minFaultHeight Height modified on each side
+ * @param maxFaultHeight Height modified on each side
+ * @param seed A seed to feed the Random generator
+ * @see setFaultRange, setMinRadius, setMaxRadius
+ */
+ public FaultHeightMap(int size, int iterations, int faultType, int faultShape, float minFaultHeight, float maxFaultHeight, long seed) throws Exception {
+ if (size < 0 || iterations < 0) {
+ throw new Exception("Size and iterations must be greater than 0!");
+ }
+ this.size = size;
+ this.iterations = iterations;
+ this.faultType = faultType;
+ this.faultShape = faultShape;
+ this.minFaultHeight = minFaultHeight;
+ this.maxFaultHeight = maxFaultHeight;
+ this.seed = seed;
+ this.minRange = minFaultHeight;
+ this.maxRange = maxFaultHeight;
+ this.minRadius = size / 10;
+ this.maxRadius = size / 4;
+ load();
+ }
+
+ /**
+ * Create an heightmap with linear step faults.
+ * @param size size of heightmap
+ * @param iterations number of iterations
+ * @param minFaultHeight Height modified on each side
+ * @param maxFaultHeight Height modified on each side
+ */
+ public FaultHeightMap(int size, int iterations, float minFaultHeight, float maxFaultHeight) throws Exception {
+ this(size, iterations, FAULTTYPE_STEP, FAULTSHAPE_LINE, minFaultHeight, maxFaultHeight, new Random().nextLong());
+ }
+
+ @Override
+ public boolean load() {
+ // clean up data if needed.
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+ heightData = new float[size * size];
+ float[][] tempBuffer = new float[size][size];
+ Random random = new Random(seed);
+
+ for (int i = 0; i < iterations; i++) {
+ addFault(tempBuffer, random);
+ }
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ setHeightAtPoint(tempBuffer[i][j], i, j);
+ }
+ }
+
+ normalizeTerrain(NORMALIZE_RANGE);
+
+ logger.log(Level.INFO, "Fault heightmap generated");
+ return true;
+ }
+
+ protected void addFault(float[][] tempBuffer, Random random) {
+ float faultHeight = minFaultHeight + random.nextFloat() * (maxFaultHeight - minFaultHeight);
+ float range = minRange + random.nextFloat() * (maxRange - minRange);
+ switch (faultShape) {
+ case FAULTSHAPE_LINE:
+ addLineFault(tempBuffer, random, faultHeight, range);
+ break;
+ case FAULTSHAPE_CIRCLE:
+ addCircleFault(tempBuffer, random, faultHeight, range);
+ break;
+ }
+ }
+
+ protected void addLineFault(float[][] tempBuffer, Random random, float faultHeight, float range) {
+ int x1 = random.nextInt(size);
+ int x2 = random.nextInt(size);
+ int y1 = random.nextInt(size);
+ int y2 = random.nextInt(size);
+
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ float dist = ((x2 - x1) * (j - y1) - (y2 - y1) * (i - x1))
+ / (FastMath.sqrt(FastMath.sqr(x2 - x1) + FastMath.sqr(y2 - y1)));
+ tempBuffer[i][j] += calcHeight(dist, random, faultHeight, range);
+ }
+ }
+ }
+
+ protected void addCircleFault(float[][] tempBuffer, Random random, float faultHeight, float range) {
+ float radius = random.nextFloat() * (maxRadius - minRadius) + minRadius;
+ int intRadius = (int) FastMath.floor(radius);
+ // Allox circle center to be out of map if not by more than radius.
+ // Unlucky cases will put them in the far corner, with the circle
+ // entirely outside heightmap
+ int x = random.nextInt(size + 2 * intRadius) - intRadius;
+ int y = random.nextInt(size + 2 * intRadius) - intRadius;
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ float dist;
+ if (i != x || j != y) {
+ int dx = i - x;
+ int dy = j - y;
+ float dmag = FastMath.sqrt(FastMath.sqr(dx) + FastMath.sqr(dy));
+ float rx = x + dx / dmag * radius;
+ float ry = y + dy / dmag * radius;
+ dist = FastMath.sign(dmag - radius)
+ * FastMath.sqrt(FastMath.sqr(i - rx) + FastMath.sqr(j - ry));
+ } else {
+ dist = 0;
+ }
+ tempBuffer[i][j] += calcHeight(dist, random, faultHeight, range);
+ }
+ }
+ }
+
+ protected float calcHeight(float dist, Random random, float faultHeight, float range) {
+ switch (faultType) {
+ case FAULTTYPE_STEP: {
+ return FastMath.sign(dist) * faultHeight;
+ }
+ case FAULTTYPE_LINEAR: {
+ if (FastMath.abs(dist) > range) {
+ return FastMath.sign(dist) * faultHeight;
+ }
+ float f = FastMath.abs(dist) / range;
+ return FastMath.sign(dist) * faultHeight * f;
+ }
+ case FAULTTYPE_SINE: {
+ if (FastMath.abs(dist) > range) {
+ return -faultHeight;
+ }
+ float f = dist / range;
+ // We want -1 at f=-1 and f=1; 1 at f=0
+ return FastMath.sin((1 + 2 * f) * FastMath.PI / 2) * faultHeight;
+ }
+ case FAULTTYPE_COSINE: {
+ if (FastMath.abs(dist) > range) {
+ return -FastMath.sign(dist) * faultHeight;
+ }
+ float f = dist / range;
+ float val = FastMath.cos((1 + f) * FastMath.PI / 2) * faultHeight;
+ return val;
+ }
+ }
+ //shoudn't go here
+ throw new RuntimeException("Code needs update to switch allcases");
+ }
+
+ public int getFaultShape() {
+ return faultShape;
+ }
+
+ public void setFaultShape(int faultShape) {
+ this.faultShape = faultShape;
+ }
+
+ public int getFaultType() {
+ return faultType;
+ }
+
+ public void setFaultType(int faultType) {
+ this.faultType = faultType;
+ }
+
+ public int getIterations() {
+ return iterations;
+ }
+
+ public void setIterations(int iterations) {
+ this.iterations = iterations;
+ }
+
+ public float getMaxFaultHeight() {
+ return maxFaultHeight;
+ }
+
+ public void setMaxFaultHeight(float maxFaultHeight) {
+ this.maxFaultHeight = maxFaultHeight;
+ }
+
+ public float getMaxRadius() {
+ return maxRadius;
+ }
+
+ public void setMaxRadius(float maxRadius) {
+ this.maxRadius = maxRadius;
+ }
+
+ public float getMaxRange() {
+ return maxRange;
+ }
+
+ public void setMaxRange(float maxRange) {
+ this.maxRange = maxRange;
+ }
+
+ public float getMinFaultHeight() {
+ return minFaultHeight;
+ }
+
+ public void setMinFaultHeight(float minFaultHeight) {
+ this.minFaultHeight = minFaultHeight;
+ }
+
+ public float getMinRadius() {
+ return minRadius;
+ }
+
+ public void setMinRadius(float minRadius) {
+ this.minRadius = minRadius;
+ }
+
+ public float getMinRange() {
+ return minRange;
+ }
+
+ public void setMinRange(float minRange) {
+ this.minRange = minRange;
+ }
+
+ public long getSeed() {
+ return seed;
+ }
+
+ public void setSeed(long seed) {
+ this.seed = seed;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/FluidSimHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/FluidSimHeightMap.java
new file mode 100644
index 0000000..ec2dfdf
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/FluidSimHeightMap.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import java.util.Random;
+import java.util.logging.Logger;
+
+/**
+ * <code>FluidSimHeightMap</code> generates a height map based using some
+ * sort of fluid simulation. The heightmap is treated as a highly viscous and
+ * rubbery fluid enabling to fine tune the generated heightmap using a number
+ * of parameters.
+ *
+ * @author Frederik Boelthoff
+ * @see <a href="http://www.gamedev.net/reference/articles/article2001.asp">Terrain Generation Using Fluid Simulation</a>
+ * @version $Id$
+ *
+ */
+public class FluidSimHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(FluidSimHeightMap.class.getName());
+ private float waveSpeed = 100.0f; // speed at which the waves travel
+ private float timeStep = 0.033f; // constant time-step between each iteration
+ private float nodeDistance = 10.0f; // distance between each node of the surface
+ private float viscosity = 100.0f; // viscosity of the fluid
+ private int iterations; // number of iterations
+ private float minInitialHeight = -500; // min initial height
+ private float maxInitialHeight = 500; // max initial height
+ private long seed; // the seed for the random number generator
+ float coefA, coefB, coefC; // pre-computed coefficients in the fluid equation
+
+ /**
+ * Constructor sets the attributes of the hill system and generates the
+ * height map. It gets passed a number of tweakable parameters which
+ * fine-tune the outcome.
+ *
+ * @param size
+ * size the size of the terrain to be generated
+ * @param iterations
+ * the number of iterations to do
+ * @param minInitialHeight
+ * the minimum initial height of a terrain value
+ * @param maxInitialHeight
+ * the maximum initial height of a terrain value
+ * @param viscosity
+ * the viscosity of the fluid
+ * @param waveSpeed
+ * the speed at which the waves travel
+ * @param timestep
+ * the constant time-step between each iteration
+ * @param nodeDistance
+ * the distance between each node of the heightmap
+ * @param seed
+ * the seed to generate the same heightmap again
+ * @throws JmeException
+ * if size of the terrain is not greater that zero, or number of
+ * iterations is not greater that zero, or the minimum initial height
+ * is greater than the maximum (or the other way around)
+ */
+ public FluidSimHeightMap(int size, int iterations, float minInitialHeight, float maxInitialHeight, float viscosity, float waveSpeed, float timestep, float nodeDistance, long seed) throws Exception {
+ if (size <= 0 || iterations <= 0 || minInitialHeight >= maxInitialHeight) {
+ throw new Exception(
+ "Either size of the terrain is not greater that zero, "
+ + "or number of iterations is not greater that zero, "
+ + "or minimum height greater or equal as the maximum, "
+ + "or maximum height smaller or equal as the minimum.");
+ }
+
+ this.size = size;
+ this.seed = seed;
+ this.iterations = iterations;
+ this.minInitialHeight = minInitialHeight;
+ this.maxInitialHeight = maxInitialHeight;
+ this.viscosity = viscosity;
+ this.waveSpeed = waveSpeed;
+ this.timeStep = timestep;
+ this.nodeDistance = nodeDistance;
+
+ load();
+ }
+
+ /**
+ * Constructor sets the attributes of the hill system and generates the
+ * height map.
+ *
+ * @param size
+ * size the size of the terrain to be generated
+ * @param iterations
+ * the number of iterations to do
+ * @throws JmeException
+ * if size of the terrain is not greater that zero, or number of
+ * iterations is not greater that zero
+ */
+ public FluidSimHeightMap(int size, int iterations) throws Exception {
+ if (size <= 0 || iterations <= 0) {
+ throw new Exception(
+ "Either size of the terrain is not greater that zero, "
+ + "or number of iterations is not greater that zero");
+ }
+
+ this.size = size;
+ this.iterations = iterations;
+
+ load();
+ }
+
+
+ /*
+ * Generates a heightmap using fluid simulation and the attributes set by
+ * the constructor or the setters.
+ */
+ public boolean load() {
+ // Clean up data if needed.
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+
+ heightData = new float[size * size];
+ float[][] tempBuffer = new float[2][size * size];
+ Random random = new Random(seed);
+
+ // pre-compute the coefficients in the fluid equation
+ coefA = (4 - (8 * waveSpeed * waveSpeed * timeStep * timeStep) / (nodeDistance * nodeDistance)) / (viscosity * timeStep + 2);
+ coefB = (viscosity * timeStep - 2) / (viscosity * timeStep + 2);
+ coefC = ((2 * waveSpeed * waveSpeed * timeStep * timeStep) / (nodeDistance * nodeDistance)) / (viscosity * timeStep + 2);
+
+ // initialize the heightmaps to random values except for the edges
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ tempBuffer[0][j + i * size] = tempBuffer[1][j + i * size] = randomRange(random, minInitialHeight, maxInitialHeight);
+ }
+ }
+
+ int curBuf = 0;
+ int ind;
+
+ float[] oldBuffer;
+ float[] newBuffer;
+
+ // Iterate over the heightmap, applying the fluid simulation equation.
+ // Although it requires knowledge of the two previous timesteps, it only
+ // accesses one pixel of the k-1 timestep, so using a simple trick we only
+ // need to store the heightmap twice, not three times, and we can avoid
+ // copying data every iteration.
+ for (int i = 0; i < iterations; i++) {
+ oldBuffer = tempBuffer[1 - curBuf];
+ newBuffer = tempBuffer[curBuf];
+
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ ind = x + y * size;
+ float neighborsValue = 0;
+ int neighbors = 0;
+
+ if (x > 0) {
+ neighborsValue += newBuffer[ind - 1];
+ neighbors++;
+ }
+ if (x < size - 1) {
+ neighborsValue += newBuffer[ind + 1];
+ neighbors++;
+ }
+ if (y > 0) {
+ neighborsValue += newBuffer[ind - size];
+ neighbors++;
+ }
+ if (y < size - 1) {
+ neighborsValue += newBuffer[ind + size];
+ neighbors++;
+ }
+ if (neighbors != 4) {
+ neighborsValue *= 4 / neighbors;
+ }
+ oldBuffer[ind] = coefA * newBuffer[ind] + coefB
+ * oldBuffer[ind] + coefC * (neighborsValue);
+ }
+ }
+
+ curBuf = 1 - curBuf;
+ }
+
+ // put the normalized heightmap into the range [0...255] and into the heightmap
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ heightData[x + y * size] = (float) (tempBuffer[curBuf][x + y * size]);
+ }
+ }
+ normalizeTerrain(NORMALIZE_RANGE);
+
+ logger.info("Created Heightmap using fluid simulation");
+
+ return true;
+ }
+
+ private float randomRange(Random random, float min, float max) {
+ return (random.nextFloat() * (max - min)) + min;
+ }
+
+ /**
+ * Sets the number of times the fluid simulation should be iterated over
+ * the heightmap. The more often this is, the less features (hills, etc)
+ * the terrain will have, and the smoother it will be.
+ *
+ * @param iterations
+ * the number of iterations to do
+ * @throws JmeException
+ * if iterations if not greater than zero
+ */
+ public void setIterations(int iterations) throws Exception {
+ if (iterations <= 0) {
+ throw new Exception(
+ "Number of iterations is not greater than zero");
+ }
+ this.iterations = iterations;
+ }
+
+ /**
+ * Sets the maximum initial height of the terrain.
+ *
+ * @param maxInitialHeight
+ * the maximum initial height
+ * @see #setMinInitialHeight(int)
+ */
+ public void setMaxInitialHeight(float maxInitialHeight) {
+ this.maxInitialHeight = maxInitialHeight;
+ }
+
+ /**
+ * Sets the minimum initial height of the terrain.
+ *
+ * @param minInitialHeight
+ * the minimum initial height
+ * @see #setMaxInitialHeight(int)
+ */
+ public void setMinInitialHeight(float minInitialHeight) {
+ this.minInitialHeight = minInitialHeight;
+ }
+
+ /**
+ * Sets the distance between each node of the heightmap.
+ *
+ * @param nodeDistance
+ * the distance between each node
+ */
+ public void setNodeDistance(float nodeDistance) {
+ this.nodeDistance = nodeDistance;
+ }
+
+ /**
+ * Sets the time-speed between each iteration of the fluid
+ * simulation algortithm.
+ *
+ * @param timeStep
+ * the time-step between each iteration
+ */
+ public void setTimeStep(float timeStep) {
+ this.timeStep = timeStep;
+ }
+
+ /**
+ * Sets the viscosity of the simulated fuid.
+ *
+ * @param viscosity
+ * the viscosity of the fluid
+ */
+ public void setViscosity(float viscosity) {
+ this.viscosity = viscosity;
+ }
+
+ /**
+ * Sets the speed at which the waves trave.
+ *
+ * @param waveSpeed
+ * the speed at which the waves travel
+ */
+ public void setWaveSpeed(float waveSpeed) {
+ this.waveSpeed = waveSpeed;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/HeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/HeightMap.java
new file mode 100644
index 0000000..1bbed36
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/HeightMap.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.heightmap;
+
+/**
+ *
+ * @author cghislai
+ */
+public interface HeightMap {
+
+ /**
+ * <code>getHeightMap</code> returns the entire grid of height data.
+ *
+ * @return the grid of height data.
+ */
+ float[] getHeightMap();
+
+ float[] getScaledHeightMap();
+
+ /**
+ * <code>getInterpolatedHeight</code> returns the height of a point that
+ * does not fall directly on the height posts.
+ *
+ * @param x
+ * the x coordinate of the point.
+ * @param z
+ * the y coordinate of the point.
+ * @return the interpolated height at this point.
+ */
+ float getInterpolatedHeight(float x, float z);
+
+ /**
+ * <code>getScaledHeightAtPoint</code> returns the scaled value at the
+ * point provided.
+ *
+ * @param x
+ * the x (east/west) coordinate.
+ * @param z
+ * the z (north/south) coordinate.
+ * @return the scaled value at (x, z).
+ */
+ float getScaledHeightAtPoint(int x, int z);
+
+ /**
+ * <code>getSize</code> returns the size of one side the height map. Where
+ * the area of the height map is size x size.
+ *
+ * @return the size of a single side.
+ */
+ int getSize();
+
+ /**
+ * <code>getTrueHeightAtPoint</code> returns the non-scaled value at the
+ * point provided.
+ *
+ * @param x
+ * the x (east/west) coordinate.
+ * @param z
+ * the z (north/south) coordinate.
+ * @return the value at (x,z).
+ */
+ float getTrueHeightAtPoint(int x, int z);
+
+ /**
+ * <code>load</code> populates the height map data. This is dependent on
+ * the subclass's implementation.
+ *
+ * @return true if the load was successful, false otherwise.
+ */
+ boolean load();
+
+ /**
+ * <code>setHeightAtPoint</code> sets the height value for a given
+ * coordinate. It is recommended that the height value be within the 0 - 255
+ * range.
+ *
+ * @param height
+ * the new height for the coordinate.
+ * @param x
+ * the x (east/west) coordinate.
+ * @param z
+ * the z (north/south) coordinate.
+ */
+ void setHeightAtPoint(float height, int x, int z);
+
+ /**
+ * <code>setHeightScale</code> sets the scale of the height values.
+ * Typically, the height is a little too extreme and should be scaled to a
+ * smaller value (i.e. 0.25), to produce cleaner slopes.
+ *
+ * @param scale
+ * the scale to multiply height values by.
+ */
+ void setHeightScale(float scale);
+
+ /**
+ * <code>setFilter</code> sets the erosion value for the filter. This
+ * value must be between 0 and 1, where 0.2 - 0.4 produces arguably the best
+ * results.
+ *
+ * @param filter
+ * the erosion value.
+ * @throws Exception
+ * @throws JmeException
+ * if filter is less than 0 or greater than 1.
+ */
+ void setMagnificationFilter(float filter) throws Exception;
+
+ /**
+ * <code>setSize</code> sets the size of the terrain where the area is
+ * size x size.
+ *
+ * @param size
+ * the new size of the terrain.
+ * @throws Exception
+ *
+ * @throws JmeException
+ * if the size is less than or equal to zero.
+ */
+ void setSize(int size) throws Exception;
+
+ /**
+ * <code>unloadHeightMap</code> clears the data of the height map. This
+ * insures it is ready for reloading.
+ */
+ void unloadHeightMap();
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java b/engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java
new file mode 100644
index 0000000..8d59429
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java
@@ -0,0 +1,23 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.heightmap;
+
+import com.jme3.math.Vector3f;
+
+/**
+ *
+ * @author Anthyon
+ */
+@Deprecated
+/**
+ * @Deprecated in favor of TerrainGridTileLoader
+ */
+public interface HeightMapGrid {
+
+ public HeightMap getHeightMapAt(Vector3f location);
+
+ public void setSize(int size);
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/HillHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/HillHeightMap.java
new file mode 100644
index 0000000..038dcb2
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/HillHeightMap.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import java.util.Random;
+import java.util.logging.Logger;
+
+/**
+ * <code>HillHeightMap</code> generates a height map base on the Hill
+ * Algorithm. Terrain is generatd by growing hills of random size and height at
+ * random points in the heightmap. The terrain is then normalized and valleys
+ * can be flattened.
+ *
+ * @author Frederik Blthoff
+ * @see <a href="http://www.robot-frog.com/3d/hills/hill.html">Hill Algorithm</a>
+ */
+public class HillHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(HillHeightMap.class.getName());
+ private int iterations; // how many hills to generate
+ private float minRadius; // the minimum size of a hill radius
+ private float maxRadius; // the maximum size of a hill radius
+ private long seed; // the seed for the random number generator
+
+ /**
+ * Constructor sets the attributes of the hill system and generates the
+ * height map.
+ *
+ * @param size
+ * size the size of the terrain to be generated
+ * @param iterations
+ * the number of hills to grow
+ * @param minRadius
+ * the minimum radius of a hill
+ * @param maxRadius
+ * the maximum radius of a hill
+ * @param seed
+ * the seed to generate the same heightmap again
+ * @throws Exception
+ * @throws JmeException
+ * if size of the terrain is not greater that zero, or number of
+ * iterations is not greater that zero
+ */
+ public HillHeightMap(int size, int iterations, float minRadius,
+ float maxRadius, long seed) throws Exception {
+ if (size <= 0 || iterations <= 0 || minRadius <= 0 || maxRadius <= 0
+ || minRadius >= maxRadius) {
+ throw new Exception(
+ "Either size of the terrain is not greater that zero, "
+ + "or number of iterations is not greater that zero, "
+ + "or minimum or maximum radius are not greater than zero, "
+ + "or minimum radius is greater than maximum radius, "
+ + "or power of flattening is below one");
+ }
+ logger.info("Contructing hill heightmap using seed: " + seed);
+ this.size = size;
+ this.seed = seed;
+ this.iterations = iterations;
+ this.minRadius = minRadius;
+ this.maxRadius = maxRadius;
+
+ load();
+ }
+
+ /**
+ * Constructor sets the attributes of the hill system and generates the
+ * height map by using a random seed.
+ *
+ * @param size
+ * size the size of the terrain to be generated
+ * @param iterations
+ * the number of hills to grow
+ * @param minRadius
+ * the minimum radius of a hill
+ * @param maxRadius
+ * the maximum radius of a hill
+ * @throws Exception
+ * @throws JmeException
+ * if size of the terrain is not greater that zero, or number of
+ * iterations is not greater that zero
+ */
+ public HillHeightMap(int size, int iterations, float minRadius,
+ float maxRadius) throws Exception {
+ this(size, iterations, minRadius, maxRadius, new Random().nextLong());
+ }
+
+ /*
+ * Generates a heightmap using the Hill Algorithm and the attributes set by
+ * the constructor or the setters.
+ */
+ public boolean load() {
+ // clean up data if needed.
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+ heightData = new float[size * size];
+ float[][] tempBuffer = new float[size][size];
+ Random random = new Random(seed);
+
+ // Add the hills
+ for (int i = 0; i < iterations; i++) {
+ addHill(tempBuffer, random);
+ }
+
+ // transfer temporary buffer to final heightmap
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ setHeightAtPoint((float) tempBuffer[i][j], j, i);
+ }
+ }
+
+ normalizeTerrain(NORMALIZE_RANGE);
+
+ logger.info("Created Heightmap using the Hill Algorithm");
+
+ return true;
+ }
+
+ /**
+ * Generates a new hill of random size and height at a random position in
+ * the heightmap. This is the actual Hill algorithm. The <code>Random</code>
+ * object is used to guarantee the same heightmap for the same seed and
+ * attributes.
+ *
+ * @param tempBuffer
+ * the temporary height map buffer
+ * @param random
+ * the random number generator
+ */
+ protected void addHill(float[][] tempBuffer, Random random) {
+ // Pick the radius for the hill
+ float radius = randomRange(random, minRadius, maxRadius);
+
+ // Pick a centerpoint for the hill
+ float x = randomRange(random, -radius, size + radius);
+ float y = randomRange(random, -radius, size + radius);
+
+ float radiusSq = radius * radius;
+ float distSq;
+ float height;
+
+ // Find the range of hills affected by this hill
+ int xMin = Math.round(x - radius - 1);
+ int xMax = Math.round(x + radius + 1);
+
+ int yMin = Math.round(y - radius - 1);
+ int yMax = Math.round(y + radius + 1);
+
+ // Don't try to affect points outside the heightmap
+ if (xMin < 0) {
+ xMin = 0;
+ }
+ if (xMax > size) {
+ xMax = size - 1;
+ }
+
+ if (yMin < 0) {
+ yMin = 0;
+ }
+ if (yMax > size) {
+ yMax = size - 1;
+ }
+
+ for (int i = xMin; i <= xMax; i++) {
+ for (int j = yMin; j <= yMax; j++) {
+ distSq = (x - i) * (x - i) + (y - j) * (y - j);
+ height = radiusSq - distSq;
+
+ if (height > 0) {
+ tempBuffer[i][j] += height;
+ }
+ }
+ }
+ }
+
+ private float randomRange(Random random, float min, float max) {
+ return (random.nextInt() * (max - min) / Integer.MAX_VALUE) + min;
+ }
+
+ /**
+ * Sets the number of hills to grow. More hills usually mean a nicer
+ * heightmap.
+ *
+ * @param iterations
+ * the number of hills to grow
+ * @throws Exception
+ * @throws JmeException
+ * if iterations if not greater than zero
+ */
+ public void setIterations(int iterations) throws Exception {
+ if (iterations <= 0) {
+ throw new Exception(
+ "Number of iterations is not greater than zero");
+ }
+ this.iterations = iterations;
+ }
+
+ /**
+ * Sets the minimum radius of a hill.
+ *
+ * @param maxRadius
+ * the maximum radius of a hill
+ * @throws Exception
+ * @throws JmeException
+ * if the maximum radius if not greater than zero or not greater
+ * than the minimum radius
+ */
+ public void setMaxRadius(float maxRadius) throws Exception {
+ if (maxRadius <= 0 || maxRadius <= minRadius) {
+ throw new Exception("The maximum radius is not greater than 0, "
+ + "or not greater than the minimum radius");
+ }
+ this.maxRadius = maxRadius;
+ }
+
+ /**
+ * Sets the maximum radius of a hill.
+ *
+ * @param minRadius
+ * the minimum radius of a hill
+ * @throws Exception
+ * @throws JmeException if the minimum radius is not greater than zero or not
+ * lower than the maximum radius
+ */
+ public void setMinRadius(float minRadius) throws Exception {
+ if (minRadius <= 0 || minRadius >= maxRadius) {
+ throw new Exception("The minimum radius is not greater than 0, "
+ + "or not lower than the maximum radius");
+ }
+ this.minRadius = minRadius;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMap.java
new file mode 100644
index 0000000..84eb857
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMap.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.terrain.heightmap;
+
+import java.nio.ByteBuffer;
+import com.jme3.math.ColorRGBA;
+import com.jme3.texture.Image;
+import java.nio.ShortBuffer;
+
+/**
+ * <code>ImageBasedHeightMap</code> is a height map created from the grayscale
+ * conversion of an image. The image used currently must have an equal height
+ * and width, although future work could scale an incoming image to a specific
+ * height and width.
+ *
+ * @author Mike Kienenberger
+ * @version $id$
+ */
+public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeightmap {
+
+
+ protected Image colorImage;
+
+
+ public void setImage(Image image) {
+ this.colorImage = image;
+ }
+
+ /**
+ * Creates a HeightMap from an Image. The image will be converted to
+ * grayscale, and the grayscale values will be used to generate the height
+ * map. White is highest point while black is lowest point.
+ *
+ * Currently, the Image used must be square (width == height), but future
+ * work could rescale the image.
+ *
+ * @param colorImage
+ * Image to map to the height map.
+ */
+ public ImageBasedHeightMap(Image colorImage) {
+ this.colorImage = colorImage;
+ }
+
+ public ImageBasedHeightMap(Image colorImage, float heightScale) {
+ this.colorImage = colorImage;
+ this.heightScale = heightScale;
+ }
+
+ /**
+ * Loads the image data from top left to bottom right
+ */
+ public boolean load() {
+ return load(false, false);
+ }
+
+ /**
+ * Get the grayscale value, or override in your own sub-classes
+ */
+ protected float calculateHeight(float red, float green, float blue) {
+ return (float) (0.299 * red + 0.587 * green + 0.114 * blue);
+ }
+
+ public boolean load(boolean flipX, boolean flipY) {
+
+ int imageWidth = colorImage.getWidth();
+ int imageHeight = colorImage.getHeight();
+
+ if (imageWidth != imageHeight)
+ throw new RuntimeException("imageWidth: " + imageWidth
+ + " != imageHeight: " + imageHeight);
+
+ size = imageWidth;
+
+ ByteBuffer buf = colorImage.getData(0);
+
+ heightData = new float[(imageWidth * imageHeight)];
+
+ ColorRGBA colorStore = new ColorRGBA();
+
+ int index = 0;
+ if (flipY) {
+ for (int h = 0; h < imageHeight; ++h) {
+ if (flipX) {
+ for (int w = imageWidth - 1; w >= 0; --w) {
+ int baseIndex = (h * imageWidth)+ w;
+ heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
+ }
+ } else {
+ for (int w = 0; w < imageWidth; ++w) {
+ int baseIndex = (h * imageWidth)+ w;
+ heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
+ }
+ }
+ }
+ } else {
+ for (int h = imageHeight - 1; h >= 0; --h) {
+ if (flipX) {
+ for (int w = imageWidth - 1; w >= 0; --w) {
+ int baseIndex = (h * imageWidth)+ w;
+ heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
+ }
+ } else {
+ for (int w = 0; w < imageWidth; ++w) {
+ int baseIndex = (h * imageWidth)+ w;
+ heightData[index++] = getHeightAtPostion(buf, colorImage, baseIndex, colorStore)*heightScale;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ protected float getHeightAtPostion(ByteBuffer buf, Image image, int position, ColorRGBA store) {
+ switch (image.getFormat()){
+ case RGBA8:
+ buf.position( position * 4 );
+ store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()));
+ return calculateHeight(store.r, store.g, store.b);
+ case ABGR8:
+ buf.position( position * 4 );
+ float a = byte2float(buf.get());
+ float b = byte2float(buf.get());
+ float g = byte2float(buf.get());
+ float r = byte2float(buf.get());
+ store.set(r,g,b,a);
+ return calculateHeight(store.r, store.g, store.b);
+ case RGB8:
+ buf.position( position * 3 );
+ store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), 1);
+ return calculateHeight(store.r, store.g, store.b);
+ case Luminance8:
+ buf.position( position );
+ return byte2float(buf.get())*255*heightScale;
+ case Luminance16:
+ ShortBuffer sbuf = buf.asShortBuffer();
+ sbuf.position( position );
+ return (sbuf.get() & 0xFFFF) / 65535f * 255f * heightScale;
+ default:
+ throw new UnsupportedOperationException("Image format: "+image.getFormat());
+ }
+ }
+
+ private float byte2float(byte b){
+ return ((float)(b & 0xFF)) / 255f;
+ }
+} \ No newline at end of file
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java b/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java
new file mode 100644
index 0000000..1ba5f3e
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java
@@ -0,0 +1,79 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.heightmap;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
+import com.jme3.math.Vector3f;
+import com.jme3.texture.Texture;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Loads Terrain grid tiles with image heightmaps.
+ * By default it expects a 16-bit grayscale image as the heightmap, but
+ * you can also call setImageType(BufferedImage.TYPE_) to set it to be a different
+ * image type. If you do this, you must also set a custom ImageHeightmap that will
+ * understand and be able to parse the image. By default if you pass in an image of type
+ * BufferedImage.TYPE_3BYTE_BGR, it will use the ImageBasedHeightMap for you.
+ *
+ * @author Anthyon, Brent Owens
+ */
+@Deprecated
+/**
+ * @Deprecated in favor of ImageTileLoader
+ */
+public class ImageBasedHeightMapGrid implements HeightMapGrid {
+
+ private static final Logger logger = Logger.getLogger(ImageBasedHeightMapGrid.class.getName());
+ private final AssetManager assetManager;
+ private final Namer namer;
+ private int size;
+
+
+ public ImageBasedHeightMapGrid(final String textureBase, final String textureExt, AssetManager assetManager) {
+ this(assetManager, new Namer() {
+
+ public String getName(int x, int y) {
+ return textureBase + "_" + x + "_" + y + "." + textureExt;
+ }
+ });
+ }
+
+ public ImageBasedHeightMapGrid(AssetManager assetManager, Namer namer) {
+ this.assetManager = assetManager;
+ this.namer = namer;
+ }
+
+ public HeightMap getHeightMapAt(Vector3f location) {
+ // HEIGHTMAP image (for the terrain heightmap)
+ int x = (int) location.x;
+ int z = (int) location.z;
+
+ AbstractHeightMap heightmap = null;
+ //BufferedImage im = null;
+
+ try {
+ String name = namer.getName(x, z);
+ logger.log(Level.INFO, "Loading heightmap from file: {0}", name);
+ final Texture texture = assetManager.loadTexture(new TextureKey(name));
+
+ // CREATE HEIGHTMAP
+ heightmap = new ImageBasedHeightMap(texture.getImage());
+
+ heightmap.setHeightScale(1);
+ heightmap.load();
+
+ } catch (AssetNotFoundException e) {
+ logger.log(Level.SEVERE, "Asset Not found! ", e);
+ }
+ return heightmap;
+ }
+
+ public void setSize(int size) {
+ this.size = size - 1;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java b/engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java
new file mode 100644
index 0000000..76a222b
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java
@@ -0,0 +1,30 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.heightmap;
+
+import com.jme3.texture.Image;
+
+/**
+ * A heightmap that is built off an image.
+ * If you want to be able to supply different Image types to
+ * ImageBaseHeightMapGrid, you need to implement this interface,
+ * and have that class extend Abstract heightmap.
+ *
+ * @author bowens
+ * @deprecated
+ */
+public interface ImageHeightmap {
+
+ /**
+ * Set the image to use for this heightmap
+ */
+ //public void setImage(Image image);
+
+ /**
+ * The BufferedImage.TYPE_ that is supported
+ * by this ImageHeightmap
+ */
+ //public int getSupportedImageType();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/MidpointDisplacementHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/MidpointDisplacementHeightMap.java
new file mode 100644
index 0000000..acb2b12
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/MidpointDisplacementHeightMap.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import com.jme3.math.FastMath;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.JMException;
+
+/**
+ * <code>MidpointDisplacementHeightMap</code> generates an heightmap based on
+ * the midpoint displacement algorithm. See Constructor javadoc for more info.
+ * @author cghislai
+ */
+public class MidpointDisplacementHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(MidpointDisplacementHeightMap.class.getName());
+ private float range; // The offset in which randomness will be added
+ private float persistence; // How the random offset evolves with increasing passes
+ private long seed; // seed for random number generator
+
+ /**
+ * The constructor generates the heightmap. After the first 4 corners are
+ * randomly given an height, the center will be heighted to the average of
+ * the 4 corners to which a random value is added. Then other passes fill
+ * the heightmap by the same principle.
+ * The random value is generated between the values <code>-range</code>
+ * and <code>range</code>. The <code>range</code> parameter is multiplied by
+ * the <code>persistence</code> parameter each pass to smoothen close cell heights.
+ * Extends this class and override the getOffset function for more control of
+ * the randomness (you can use the coordinates and/or the computed average height
+ * to influence the random amount added.
+ *
+ * @param size
+ * The size of the heightmap, must be 2^N+1
+ * @param range
+ * The range in which randomness will be added. A value of 1 will
+ * allow -1 to 1 value changes.
+ * @param persistence
+ * The factor by which the range will evolve at each iteration.
+ * A value of 0.5f will halve the range at each iteration and is
+ * typically a good choice
+ * @param seed
+ * A seed to feed the random number generator.
+ * @throw JMException if size is not a power of two plus one.
+ */
+ public MidpointDisplacementHeightMap(int size, float range, float persistence, long seed) throws Exception {
+ if (size < 0 || !FastMath.isPowerOfTwo(size - 1)) {
+ throw new JMException("The size is negative or not of the form 2^N +1"
+ + " (a power of two plus one)");
+ }
+ this.size = size;
+ this.range = range;
+ this.persistence = persistence;
+ this.seed = seed;
+ load();
+ }
+
+ /**
+ * The constructor generates the heightmap. After the first 4 corners are
+ * randomly given an height, the center will be heighted to the average of
+ * the 4 corners to which a random value is added. Then other passes fill
+ * the heightmap by the same principle.
+ * The random value is generated between the values <code>-range</code>
+ * and <code>range</code>. The <code>range</code> parameter is multiplied by
+ * the <code>persistence</code> parameter each pass to smoothen close cell heights.
+ * @param size
+ * The size of the heightmap, must be 2^N+1
+ * @param range
+ * The range in which randomness will be added. A value of 1 will
+ * allow -1 to 1 value changes.
+ * @param persistence
+ * The factor by which the range will evolve at each iteration.
+ * A value of 0.5f will halve the range at each iteration and is
+ * typically a good choice
+ * @throw JMException if size is not a power of two plus one.
+ */
+ public MidpointDisplacementHeightMap(int size, float range, float persistence) throws Exception {
+ this(size, range, persistence, new Random().nextLong());
+ }
+
+ /**
+ * Generate the heightmap.
+ * @return
+ */
+ @Override
+ public boolean load() {
+ // clean up data if needed.
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+ heightData = new float[size * size];
+ float[][] tempBuffer = new float[size][size];
+ Random random = new Random(seed);
+
+ tempBuffer[0][0] = random.nextFloat();
+ tempBuffer[0][size - 1] = random.nextFloat();
+ tempBuffer[size - 1][0] = random.nextFloat();
+ tempBuffer[size - 1][size - 1] = random.nextFloat();
+
+ float offsetRange = range;
+ int stepSize = size - 1;
+ while (stepSize > 1) {
+ int[] nextCoords = {0, 0};
+ while (nextCoords != null) {
+ nextCoords = doSquareStep(tempBuffer, nextCoords, stepSize, offsetRange, random);
+ }
+ nextCoords = new int[]{0, 0};
+ while (nextCoords != null) {
+ nextCoords = doDiamondStep(tempBuffer, nextCoords, stepSize, offsetRange, random);
+ }
+ stepSize /= 2;
+ offsetRange *= persistence;
+ }
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ setHeightAtPoint((float) tempBuffer[i][j], j, i);
+ }
+ }
+
+ normalizeTerrain(NORMALIZE_RANGE);
+
+ logger.log(Level.INFO, "Midpoint displacement heightmap generated");
+ return true;
+ }
+
+ /**
+ * Will fill the value at (coords[0]+stepSize/2, coords[1]+stepSize/2) with
+ * the average from the corners of the square with topleft corner at (coords[0],coords[1])
+ * and width of stepSize.
+ * @param tempBuffer the temprary heightmap
+ * @param coords an int array of lenght 2 with the x coord in position 0
+ * @param stepSize the size of the square
+ * @param offsetRange the offset range within a random value is picked and added to the average
+ * @param random the random generator
+ * @return
+ */
+ protected int[] doSquareStep(float[][] tempBuffer, int[] coords, int stepSize, float offsetRange, Random random) {
+ float cornerAverage = 0;
+ int x = coords[0];
+ int y = coords[1];
+ cornerAverage += tempBuffer[x][y];
+ cornerAverage += tempBuffer[x + stepSize][y];
+ cornerAverage += tempBuffer[x + stepSize][y + stepSize];
+ cornerAverage += tempBuffer[x][y + stepSize];
+ cornerAverage /= 4;
+ float offset = getOffset(random, offsetRange, coords, cornerAverage);
+ tempBuffer[x + stepSize / 2][y + stepSize / 2] = cornerAverage + offset;
+
+ // Only get to next square if the center is still in map
+ if (x + stepSize * 3 / 2 < size) {
+ return new int[]{x + stepSize, y};
+ }
+ if (y + stepSize * 3 / 2 < size) {
+ return new int[]{0, y + stepSize};
+ }
+ return null;
+ }
+
+ /**
+ * Will fill the cell at (x+stepSize/2, y) with the average of the 4 corners
+ * of the diamond centered on that point with width and height of stepSize.
+ * @param tempBuffer
+ * @param coords
+ * @param stepSize
+ * @param offsetRange
+ * @param random
+ * @return
+ */
+ protected int[] doDiamondStep(float[][] tempBuffer, int[] coords, int stepSize, float offsetRange, Random random) {
+ int cornerNbr = 0;
+ float cornerAverage = 0;
+ int x = coords[0];
+ int y = coords[1];
+ int[] dxs = new int[]{0, stepSize / 2, stepSize, stepSize / 2};
+ int[] dys = new int[]{0, -stepSize / 2, 0, stepSize / 2};
+
+ for (int d = 0; d < 4; d++) {
+ int i = x + dxs[d];
+ if (i < 0 || i > size - 1) {
+ continue;
+ }
+ int j = y + dys[d];
+ if (j < 0 || j > size - 1) {
+ continue;
+ }
+ cornerAverage += tempBuffer[i][j];
+ cornerNbr++;
+ }
+ cornerAverage /= cornerNbr;
+ float offset = getOffset(random, offsetRange, coords, cornerAverage);
+ tempBuffer[x + stepSize / 2][y] = cornerAverage + offset;
+
+ if (x + stepSize * 3 / 2 < size) {
+ return new int[]{x + stepSize, y};
+ }
+ if (y + stepSize / 2 < size) {
+ if (x + stepSize == size - 1) {
+ return new int[]{-stepSize / 2, y + stepSize / 2};
+ } else {
+ return new int[]{0, y + stepSize / 2};
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generate a random value to add to the computed average
+ * @param random the random generator
+ * @param offsetRange
+ * @param coords
+ * @param average
+ * @return A semi-random value within offsetRange
+ */
+ protected float getOffset(Random random, float offsetRange, int[] coords, float average) {
+ return 2 * (random.nextFloat() - 0.5F) * offsetRange;
+ }
+
+ public float getPersistence() {
+ return persistence;
+ }
+
+ public void setPersistence(float persistence) {
+ this.persistence = persistence;
+ }
+
+ public float getRange() {
+ return range;
+ }
+
+ public void setRange(float range) {
+ this.range = range;
+ }
+
+ public long getSeed() {
+ return seed;
+ }
+
+ public void setSeed(long seed) {
+ this.seed = seed;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/Namer.java b/engine/src/terrain/com/jme3/terrain/heightmap/Namer.java
new file mode 100644
index 0000000..73171ae
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/Namer.java
@@ -0,0 +1,21 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.heightmap;
+
+/**
+ *
+ * @author Anthyon
+ */
+public interface Namer {
+
+ /**
+ * Gets a name for a heightmap tile given it's cell id
+ * @param x
+ * @param y
+ * @return
+ */
+ public String getName(int x, int y);
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/ParticleDepositionHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/ParticleDepositionHeightMap.java
new file mode 100644
index 0000000..63dccc6
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/ParticleDepositionHeightMap.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import java.util.logging.Logger;
+
+/**
+ * <code>ParticleDepositionHeightMap</code> creates a heightmap based on the
+ * Particle Deposition algorithm based on Jason Shankel's paper from
+ * "Game Programming Gems". A heightmap is created using a Molecular beam
+ * epitaxy, or MBE, for depositing thin layers of atoms on a substrate.
+ * We drop a sequence of particles and simulate their flow across a surface
+ * of previously dropped particles. This creates a few high peaks, for further
+ * realism we can define a caldera. Similar to the way volcano's form
+ * islands, rock is deposited via lava, when the lava cools, it recedes
+ * into the volcano, creating the caldera.
+ *
+ * @author Mark Powell
+ * @version $Id$
+ */
+public class ParticleDepositionHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(ParticleDepositionHeightMap.class.getName());
+ //Attributes.
+ private int jumps;
+ private int peakWalk;
+ private int minParticles;
+ private int maxParticles;
+ private float caldera;
+
+ /**
+ * Constructor sets the attributes of the Particle Deposition
+ * Height Map and then generates the map.
+ *
+ * @param size the size of the terrain where the area is size x size.
+ * @param jumps number of areas to drop particles. Can also think
+ * of it as the number of peaks.
+ * @param peakWalk determines how much to agitate the drop point
+ * during a creation of a single peak. The lower the number
+ * the more the drop point will be agitated. 1 will insure
+ * agitation every round.
+ * @param minParticles defines the minimum number of particles to
+ * drop during a single jump.
+ * @param maxParticles defines the maximum number of particles to
+ * drop during a single jump.
+ * @param caldera defines the altitude to invert a peak. This is
+ * represented as a percentage, where 0.0 will not invert
+ * anything, and 1.0 will invert all.
+ *
+ * @throws JmeException if any value is less than zero, and
+ * if caldera is not between 0 and 1. If minParticles is greater than
+ * max particles as well.
+ */
+ public ParticleDepositionHeightMap(
+ int size,
+ int jumps,
+ int peakWalk,
+ int minParticles,
+ int maxParticles,
+ float caldera) throws Exception {
+
+
+ if (size <= 0
+ || jumps < 0
+ || peakWalk < 0
+ || minParticles > maxParticles
+ || minParticles < 0
+ || maxParticles < 0) {
+
+
+ throw new Exception(
+ "values must be greater than zero, "
+ + "and minParticles must be greater than maxParticles");
+ }
+ if (caldera < 0.0f || caldera > 1.0f) {
+ throw new Exception(
+ "Caldera level must be " + "between 0 and 1");
+ }
+
+
+ this.size = size;
+ this.jumps = jumps;
+ this.peakWalk = peakWalk;
+ this.minParticles = minParticles;
+ this.maxParticles = maxParticles;
+ this.caldera = caldera;
+
+
+ load();
+ }
+
+ /**
+ * <code>load</code> generates the heightfield using the Particle Deposition
+ * algorithm. <code>load</code> uses the latest attributes, so a call
+ * to <code>load</code> is recommended if attributes have changed using
+ * the set methods.
+ */
+ public boolean load() {
+ int x, y;
+ int calderaX, calderaY;
+ int sx, sy;
+ int tx, ty;
+ int m;
+ float calderaStartPoint;
+ float cutoff;
+ int dx[] = {0, 1, 0, size - 1, 1, 1, size - 1, size - 1};
+ int dy[] = {1, 0, size - 1, 0, size - 1, 1, size - 1, 1};
+ float[][] tempBuffer = new float[size][size];
+ //map 0 unmarked, unvisited, 1 marked, unvisited, 2 marked visited.
+ int[][] calderaMap = new int[size][size];
+ boolean done;
+
+
+ int minx, maxx;
+ int miny, maxy;
+
+
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+
+
+ heightData = new float[size * size];
+
+
+ //create peaks.
+ for (int i = 0; i < jumps; i++) {
+
+
+ //pick a random point.
+ x = (int) (Math.rint(Math.random() * (size - 1)));
+ y = (int) (Math.rint(Math.random() * (size - 1)));
+
+
+ //set the caldera point.
+ calderaX = x;
+ calderaY = y;
+
+
+ int numberParticles =
+ (int) (Math.rint(
+ (Math.random() * (maxParticles - minParticles))
+ + minParticles));
+ //drop particles.
+ for (int j = 0; j < numberParticles; j++) {
+ //check to see if we should aggitate the drop point.
+ if (peakWalk != 0 && j % peakWalk == 0) {
+ m = (int) (Math.rint(Math.random() * 7));
+ x = (x + dx[m] + size) % size;
+ y = (y + dy[m] + size) % size;
+ }
+
+
+ //add the particle to the piont.
+ tempBuffer[x][y] += 1;
+
+
+ sx = x;
+ sy = y;
+ done = false;
+
+
+ //cause the particle to "slide" down the slope and settle at
+ //a low point.
+ while (!done) {
+ done = true;
+
+
+ //check neighbors to see if we are higher.
+ m = (int) (Math.rint((Math.random() * 8)));
+ for (int jj = 0; jj < 8; jj++) {
+ tx = (sx + dx[(jj + m) % 8]) % (size);
+ ty = (sy + dy[(jj + m) % 8]) % (size);
+
+
+ //move to the neighbor.
+ if (tempBuffer[tx][ty] + 1.0f < tempBuffer[sx][sy]) {
+ tempBuffer[tx][ty] += 1.0f;
+ tempBuffer[sx][sy] -= 1.0f;
+ sx = tx;
+ sy = ty;
+ done = false;
+ break;
+ }
+ }
+ }
+
+
+ //This point is higher than the current caldera point,
+ //so move the caldera here.
+ if (tempBuffer[sx][sy] > tempBuffer[calderaX][calderaY]) {
+ calderaX = sx;
+ calderaY = sy;
+ }
+ }
+
+
+ //apply the caldera.
+ calderaStartPoint = tempBuffer[calderaX][calderaY];
+ cutoff = calderaStartPoint * (1.0f - caldera);
+ minx = calderaX;
+ maxx = calderaX;
+ miny = calderaY;
+ maxy = calderaY;
+
+
+ calderaMap[calderaX][calderaY] = 1;
+
+
+ done = false;
+ while (!done) {
+ done = true;
+ sx = minx;
+ sy = miny;
+ tx = maxx;
+ ty = maxy;
+
+
+ for (x = sx; x <= tx; x++) {
+ for (y = sy; y <= ty; y++) {
+
+
+ calderaX = (x + size) % size;
+ calderaY = (y + size) % size;
+
+
+ if (calderaMap[calderaX][calderaY] == 1) {
+ calderaMap[calderaX][calderaY] = 2;
+
+
+ if (tempBuffer[calderaX][calderaY] > cutoff
+ && tempBuffer[calderaX][calderaY]
+ <= calderaStartPoint) {
+
+
+ done = false;
+ tempBuffer[calderaX][calderaY] =
+ 2 * cutoff - tempBuffer[calderaX][calderaY];
+
+
+ //check the left and right neighbors
+ calderaX = (calderaX + 1) % size;
+ if (calderaMap[calderaX][calderaY] == 0) {
+ if (x + 1 > maxx) {
+ maxx = x + 1;
+ }
+ calderaMap[calderaX][calderaY] = 1;
+ }
+
+
+ calderaX = (calderaX + size - 2) % size;
+ if (calderaMap[calderaX][calderaY] == 0) {
+ if (x - 1 < minx) {
+ minx = x - 1;
+ }
+ calderaMap[calderaX][calderaY] = 1;
+ }
+
+
+ //check the upper and lower neighbors.
+ calderaX = (x + size) % size;
+ calderaY = (calderaY + 1) % size;
+ if (calderaMap[calderaX][calderaY] == 0) {
+ if (y + 1 > maxy) {
+ maxy = y + 1;
+ }
+ calderaMap[calderaX][calderaY] = 1;
+ }
+ calderaY = (calderaY + size - 2) % size;
+ if (calderaMap[calderaX][calderaY] == 0) {
+ if (y - 1 < miny) {
+ miny = y - 1;
+ }
+ calderaMap[calderaX][calderaY] = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //transfer the new terrain into the height map.
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ setHeightAtPoint((float) tempBuffer[i][j], j, i);
+ }
+ }
+ erodeTerrain();
+ normalizeTerrain(NORMALIZE_RANGE);
+
+ logger.info("Created heightmap using Particle Deposition");
+
+
+ return false;
+ }
+
+ /**
+ * <code>setJumps</code> sets the number of jumps or peaks that will
+ * be created during the next call to <code>load</code>.
+ * @param jumps the number of jumps to use for next load.
+ * @throws JmeException if jumps is less than zero.
+ */
+ public void setJumps(int jumps) throws Exception {
+ if (jumps < 0) {
+ throw new Exception("jumps must be positive");
+ }
+ this.jumps = jumps;
+ }
+
+ /**
+ * <code>setPeakWalk</code> sets how often the jump point will be
+ * aggitated. The lower the peakWalk, the more often the point will
+ * be aggitated.
+ *
+ * @param peakWalk the amount to aggitate the jump point.
+ * @throws JmeException if peakWalk is negative or zero.
+ */
+ public void setPeakWalk(int peakWalk) throws Exception {
+ if (peakWalk <= 0) {
+ throw new Exception(
+ "peakWalk must be greater than " + "zero");
+ }
+ this.peakWalk = peakWalk;
+ }
+
+ /**
+ * <code>setCaldera</code> sets the level at which a peak will be
+ * inverted.
+ *
+ * @param caldera the level at which a peak will be inverted. This must be
+ * between 0 and 1, as it is represented as a percentage.
+ * @throws JmeException if caldera is not between 0 and 1.
+ */
+ public void setCaldera(float caldera) throws Exception {
+ if (caldera < 0.0f || caldera > 1.0f) {
+ throw new Exception(
+ "Caldera level must be " + "between 0 and 1");
+ }
+ this.caldera = caldera;
+ }
+
+ /**
+ * <code>setMaxParticles</code> sets the maximum number of particles
+ * for a single jump.
+ * @param maxParticles the maximum number of particles for a single jump.
+ * @throws JmeException if maxParticles is negative or less than
+ * the current number of minParticles.
+ */
+ public void setMaxParticles(int maxParticles) {
+ this.maxParticles = maxParticles;
+ }
+
+ /**
+ * <code>setMinParticles</code> sets the minimum number of particles
+ * for a single jump.
+ * @param minParticles the minimum number of particles for a single jump.
+ * @throws JmeException if minParticles are greater than
+ * the current maxParticles;
+ */
+ public void setMinParticles(int minParticles) throws Exception {
+ if (minParticles > maxParticles) {
+ throw new Exception(
+ "minParticles must be less " + "than the current maxParticles");
+ }
+ this.minParticles = minParticles;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/RawHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/RawHeightMap.java
new file mode 100644
index 0000000..0318961
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/heightmap/RawHeightMap.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+import com.jme3.math.FastMath;
+import com.jme3.util.LittleEndien;
+import java.io.*;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ * <code>RawHeightMap</code> creates a height map from a RAW image file. The
+ * greyscale image denotes height based on the value of the pixel for each
+ * point. Where pure black the lowest point and pure white denotes the highest.
+ *
+ * @author Mark Powell
+ * @version $Id$
+ */
+public class RawHeightMap extends AbstractHeightMap {
+
+ private static final Logger logger = Logger.getLogger(RawHeightMap.class.getName());
+ /**
+ * Format specification for 8 bit precision heightmaps
+ */
+ public static final int FORMAT_8BIT = 0;
+ /**
+ * Format specification for 16 bit little endian heightmaps
+ */
+ public static final int FORMAT_16BITLE = 1;
+ /**
+ * Format specification for 16 bit big endian heightmaps
+ */
+ public static final int FORMAT_16BITBE = 2;
+ private int format;
+ private boolean swapxy;
+ private InputStream stream;
+
+ /**
+ * Constructor creates a new <code>RawHeightMap</code> object and loads a
+ * RAW image file to use as a height field. The greyscale image denotes the
+ * height of the terrain, where dark is low point and bright is high point.
+ * The values of the RAW correspond directly with the RAW values or 0 - 255.
+ *
+ * @param filename
+ * the RAW file to use as the heightmap.
+ * @param size
+ * the size of the RAW (must be square).
+ * @throws JmeException
+ * if the filename is null or not RAW, and if the size is 0 or
+ * less.
+ */
+ public RawHeightMap(String filename, int size) throws Exception {
+ this(filename, size, FORMAT_8BIT, false);
+ }
+
+ public RawHeightMap(float heightData[]) {
+ this.heightData = heightData;
+ this.size = (int) FastMath.sqrt(heightData.length);
+ this.format = FORMAT_8BIT;
+ }
+
+ public RawHeightMap(String filename, int size, int format, boolean swapxy) throws Exception {
+ // varify that filename and size are valid.
+ if (null == filename || size <= 0) {
+ throw new Exception("Must supply valid filename and "
+ + "size (> 0)");
+ }
+ try {
+ setup(new FileInputStream(filename), size, format, swapxy);
+ } catch (FileNotFoundException e) {
+ throw new Exception("height file not found: " + filename);
+ }
+ }
+
+ public RawHeightMap(InputStream stream, int size, int format, boolean swapxy) throws Exception {
+ setup(stream, size, format, swapxy);
+ }
+
+ public RawHeightMap(URL resource, int size, int format, boolean swapxy) throws Exception {
+ // varify that resource and size are valid.
+ if (null == resource || size <= 0) {
+ throw new Exception("Must supply valid resource and "
+ + "size (> 0)");
+ }
+
+
+ try {
+ setup(resource.openStream(), size, format, swapxy);
+ } catch (IOException e) {
+ throw new Exception("Unable to open height url: " + resource);
+ }
+ }
+
+ private void setup(InputStream stream, int size, int format, boolean swapxy) throws Exception {
+ // varify that filename and size are valid.
+ if (null == stream || size <= 0) {
+ throw new Exception("Must supply valid stream and "
+ + "size (> 0)");
+ }
+
+
+ this.stream = stream;
+ this.size = size;
+ this.format = format;
+ this.swapxy = swapxy;
+ load();
+ }
+
+ /**
+ * <code>load</code> fills the height data array with the appropriate data
+ * from the set RAW image. If the RAW image has not been set a JmeException
+ * will be thrown.
+ *
+ * @return true if the load is successfull, false otherwise.
+ */
+ @Override
+ public boolean load() {
+ // confirm data has been set. Redundant check...
+ if (null == stream || size <= 0) {
+ throw new RuntimeException("Must supply valid stream and "
+ + "size (> 0)");
+ }
+
+
+ // clean up
+ if (null != heightData) {
+ unloadHeightMap();
+ }
+
+
+ // initialize the height data attributes
+ heightData = new float[size * size];
+
+
+ // attempt to connect to the supplied file.
+ BufferedInputStream bis = null;
+
+
+ try {
+ bis = new BufferedInputStream(stream);
+ if (format == RawHeightMap.FORMAT_16BITLE) {
+ LittleEndien dis = new LittleEndien(bis);
+ int index;
+ // read the raw file
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ if (swapxy) {
+ index = i + j * size;
+ } else {
+ index = (i * size) + j;
+ }
+ heightData[index] = dis.readUnsignedShort();
+ }
+ }
+ dis.close();
+ } else {
+ DataInputStream dis = new DataInputStream(bis);
+ // read the raw file
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ int index;
+ if (swapxy) {
+ index = i + j * size;
+ } else {
+ index = (i * size) + j;
+ }
+ if (format == RawHeightMap.FORMAT_16BITBE) {
+ heightData[index] = dis.readUnsignedShort();
+ } else {
+ heightData[index] = dis.readUnsignedByte();
+ }
+ }
+ }
+ dis.close();
+ }
+ bis.close();
+ } catch (IOException e1) {
+ logger.warning("Error reading height data from stream.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * <code>setFilename</code> sets the file to use for the RAW data. A call
+ * to <code>load</code> is required to put the changes into effect.
+ *
+ * @param filename
+ * the new file to use for the height data.
+ * @throws JmeException
+ * if the file is null or not RAW.
+ */
+ public void setFilename(String filename) throws Exception {
+ if (null == filename) {
+ throw new Exception("Must supply valid filename.");
+ }
+ try {
+ this.stream = new FileInputStream(filename);
+ } catch (FileNotFoundException e) {
+ throw new Exception("height file not found: " + filename);
+ }
+ }
+
+ /**
+ * <code>setHeightStream</code> sets the stream to use for the RAW data. A call
+ * to <code>load</code> is required to put the changes into effect.
+ *
+ * @param stream
+ * the new stream to use for the height data.
+ * @throws JmeException
+ * if the stream is null or not RAW.
+ */
+ public void setHeightStream(InputStream stream) throws Exception {
+ if (null == stream) {
+ throw new Exception("Must supply valid stream.");
+ }
+ this.stream = stream;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/Basis.java b/engine/src/terrain/com/jme3/terrain/noise/Basis.java
new file mode 100644
index 0000000..9f310f6
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/Basis.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise;
+
+import java.nio.FloatBuffer;
+
+import com.jme3.terrain.noise.basis.ImprovedNoise;
+import com.jme3.terrain.noise.modulator.Modulator;
+
+/**
+ * Interface for - basically 3D - noise generation algorithms, based on the
+ * book: Texturing & Modeling - A Procedural Approach
+ *
+ * The main concept is to look at noise as a basis for generating fractals.
+ * Basis can be anything, like a simple:
+ *
+ * <code>
+ * float value(float x, float y, float z) {
+ * return 0; // a flat noise with 0 value everywhere
+ * }
+ * </code>
+ *
+ * or a more complex perlin noise ({@link ImprovedNoise}
+ *
+ * Fractals use these functions to generate a more complex result based on some
+ * frequency, roughness, etc values.
+ *
+ * Fractals themselves are implementing the Basis interface as well, opening
+ * an infinite range of results.
+ *
+ * @author Anthyon
+ *
+ * @since 2011
+ *
+ */
+public interface Basis {
+
+ public void init();
+
+ public Basis setScale(float scale);
+
+ public float getScale();
+
+ public Basis addModulator(Modulator modulator);
+
+ public float value(float x, float y, float z);
+
+ public FloatBuffer getBuffer(float sx, float sy, float base, int size);
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/Color.java b/engine/src/terrain/com/jme3/terrain/noise/Color.java
new file mode 100644
index 0000000..719c0ff
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/Color.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise;
+
+/**
+ * Helper class for working with colors and gradients
+ *
+ * @author Anthyon
+ *
+ */
+public class Color {
+
+ private final float[] rgba = new float[4];
+
+ public Color() {}
+
+ public Color(final int r, final int g, final int b) {
+ this(r, g, b, 255);
+ }
+
+ public Color(final int r, final int g, final int b, final int a) {
+ this.rgba[0] = (r & 255) / 256f;
+ this.rgba[1] = (g & 255) / 256f;
+ this.rgba[2] = (b & 255) / 256f;
+ this.rgba[3] = (a & 255) / 256f;
+ }
+
+ public Color(final float r, final float g, final float b) {
+ this(r, g, b, 1);
+ }
+
+ public Color(final float r, final float g, final float b, final float a) {
+ this.rgba[0] = ShaderUtils.clamp(r, 0, 1);
+ this.rgba[1] = ShaderUtils.clamp(g, 0, 1);
+ this.rgba[2] = ShaderUtils.clamp(b, 0, 1);
+ this.rgba[3] = ShaderUtils.clamp(a, 0, 1);
+ }
+
+ public Color(final int h, final float s, final float b) {
+ this(h, s, b, 1);
+ }
+
+ public Color(final int h, final float s, final float b, final float a) {
+ this.rgba[3] = a;
+ if (s == 0) {
+ // achromatic ( grey )
+ this.rgba[0] = b;
+ this.rgba[1] = b;
+ this.rgba[2] = b;
+ return;
+ }
+
+ float hh = h / 60.0f;
+ int i = ShaderUtils.floor(hh);
+ float f = hh - i;
+ float p = b * (1 - s);
+ float q = b * (1 - s * f);
+ float t = b * (1 - s * (1 - f));
+
+ if (i == 0) {
+ this.rgba[0] = b;
+ this.rgba[1] = t;
+ this.rgba[2] = p;
+ } else if (i == 1) {
+ this.rgba[0] = q;
+ this.rgba[1] = b;
+ this.rgba[2] = p;
+ } else if (i == 2) {
+ this.rgba[0] = p;
+ this.rgba[1] = b;
+ this.rgba[2] = t;
+ } else if (i == 3) {
+ this.rgba[0] = p;
+ this.rgba[1] = q;
+ this.rgba[2] = b;
+ } else if (i == 4) {
+ this.rgba[0] = t;
+ this.rgba[1] = p;
+ this.rgba[2] = b;
+ } else {
+ this.rgba[0] = b;
+ this.rgba[1] = p;
+ this.rgba[2] = q;
+ }
+ }
+
+ public int toInteger() {
+ return 0x00000000 | (int) (this.rgba[3] * 256) << 24 | (int) (this.rgba[0] * 256) << 16 | (int) (this.rgba[1] * 256) << 8
+ | (int) (this.rgba[2] * 256);
+ }
+
+ public String toWeb() {
+ return Integer.toHexString(this.toInteger());
+ }
+
+ public Color toGrayscale() {
+ float v = (this.rgba[0] + this.rgba[1] + this.rgba[2]) / 3f;
+ return new Color(v, v, v, this.rgba[3]);
+ }
+
+ public Color toSepia() {
+ float r = ShaderUtils.clamp(this.rgba[0] * 0.393f + this.rgba[1] * 0.769f + this.rgba[2] * 0.189f, 0, 1);
+ float g = ShaderUtils.clamp(this.rgba[0] * 0.349f + this.rgba[1] * 0.686f + this.rgba[2] * 0.168f, 0, 1);
+ float b = ShaderUtils.clamp(this.rgba[0] * 0.272f + this.rgba[1] * 0.534f + this.rgba[2] * 0.131f, 0, 1);
+ return new Color(r, g, b, this.rgba[3]);
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/Filter.java b/engine/src/terrain/com/jme3/terrain/noise/Filter.java
new file mode 100644
index 0000000..1dca8bf
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/Filter.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise;
+
+import java.nio.FloatBuffer;
+
+public interface Filter {
+ public Filter addPreFilter(Filter filter);
+
+ public Filter addPostFilter(Filter filter);
+
+ public FloatBuffer doFilter(float sx, float sy, float base, FloatBuffer data, int size);
+
+ public int getMargin(int size, int margin);
+
+ public boolean isEnabled();
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java b/engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java
new file mode 100644
index 0000000..d1c2f6e
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java
@@ -0,0 +1,288 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferInt;
+import java.awt.image.WritableRaster;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Helper class containing useful functions explained in the book:
+ * Texturing & Modeling - A Procedural Approach
+ *
+ * @author Anthyon
+ *
+ */
+public class ShaderUtils {
+
+ public static final float[] i2c(final int color) {
+ return new float[] { (color & 0x00ff0000) / 256f, (color & 0x0000ff00) / 256f, (color & 0x000000ff) / 256f,
+ (color & 0xff000000) / 256f };
+ }
+
+ public static final int c2i(final float[] color) {
+ return (color.length == 4 ? (int) (color[3] * 256) : 0xff000000) | ((int) (color[0] * 256) << 16) | ((int) (color[1] * 256) << 8)
+ | (int) (color[2] * 256);
+ }
+
+ public static final float mix(final float a, final float b, final float f) {
+ return (1 - f) * a + f * b;
+ }
+
+ public static final Color mix(final Color a, final Color b, final float f) {
+ return new Color((int) ShaderUtils.clamp(ShaderUtils.mix(a.getRed(), b.getRed(), f), 0, 255), (int) ShaderUtils.clamp(
+ ShaderUtils.mix(a.getGreen(), b.getGreen(), f), 0, 255), (int) ShaderUtils.clamp(
+ ShaderUtils.mix(a.getBlue(), b.getBlue(), f), 0, 255));
+ }
+
+ public static final int mix(final int a, final int b, final float f) {
+ return (int) ((1 - f) * a + f * b);
+ }
+
+ public static final float[] mix(final float[] c1, final float[] c2, final float f) {
+ return new float[] { ShaderUtils.mix(c1[0], c2[0], f), ShaderUtils.mix(c1[1], c2[1], f), ShaderUtils.mix(c1[2], c2[2], f) };
+ }
+
+ public static final float step(final float a, final float x) {
+ return x < a ? 0 : 1;
+ }
+
+ public static final float boxstep(final float a, final float b, final float x) {
+ return ShaderUtils.clamp((x - a) / (b - a), 0, 1);
+ }
+
+ public static final float pulse(final float a, final float b, final float x) {
+ return ShaderUtils.step(a, x) - ShaderUtils.step(b, x);
+ }
+
+ public static final float clamp(final float x, final float a, final float b) {
+ return x < a ? a : x > b ? b : x;
+ }
+
+ public static final float min(final float a, final float b) {
+ return a < b ? a : b;
+ }
+
+ public static final float max(final float a, final float b) {
+ return a > b ? a : b;
+ }
+
+ public static final float abs(final float x) {
+ return x < 0 ? -x : x;
+ }
+
+ public static final float smoothstep(final float a, final float b, final float x) {
+ if (x < a) {
+ return 0;
+ } else if (x > b) {
+ return 1;
+ }
+ float xx = (x - a) / (b - a);
+ return xx * xx * (3 - 2 * xx);
+ }
+
+ public static final float mod(final float a, final float b) {
+ int n = (int) (a / b);
+ float aa = a - n * b;
+ if (aa < 0) {
+ aa += b;
+ }
+ return aa;
+ }
+
+ public static final int floor(final float x) {
+ return x > 0 ? (int) x : (int) x - 1;
+ }
+
+ public static final float ceil(final float x) {
+ return (int) x + (x > 0 && x != (int) x ? 1 : 0);
+ }
+
+ public static final float spline(float x, final float[] knot) {
+ float CR00 = -0.5f;
+ float CR01 = 1.5f;
+ float CR02 = -1.5f;
+ float CR03 = 0.5f;
+ float CR10 = 1.0f;
+ float CR11 = -2.5f;
+ float CR12 = 2.0f;
+ float CR13 = -0.5f;
+ float CR20 = -0.5f;
+ float CR21 = 0.0f;
+ float CR22 = 0.5f;
+ float CR23 = 0.0f;
+ float CR30 = 0.0f;
+ float CR31 = 1.0f;
+ float CR32 = 0.0f;
+ float CR33 = 0.0f;
+
+ int span;
+ int nspans = knot.length - 3;
+ float c0, c1, c2, c3; /* coefficients of the cubic. */
+ if (nspans < 1) {/* illegal */
+ throw new RuntimeException("Spline has too few knots.");
+ }
+ /* Find the appropriate 4-point span of the spline. */
+ x = ShaderUtils.clamp(x, 0, 1) * nspans;
+ span = (int) x;
+ if (span >= knot.length - 3) {
+ span = knot.length - 3;
+ }
+ x -= span;
+ /* Evaluate the span cubic at x using Horner’s rule. */
+ c3 = CR00 * knot[span + 0] + CR01 * knot[span + 1] + CR02 * knot[span + 2] + CR03 * knot[span + 3];
+ c2 = CR10 * knot[span + 0] + CR11 * knot[span + 1] + CR12 * knot[span + 2] + CR13 * knot[span + 3];
+ c1 = CR20 * knot[span + 0] + CR21 * knot[span + 1] + CR22 * knot[span + 2] + CR23 * knot[span + 3];
+ c0 = CR30 * knot[span + 0] + CR31 * knot[span + 1] + CR32 * knot[span + 2] + CR33 * knot[span + 3];
+ return ((c3 * x + c2) * x + c1) * x + c0;
+ }
+
+ public static final float[] spline(final float x, final float[][] knots) {
+ float[] retval = new float[knots.length];
+ for (int i = 0; i < knots.length; i++) {
+ retval[i] = ShaderUtils.spline(x, knots[i]);
+ }
+ return retval;
+ }
+
+ public static final float gammaCorrection(final float gamma, final float x) {
+ return (float) Math.pow(x, 1 / gamma);
+ }
+
+ public static final float bias(final float b, final float x) {
+ return (float) Math.pow(x, Math.log(b) / Math.log(0.5));
+ }
+
+ public static final float gain(final float g, final float x) {
+ return x < 0.5 ? ShaderUtils.bias(1 - g, 2 * x) / 2 : 1 - ShaderUtils.bias(1 - g, 2 - 2 * x) / 2;
+ }
+
+ public static final float sinValue(final float s, final float minFreq, final float maxFreq, final float swidth) {
+ float value = 0;
+ float cutoff = ShaderUtils.clamp(0.5f / swidth, 0, maxFreq);
+ float f;
+ for (f = minFreq; f < 0.5 * cutoff; f *= 2) {
+ value += Math.sin(2 * Math.PI * f * s) / f;
+ }
+ float fade = ShaderUtils.clamp(2 * (cutoff - f) / cutoff, 0, 1);
+ value += fade * Math.sin(2 * Math.PI * f * s) / f;
+ return value;
+ }
+
+ public static final float length(final float x, final float y, final float z) {
+ return (float) Math.sqrt(x * x + y * y + z * z);
+ }
+
+ public static final float[] rotate(final float[] v, final float[][] m) {
+ float x = v[0] * m[0][0] + v[1] * m[0][1] + v[2] * m[0][2];
+ float y = v[0] * m[1][0] + v[1] * m[1][1] + v[2] * m[1][2];
+ float z = v[0] * m[2][0] + v[1] * m[2][1] + v[2] * m[2][2];
+ return new float[] { x, y, z };
+ }
+
+ public static final float[][] calcRotationMatrix(final float ax, final float ay, final float az) {
+ float[][] retval = new float[3][3];
+ float cax = (float) Math.cos(ax);
+ float sax = (float) Math.sin(ax);
+ float cay = (float) Math.cos(ay);
+ float say = (float) Math.sin(ay);
+ float caz = (float) Math.cos(az);
+ float saz = (float) Math.sin(az);
+
+ retval[0][0] = cay * caz;
+ retval[0][1] = -cay * saz;
+ retval[0][2] = say;
+ retval[1][0] = sax * say * caz + cax * saz;
+ retval[1][1] = -sax * say * saz + cax * caz;
+ retval[1][2] = -sax * cay;
+ retval[2][0] = -cax * say * caz + sax * saz;
+ retval[2][1] = cax * say * saz + sax * caz;
+ retval[2][2] = cax * cay;
+
+ return retval;
+ }
+
+ public static final float[] normalize(final float[] v) {
+ float l = ShaderUtils.length(v);
+ float[] r = new float[v.length];
+ int i = 0;
+ for (float vv : v) {
+ r[i++] = vv / l;
+ }
+ return r;
+ }
+
+ public static final float length(final float[] v) {
+ float s = 0;
+ for (float vv : v) {
+ s += vv * vv;
+ }
+ return (float) Math.sqrt(s);
+ }
+
+ public static final ByteBuffer getImageDataFromImage(BufferedImage bufferedImage) {
+ WritableRaster wr;
+ DataBuffer db;
+
+ BufferedImage bi = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = bi.createGraphics();
+ g.drawImage(bufferedImage, null, null);
+ bufferedImage = bi;
+ wr = bi.getRaster();
+ db = wr.getDataBuffer();
+
+ DataBufferInt dbi = (DataBufferInt) db;
+ int[] data = dbi.getData();
+
+ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ byteBuffer.asIntBuffer().put(data);
+ byteBuffer.flip();
+
+ return byteBuffer;
+ }
+
+ public static float frac(float f) {
+ return f - ShaderUtils.floor(f);
+ }
+
+ public static float[] floor(float[] fs) {
+ float[] retval = new float[fs.length];
+ for (int i = 0; i < fs.length; i++) {
+ retval[i] = ShaderUtils.floor(fs[i]);
+ }
+ return retval;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java b/engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java
new file mode 100644
index 0000000..53d0938
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.basis;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jme3.terrain.noise.Basis;
+import com.jme3.terrain.noise.filter.AbstractFilter;
+import com.jme3.terrain.noise.modulator.Modulator;
+
+public class FilteredBasis extends AbstractFilter implements Basis {
+
+ private Basis basis;
+ private List<Modulator> modulators = new ArrayList<Modulator>();
+ private float scale;
+
+ public FilteredBasis() {}
+
+ public FilteredBasis(Basis basis) {
+ this.basis = basis;
+ }
+
+ public Basis getBasis() {
+ return this.basis;
+ }
+
+ public void setBasis(Basis basis) {
+ this.basis = basis;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer data, int size) {
+ return data;
+ }
+
+ @Override
+ public void init() {
+ this.basis.init();
+ }
+
+ @Override
+ public Basis setScale(float scale) {
+ this.scale = scale;
+ return this;
+ }
+
+ @Override
+ public float getScale() {
+ return this.scale;
+ }
+
+ @Override
+ public Basis addModulator(Modulator modulator) {
+ this.modulators.add(modulator);
+ return this;
+ }
+
+ @Override
+ public float value(float x, float y, float z) {
+ throw new UnsupportedOperationException(
+ "Method value cannot be called on FilteredBasis and its descendants. Use getBuffer instead!");
+ }
+
+ @Override
+ public FloatBuffer getBuffer(float sx, float sy, float base, int size) {
+ int margin = this.getMargin(size, 0);
+ int workSize = size + 2 * margin;
+ FloatBuffer retval = this.basis.getBuffer(sx - margin, sy - margin, base, workSize);
+ return this.clip(this.doFilter(sx, sy, base, retval, workSize), workSize, size, margin);
+ }
+
+ public FloatBuffer clip(FloatBuffer buf, int origSize, int newSize, int offset) {
+ FloatBuffer result = FloatBuffer.allocate(newSize * newSize);
+
+ float[] orig = buf.array();
+ for (int i = offset; i < offset + newSize; i++) {
+ result.put(orig, i * origSize + offset, newSize);
+ }
+
+ return result;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java b/engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java
new file mode 100644
index 0000000..9233ef8
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.basis;
+
+import com.jme3.terrain.noise.ShaderUtils;
+
+// JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
+/**
+ * Perlin's default implementation of Improved Perlin Noise
+ * designed to work with Noise base
+ */
+public final class ImprovedNoise extends Noise {
+
+ @Override
+ public void init() {
+
+ }
+
+ static public float noise(float x, float y, float z) {
+ int X = ShaderUtils.floor(x), // FIND UNIT CUBE THAT
+ Y = ShaderUtils.floor(y), // CONTAINS POINT.
+ Z = ShaderUtils.floor(z);
+ x -= X; // FIND RELATIVE X,Y,Z
+ y -= Y; // OF POINT IN CUBE.
+ z -= Z;
+ X = X & 255;
+ Y = Y & 255;
+ Z = Z & 255;
+ float u = ImprovedNoise.fade(x), // COMPUTE FADE CURVES
+ v = ImprovedNoise.fade(y), // FOR EACH OF X,Y,Z.
+ w = ImprovedNoise.fade(z);
+ int A = ImprovedNoise.p[X] + Y;
+ int AA = ImprovedNoise.p[A] + Z;
+ int AB = ImprovedNoise.p[A + 1] + Z;
+ int B = ImprovedNoise.p[X + 1] + Y;
+ int BA = ImprovedNoise.p[B] + Z;
+ int BB = ImprovedNoise.p[B + 1] + Z;
+
+ return ImprovedNoise.lerp(
+ w,
+ ImprovedNoise.lerp(
+ v,
+ ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AA], x, y, z),
+ ImprovedNoise.grad3(ImprovedNoise.p[BA], x - 1, y, z)), // BLENDED
+ ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AB], x, y - 1, z), // RESULTS
+ ImprovedNoise.grad3(ImprovedNoise.p[BB], x - 1, y - 1, z))),// FROM
+ ImprovedNoise.lerp(v,
+ ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AA + 1], x, y, z - 1), // CORNERS
+ ImprovedNoise.grad3(ImprovedNoise.p[BA + 1], x - 1, y, z - 1)), // OF
+ ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AB + 1], x, y - 1, z - 1),
+ ImprovedNoise.grad3(ImprovedNoise.p[BB + 1], x - 1, y - 1, z - 1))));
+ }
+
+ static final float fade(final float t) {
+ return t * t * t * (t * (t * 6 - 15) + 10);
+ }
+
+ static final float lerp(final float t, final float a, final float b) {
+ return a + t * (b - a);
+ }
+
+ static float grad(final int hash, final float x, final float y, final float z) {
+ int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
+ float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
+ v = h < 4 ? y : h == 12 || h == 14 ? x : z;
+ return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
+ }
+
+ static final float grad3(final int hash, final float x, final float y, final float z) {
+ int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
+ return x * ImprovedNoise.GRAD3[h][0] + y * ImprovedNoise.GRAD3[h][1] + z * ImprovedNoise.GRAD3[h][2];
+ }
+
+ static final int p[] = new int[512], permutation[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36,
+ 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11,
+ 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158,
+ 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80,
+ 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217,
+ 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183,
+ 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113,
+ 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235,
+ 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
+ 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 };
+
+ private static float[][] GRAD3 = new float[][] { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 },
+ { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 }, { 0, -1, -1 }, { 1, 0, -1 }, { -1, 0, -1 }, { 0, -1, 1 },
+ { 0, 1, 1 } };
+
+ static {
+ for (int i = 0; i < 256; i++) {
+ ImprovedNoise.p[256 + i] = ImprovedNoise.p[i] = ImprovedNoise.permutation[i];
+ }
+ }
+
+ @Override
+ public float value(final float x, final float y, final float z) {
+ return ImprovedNoise.noise(this.scale * x, this.scale * y, this.scale * z);
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java b/engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java
new file mode 100644
index 0000000..c9de6f5
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.basis;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jme3.terrain.noise.Basis;
+import com.jme3.terrain.noise.modulator.Modulator;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
+
+/**
+ * Utility base class for Noise implementations
+ *
+ * @author Anthyon
+ *
+ */
+public abstract class Noise implements Basis {
+
+ protected List<Modulator> modulators = new ArrayList<Modulator>();
+
+ protected float scale = 1.0f;
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public FloatBuffer getBuffer(float sx, float sy, float base, int size) {
+ FloatBuffer retval = FloatBuffer.allocate(size * size);
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ retval.put(this.modulate((sx + x) / size, (sy + y) / size, base));
+ }
+ }
+ return retval;
+ }
+
+ public float modulate(float x, float y, float z) {
+ float retval = this.value(x, y, z);
+ for (Modulator m : this.modulators) {
+ if (m instanceof NoiseModulator) {
+ retval = m.value(retval);
+ }
+ }
+ return retval;
+ }
+
+ @Override
+ public Basis addModulator(Modulator modulator) {
+ this.modulators.add(modulator);
+ return this;
+ }
+
+ @Override
+ public Basis setScale(float scale) {
+ this.scale = scale;
+ return this;
+ }
+
+ @Override
+ public float getScale() {
+ return this.scale;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java b/engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java
new file mode 100644
index 0000000..8a547a3
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.basis;
+
+import com.jme3.terrain.noise.Basis;
+
+/**
+ * A simple aggregator basis. Takes two basis functions and a rate and return
+ * some mixed values
+ *
+ * @author Anthyon
+ *
+ */
+public class NoiseAggregator extends Noise {
+
+ private final float rate;
+ private final Basis a;
+ private final Basis b;
+
+ public NoiseAggregator(final Basis a, final Basis b, final float rate) {
+ this.a = a;
+ this.b = b;
+ this.rate = rate;
+ }
+
+ @Override
+ public void init() {
+ this.a.init();
+ this.b.init();
+ }
+
+ @Override
+ public float value(final float x, final float y, final float z) {
+ return this.a.value(x, y, z) * (1 - this.rate) + this.rate * this.b.value(x, y, z);
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java
new file mode 100644
index 0000000..4174ba9
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jme3.terrain.noise.Filter;
+
+public abstract class AbstractFilter implements Filter {
+
+ protected List<Filter> preFilters = new ArrayList<Filter>();
+ protected List<Filter> postFilters = new ArrayList<Filter>();
+
+ private boolean enabled = true;
+
+ @Override
+ public Filter addPreFilter(Filter filter) {
+ this.preFilters.add(filter);
+ return this;
+ }
+
+ @Override
+ public Filter addPostFilter(Filter filter) {
+ this.postFilters.add(filter);
+ return this;
+ }
+
+ @Override
+ public FloatBuffer doFilter(float sx, float sy, float base, FloatBuffer data, int size) {
+ if (!this.isEnabled()) {
+ return data;
+ }
+ FloatBuffer retval = data;
+ for (Filter f : this.preFilters) {
+ retval = f.doFilter(sx, sy, base, retval, size);
+ }
+ retval = this.filter(sx, sy, base, retval, size);
+ for (Filter f : this.postFilters) {
+ retval = f.doFilter(sx, sy, base, retval, size);
+ }
+ return retval;
+ }
+
+ public abstract FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int size);
+
+ @Override
+ public int getMargin(int size, int margin) {
+ // TODO sums up all the margins from filters... maybe there's a more
+ // efficient algorithm
+ if (!this.isEnabled()) {
+ return margin;
+ }
+ for (Filter f : this.preFilters) {
+ margin = f.getMargin(size, margin);
+ }
+ for (Filter f : this.postFilters) {
+ margin = f.getMargin(size, margin);
+ }
+ return margin;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java
new file mode 100644
index 0000000..13e2c5e
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+
+import com.jme3.terrain.noise.Basis;
+
+public class HydraulicErodeFilter extends AbstractFilter {
+
+ private Basis waterMap;
+ private Basis sedimentMap;
+ private float Kr;
+ private float Ks;
+ private float Ke;
+ private float Kc;
+ private float T;
+
+ public void setKc(float kc) {
+ this.Kc = kc;
+ }
+
+ public void setKe(float ke) {
+ this.Ke = ke;
+ }
+
+ public void setKr(float kr) {
+ this.Kr = kr;
+ }
+
+ public void setKs(float ks) {
+ this.Ks = ks;
+ }
+
+ public void setSedimentMap(Basis sedimentMap) {
+ this.sedimentMap = sedimentMap;
+ }
+
+ public void setT(float t) {
+ this.T = t;
+ }
+
+ public void setWaterMap(Basis waterMap) {
+ this.waterMap = waterMap;
+ }
+
+ @Override
+ public int getMargin(int size, int margin) {
+ return super.getMargin(size, margin) + 1;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int workSize) {
+ float[] ga = buffer.array();
+ // float[] wa = this.waterMap.getBuffer(sx, sy, base, workSize).array();
+ // float[] sa = this.sedimentMap.getBuffer(sx, sy, base,
+ // workSize).array();
+ float[] wt = new float[workSize * workSize];
+ float[] st = new float[workSize * workSize];
+
+ int[] idxrel = { -workSize - 1, -workSize + 1, workSize - 1, workSize + 1 };
+
+ // step 1. water arrives and step 2. captures material
+ for (int y = 0; y < workSize; y++) {
+ for (int x = 0; x < workSize; x++) {
+ int idx = y * workSize + x;
+ float wtemp = this.Kr; // * wa[idx];
+ float stemp = this.Ks; // * sa[idx];
+ if (wtemp > 0) {
+ wt[idx] += wtemp;
+ if (stemp > 0) {
+ ga[idx] -= stemp * wt[idx];
+ st[idx] += stemp * wt[idx];
+ }
+ }
+
+ // step 3. water is transported to it's neighbours
+ float a = ga[idx] + wt[idx];
+ // float[] aj = new float[idxrel.length];
+ float amax = 0;
+ int amaxidx = -1;
+ float ac = 0;
+ float dtotal = 0;
+
+ for (int j = 0; j < idxrel.length; j++) {
+ if (idx + idxrel[j] > 0 && idx + idxrel[j] < workSize) {
+ float at = ga[idx + idxrel[j]] + wt[idx + idxrel[j]];
+ if (a - at > a - amax) {
+ dtotal += at;
+ amax = at;
+ amaxidx = j;
+ ac++;
+ }
+ }
+ }
+
+ float aa = (dtotal + a) / (ac + 1);
+ // for (int j = 0; j < idxrel.length; j++) {
+ // if (idx + idxrel[j] > 0 && idx + idxrel[j] < workSize && a -
+ // aj[j] > 0) {
+ if (amaxidx > -1) {
+ float dwj = Math.min(wt[idx], a - aa) * (a - amax) / dtotal;
+ float dsj = st[idx] * dwj / wt[idx];
+ wt[idx] -= dwj;
+ st[idx] -= dsj;
+ wt[idx + idxrel[amaxidx]] += dwj;
+ st[idx + idxrel[amaxidx]] += dsj;
+ }
+ // }
+
+ // step 4. water evaporates and deposits material
+ wt[idx] = wt[idx] * (1 - this.Ke);
+ if (wt[idx] < this.T) {
+ wt[idx] = 0;
+ }
+ float smax = this.Kc * wt[idx];
+ if (st[idx] > smax) {
+ ga[idx] += st[idx] - smax;
+ st[idx] -= st[idx] - smax;
+ }
+ }
+ }
+
+ return buffer;
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java
new file mode 100644
index 0000000..3764256
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jme3.terrain.noise.Filter;
+
+public class IterativeFilter extends AbstractFilter {
+
+ private int iterations;
+
+ private List<Filter> preIterateFilters = new ArrayList<Filter>();
+ private List<Filter> postIterateFilters = new ArrayList<Filter>();
+ private Filter filter;
+
+ @Override
+ public int getMargin(int size, int margin) {
+ if (!this.isEnabled()) {
+ return margin;
+ }
+ for (Filter f : this.preIterateFilters) {
+ margin = f.getMargin(size, margin);
+ }
+ margin = this.filter.getMargin(size, margin);
+ for (Filter f : this.postIterateFilters) {
+ margin = f.getMargin(size, margin);
+ }
+ return this.iterations * margin + super.getMargin(size, margin);
+ }
+
+ public void setIterations(int iterations) {
+ this.iterations = iterations;
+ }
+
+ public int getIterations() {
+ return this.iterations;
+ }
+
+ public IterativeFilter addPostIterateFilter(Filter filter) {
+ this.postIterateFilters.add(filter);
+ return this;
+ }
+
+ public IterativeFilter addPreIterateFilter(Filter filter) {
+ this.preIterateFilters.add(filter);
+ return this;
+ }
+
+ public void setFilter(Filter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer data, int size) {
+ if (!this.isEnabled()) {
+ return data;
+ }
+ FloatBuffer retval = data;
+
+ for (int i = 0; i < this.iterations; i++) {
+ for (Filter f : this.preIterateFilters) {
+ retval = f.doFilter(sx, sy, base, retval, size);
+ }
+ retval = this.filter.doFilter(sx, sy, base, retval, size);
+ for (Filter f : this.postIterateFilters) {
+ retval = f.doFilter(sx, sy, base, retval, size);
+ }
+ }
+
+ return retval;
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java b/engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java
new file mode 100644
index 0000000..fc20da6
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+
+public class OptimizedErode extends AbstractFilter {
+
+ private float talus;
+ private int radius;
+
+ public OptimizedErode setRadius(int radius) {
+ this.radius = radius;
+ return this;
+ }
+
+ public int getRadius() {
+ return this.radius;
+ }
+
+ public OptimizedErode setTalus(float talus) {
+ this.talus = talus;
+ return this;
+ }
+
+ public float getTalus() {
+ return this.talus;
+ }
+
+ @Override
+ public int getMargin(int size, int margin) {
+ return super.getMargin(size, margin) + this.radius;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int size) {
+ float[] tmp = buffer.array();
+ float[] retval = new float[tmp.length];
+
+ for (int y = this.radius + 1; y < size - this.radius; y++) {
+ for (int x = this.radius + 1; x < size - this.radius; x++) {
+ int idx = y * size + x;
+ float h = tmp[idx];
+
+ float horizAvg = 0;
+ int horizCount = 0;
+ float vertAvg = 0;
+ int vertCount = 0;
+
+ boolean horizT = false;
+ boolean vertT = false;
+
+ for (int i = 0; i >= -this.radius; i--) {
+ int idxV = (y + i) * size + x;
+ int idxVL = (y + i - 1) * size + x;
+ int idxH = y * size + x + i;
+ int idxHL = y * size + x + i - 1;
+ float hV = tmp[idxV];
+ float hH = tmp[idxH];
+
+ if (Math.abs(h - hV) > this.talus && Math.abs(h - tmp[idxVL]) > this.talus || vertT) {
+ vertT = true;
+ } else {
+ if (Math.abs(h - hV) <= this.talus) {
+ vertAvg += hV;
+ vertCount++;
+ }
+ }
+
+ if (Math.abs(h - hH) > this.talus && Math.abs(h - tmp[idxHL]) > this.talus || horizT) {
+ horizT = true;
+ } else {
+ if (Math.abs(h - hH) <= this.talus) {
+ horizAvg += hH;
+ horizCount++;
+ }
+ }
+ }
+
+ retval[idx] = 0.5f * (vertAvg / (vertCount > 0 ? vertCount : 1) + horizAvg / (horizCount > 0 ? horizCount : 1));
+ }
+ }
+ return FloatBuffer.wrap(retval);
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java
new file mode 100644
index 0000000..f510f90
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.fractal.FractalSum;
+
+public class PerturbFilter extends AbstractFilter {
+
+ private float magnitude;
+
+ @Override
+ public int getMargin(int size, int margin) {
+ margin = super.getMargin(size, margin);
+ return (int) Math.floor(this.magnitude * (margin + size) + margin);
+ }
+
+ public void setMagnitude(float magnitude) {
+ this.magnitude = magnitude;
+ }
+
+ public float getMagnitude() {
+ return this.magnitude;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer data, int workSize) {
+ float[] arr = data.array();
+ int origSize = (int) Math.ceil(workSize / (2 * this.magnitude + 1));
+ int offset = (workSize - origSize) / 2;
+ Logger.getLogger(PerturbFilter.class.getCanonicalName()).info(
+ "Found origSize : " + origSize + " and offset: " + offset + " for workSize : " + workSize + " and magnitude : "
+ + this.magnitude);
+ float[] retval = new float[workSize * workSize];
+ float[] perturbx = new FractalSum().setOctaves(8).setScale(5f).getBuffer(sx, sy, base, workSize).array();
+ float[] perturby = new FractalSum().setOctaves(8).setScale(5f).getBuffer(sx, sy, base + 1, workSize).array();
+ for (int y = 0; y < workSize; y++) {
+ for (int x = 0; x < workSize; x++) {
+ // Perturb our coordinates
+ float noisex = perturbx[y * workSize + x];
+ float noisey = perturby[y * workSize + x];
+
+ int px = (int) (origSize * noisex * this.magnitude);
+ int py = (int) (origSize * noisey * this.magnitude);
+
+ float c00 = arr[this.wrap(y - py, workSize) * workSize + this.wrap(x - px, workSize)];
+ float c01 = arr[this.wrap(y - py, workSize) * workSize + this.wrap(x + px, workSize)];
+ float c10 = arr[this.wrap(y + py, workSize) * workSize + this.wrap(x - px, workSize)];
+ float c11 = arr[this.wrap(y + py, workSize) * workSize + this.wrap(x + px, workSize)];
+
+ float c0 = ShaderUtils.mix(c00, c01, noisex);
+ float c1 = ShaderUtils.mix(c10, c11, noisex);
+ retval[y * workSize + x] = ShaderUtils.mix(c0, c1, noisey);
+ }
+ }
+ return FloatBuffer.wrap(retval);
+ }
+
+ private int wrap(int v, int size) {
+ if (v < 0) {
+ return v + size - 1;
+ } else if (v >= size) {
+ return v - size;
+ } else {
+ return v;
+ }
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java
new file mode 100644
index 0000000..1c20c24
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+
+public class SmoothFilter extends AbstractFilter {
+
+ private int radius;
+ private float effect;
+
+ public void setRadius(int radius) {
+ this.radius = radius;
+ }
+
+ public int getRadius() {
+ return this.radius;
+ }
+
+ public void setEffect(float effect) {
+ this.effect = effect;
+ }
+
+ public float getEffect() {
+ return this.effect;
+ }
+
+ @Override
+ public int getMargin(int size, int margin) {
+ return super.getMargin(size, margin) + this.radius;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int size) {
+ float[] data = buffer.array();
+ float[] retval = new float[data.length];
+
+ for (int y = this.radius; y < size - this.radius; y++) {
+ for (int x = this.radius; x < size - this.radius; x++) {
+ int idx = y * size + x;
+ float n = 0;
+ for (int i = -this.radius; i < this.radius + 1; i++) {
+ for (int j = -this.radius; j < this.radius + 1; j++) {
+ n += data[(y + i) * size + x + j];
+ }
+ }
+ retval[idx] = this.effect * n / (4 * this.radius * (this.radius + 1) + 1) + (1 - this.effect) * data[idx];
+ }
+ }
+
+ return FloatBuffer.wrap(retval);
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java
new file mode 100644
index 0000000..2e49679
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.filter;
+
+import java.nio.FloatBuffer;
+
+public class ThermalErodeFilter extends AbstractFilter {
+
+ private float talus;
+ private float c;
+
+ public ThermalErodeFilter setC(float c) {
+ this.c = c;
+ return this;
+ }
+
+ public ThermalErodeFilter setTalus(float talus) {
+ this.talus = talus;
+ return this;
+ }
+
+ @Override
+ public int getMargin(int size, int margin) {
+ return super.getMargin(size, margin) + 1;
+ }
+
+ @Override
+ public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int workSize) {
+ float[] ga = buffer.array();
+ float[] sa = new float[workSize * workSize];
+
+ int[] idxrel = { -workSize - 1, -workSize + 1, workSize - 1, workSize + 1 };
+
+ for (int y = 0; y < workSize; y++) {
+ for (int x = 0; x < workSize; x++) {
+ int idx = y * workSize + x;
+ ga[idx] += sa[idx];
+ sa[idx] = 0;
+
+ float[] deltas = new float[idxrel.length];
+ float deltaMax = this.talus;
+ float deltaTotal = 0;
+
+ for (int j = 0; j < idxrel.length; j++) {
+ if (idx + idxrel[j] > 0 && idx + idxrel[j] < ga.length) {
+ float dj = ga[idx] - ga[idx + idxrel[j]];
+ if (dj > this.talus) {
+ deltas[j] = dj;
+ deltaTotal += dj;
+ if (dj > deltaMax) {
+ deltaMax = dj;
+ }
+ }
+ }
+ }
+
+ for (int j = 0; j < idxrel.length; j++) {
+ if (deltas[j] != 0) {
+ float d = this.c * (deltaMax - this.talus) * deltas[j] / deltaTotal;
+ if (d > ga[idx] + sa[idx]) {
+ d = ga[idx] + sa[idx];
+ }
+ sa[idx] -= d;
+ sa[idx + idxrel[j]] += d;
+ }
+ deltas[j] = 0;
+ }
+ }
+ }
+
+ return buffer;
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java b/engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java
new file mode 100644
index 0000000..9b53447
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.fractal;
+
+import com.jme3.terrain.noise.Basis;
+
+/**
+ * Interface for a general fractal basis.
+ *
+ * Takes any number of basis funcions to work with and a few common parameters
+ * for noise fractals
+ *
+ * @author Anthyon
+ *
+ */
+public interface Fractal extends Basis {
+
+ public Fractal setOctaves(final float octaves);
+
+ public Fractal setFrequency(final float frequency);
+
+ public Fractal setRoughness(final float roughness);
+
+ public Fractal setAmplitude(final float amplitude);
+
+ public Fractal setLacunarity(final float lacunarity);
+
+ public Fractal addBasis(Basis basis);
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java b/engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java
new file mode 100644
index 0000000..5fe5dcb
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.fractal;
+
+import com.jme3.terrain.noise.Basis;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.ImprovedNoise;
+import com.jme3.terrain.noise.basis.Noise;
+
+/**
+ * FractalSum is the simplest form of fractal functions summing up a few octaves
+ * of the noise value with an ever decreasing (0 < roughness < 1) amplitude
+ *
+ * lacunarity = 2.0f is the classical octave distance
+ *
+ * Note: though noise basis functions are generally designed to return value
+ * between -1..1, there sum can easily be made to extend out of this range. To
+ * handle this is up to the user.
+ *
+ * @author Anthyon
+ *
+ */
+public class FractalSum extends Noise implements Fractal {
+
+ private Basis basis;
+ private float lacunarity;
+ private float amplitude;
+ private float roughness;
+ private float frequency;
+ private float octaves;
+ private int maxFreq;
+
+ public FractalSum() {
+ this.basis = new ImprovedNoise();
+ this.lacunarity = 2.124367f;
+ this.amplitude = 1.0f;
+ this.roughness = 0.6f;
+ this.frequency = 1f;
+ this.setOctaves(1);
+ }
+
+ @Override
+ public float value(final float x, final float y, final float z) {
+ float total = 0;
+
+ for (float f = this.frequency, a = this.amplitude; f < this.maxFreq; f *= this.lacunarity, a *= this.roughness) {
+ total += this.basis.value(this.scale * x * f, this.scale * y * f, this.scale * z * f) * a;
+ }
+
+ return ShaderUtils.clamp(total, -1, 1);
+ }
+
+ @Override
+ public Fractal addBasis(final Basis basis) {
+ this.basis = basis;
+ return this;
+ }
+
+ public float getOctaves() {
+ return this.octaves;
+ }
+
+ @Override
+ public Fractal setOctaves(final float octaves) {
+ this.octaves = octaves;
+ this.maxFreq = 1 << (int) octaves;
+ return this;
+ }
+
+ public float getFrequency() {
+ return this.frequency;
+ }
+
+ @Override
+ public Fractal setFrequency(final float frequency) {
+ this.frequency = frequency;
+ return this;
+ }
+
+ public float getRoughness() {
+ return this.roughness;
+ }
+
+ @Override
+ public Fractal setRoughness(final float roughness) {
+ this.roughness = roughness;
+ return this;
+ }
+
+ public float getAmplitude() {
+ return this.amplitude;
+ }
+
+ @Override
+ public Fractal setAmplitude(final float amplitude) {
+ this.amplitude = amplitude;
+ return this;
+ }
+
+ public float getLacunarity() {
+ return this.lacunarity;
+ }
+
+ @Override
+ public Fractal setLacunarity(final float lacunarity) {
+ this.lacunarity = lacunarity;
+ return this;
+ }
+
+ @Override
+ public void init() {
+
+ }
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java b/engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java
new file mode 100644
index 0000000..3f09c29
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.modulator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jme3.terrain.noise.ShaderUtils;
+
+public class CatRom2 implements Modulator {
+
+ private int sampleRate = 100;
+
+ private final float[] table;
+
+ private static Map<Integer, CatRom2> instances = new HashMap<Integer, CatRom2>();
+
+ public CatRom2(final int sampleRate) {
+ this.sampleRate = sampleRate;
+ this.table = new float[4 * sampleRate + 1];
+ for (int i = 0; i < 4 * sampleRate + 1; i++) {
+ float x = i / (float) sampleRate;
+ x = (float) Math.sqrt(x);
+ if (x < 1) {
+ this.table[i] = 0.5f * (2 + x * x * (-5 + x * 3));
+ } else {
+ this.table[i] = 0.5f * (4 + x * (-8 + x * (5 - x)));
+ }
+ }
+ }
+
+ public static CatRom2 getInstance(final int sampleRate) {
+ if (!CatRom2.instances.containsKey(sampleRate)) {
+ CatRom2.instances.put(sampleRate, new CatRom2(sampleRate));
+ }
+ return CatRom2.instances.get(sampleRate);
+ }
+
+ @Override
+ public float value(final float... in) {
+ if (in[0] >= 4) {
+ return 0;
+ }
+ in[0] = in[0] * this.sampleRate + 0.5f;
+ int i = ShaderUtils.floor(in[0]);
+ if (i >= 4 * this.sampleRate + 1) {
+ return 0;
+ }
+ return this.table[i];
+ }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java b/engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java
new file mode 100644
index 0000000..28bfd1c
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.modulator;
+
+public interface Modulator {
+
+ public float value(float... in);
+
+}
diff --git a/engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java b/engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java
new file mode 100644
index 0000000..38a3bd6
--- /dev/null
+++ b/engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2011, Novyon Events
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Anthyon
+ */
+package com.jme3.terrain.noise.modulator;
+
+public interface NoiseModulator extends Modulator {
+
+}
diff --git a/engine/src/test/jme3test/TestChooser.java b/engine/src/test/jme3test/TestChooser.java
new file mode 100644
index 0000000..3d8c1f8
--- /dev/null
+++ b/engine/src/test/jme3test/TestChooser.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.system.JmeContext;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+
+/**
+ * Class with a main method that displays a dialog to choose any jME demo to be
+ * started.
+ */
+public class TestChooser extends JDialog {
+ private static final Logger logger = Logger.getLogger(TestChooser.class
+ .getName());
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Only accessed from EDT
+ */
+ private Object[] selectedClass = null;
+ private boolean showSetting = true;
+
+ /**
+ * Constructs a new TestChooser that is initially invisible.
+ */
+ public TestChooser() throws HeadlessException {
+ super((JFrame) null, "TestChooser");
+ }
+
+ /**
+ * @param classes
+ * vector that receives the found classes
+ * @return classes vector, list of all the classes in a given package (must
+ * be found in classpath).
+ */
+ protected Vector<Class> find(String pckgname, boolean recursive,
+ Vector<Class> classes) {
+ URL url;
+
+ // Translate the package name into an absolute path
+ String name = pckgname;
+ if (!name.startsWith("/")) {
+ name = "/" + name;
+ }
+ name = name.replace('.', '/');
+
+ // Get a File object for the package
+ // URL url = UPBClassLoader.get().getResource(name);
+ url = this.getClass().getResource(name);
+ // URL url = ClassLoader.getSystemClassLoader().getResource(name);
+ pckgname = pckgname + ".";
+
+ File directory;
+ try {
+ directory = new File(URLDecoder.decode(url.getFile(), "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e); // should never happen
+ }
+
+ if (directory.exists()) {
+ logger.info("Searching for Demo classes in \""
+ + directory.getName() + "\".");
+ addAllFilesInDirectory(directory, classes, pckgname, recursive);
+ } else {
+ try {
+ // It does not work with the filesystem: we must
+ // be in the case of a package contained in a jar file.
+ logger.info("Searching for Demo classes in \"" + url + "\".");
+ URLConnection urlConnection = url.openConnection();
+ if (urlConnection instanceof JarURLConnection) {
+ JarURLConnection conn = (JarURLConnection) urlConnection;
+
+ JarFile jfile = conn.getJarFile();
+ Enumeration e = jfile.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+ Class result = load(entry.getName());
+ if (result != null && !classes.contains(result)) {
+ classes.add(result);
+ }
+ }
+ }
+ } catch (IOException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(),
+ "find(pckgname, recursive, classes)", "Exception", e);
+ } catch (Exception e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(),
+ "find(pckgname, recursive, classes)", "Exception", e);
+ }
+ }
+ return classes;
+ }
+
+ /**
+ * Load a class specified by a file- or entry-name
+ *
+ * @param name
+ * name of a file or entry
+ * @return class file that was denoted by the name, null if no class or does
+ * not contain a main method
+ */
+ private Class load(String name) {
+ if (name.endsWith(".class")
+ && name.indexOf("Test") >= 0
+ && name.indexOf('$') < 0) {
+ String classname = name.substring(0, name.length()
+ - ".class".length());
+
+ if (classname.startsWith("/")) {
+ classname = classname.substring(1);
+ }
+ classname = classname.replace('/', '.');
+
+ try {
+ final Class<?> cls = Class.forName(classname);
+ cls.getMethod("main", new Class[] { String[].class });
+ if (!getClass().equals(cls)) {
+ return cls;
+ }
+ } catch (NoClassDefFoundError e) {
+ // class has unresolved dependencies
+ return null;
+ } catch (ClassNotFoundException e) {
+ // class not in classpath
+ return null;
+ } catch (NoSuchMethodException e) {
+ // class does not have a main method
+ return null;
+ } catch (UnsupportedClassVersionError e){
+ // unsupported version
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Used to descent in directories, loads classes via {@link #load}
+ *
+ * @param directory
+ * where to search for class files
+ * @param allClasses
+ * add loaded classes to this collection
+ * @param packageName
+ * current package name for the diven directory
+ * @param recursive
+ * true to descent into subdirectories
+ */
+ private void addAllFilesInDirectory(File directory,
+ Collection<Class> allClasses, String packageName, boolean recursive) {
+ // Get the list of the files contained in the package
+ File[] files = directory.listFiles(getFileFilter());
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ // we are only interested in .class files
+ if (files[i].isDirectory()) {
+ if (recursive) {
+ addAllFilesInDirectory(files[i], allClasses,
+ packageName + files[i].getName() + ".", true);
+ }
+ } else {
+ Class result = load(packageName + files[i].getName());
+ if (result != null && !allClasses.contains(result)) {
+ allClasses.add(result);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return FileFilter for searching class files (no inner classes, only
+ * those with "Test" in the name)
+ */
+ private FileFilter getFileFilter() {
+ return new FileFilter() {
+ public boolean accept(File pathname) {
+ return (pathname.isDirectory() && !pathname.getName().startsWith("."))
+ || (pathname.getName().endsWith(".class")
+ && (pathname.getName().indexOf("Test") >= 0)
+ && pathname.getName().indexOf('$') < 0);
+ }
+ };
+ }
+
+ private void startApp(final Object[] appClass){
+ if (appClass == null){
+ JOptionPane.showMessageDialog(rootPane,
+ "Please select a test from the list",
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ new Thread(new Runnable(){
+ public void run(){
+ for (int i = 0; i < appClass.length; i++) {
+ Class<?> clazz = (Class)appClass[i];
+ try {
+ Object app = clazz.newInstance();
+ if (app instanceof Application) {
+ if (app instanceof SimpleApplication) {
+ final Method settingMethod = clazz.getMethod("setShowSettings", boolean.class);
+ settingMethod.invoke(app, showSetting);
+ }
+ final Method mainMethod = clazz.getMethod("start");
+ mainMethod.invoke(app);
+ Field contextField = Application.class.getDeclaredField("context");
+ contextField.setAccessible(true);
+ JmeContext context = null;
+ while (context == null) {
+ context = (JmeContext) contextField.get(app);
+ Thread.sleep(100);
+ }
+ while (!context.isCreated()) {
+ Thread.sleep(100);
+ }
+ while (context.isCreated()) {
+ Thread.sleep(100);
+ }
+ } else {
+ final Method mainMethod = clazz.getMethod("main", (new String[0]).getClass());
+ mainMethod.invoke(app, new Object[]{new String[0]});
+ }
+ // wait for destroy
+ System.gc();
+ } catch (IllegalAccessException ex) {
+ logger.log(Level.SEVERE, "Cannot access constructor: "+clazz.getName(), ex);
+ } catch (IllegalArgumentException ex) {
+ logger.log(Level.SEVERE, "main() had illegal argument: "+clazz.getName(), ex);
+ } catch (InvocationTargetException ex) {
+ logger.log(Level.SEVERE, "main() method had exception: "+clazz.getName(), ex);
+ } catch (InstantiationException ex) {
+ logger.log(Level.SEVERE, "Failed to create app: "+clazz.getName(), ex);
+ } catch (NoSuchMethodException ex){
+ logger.log(Level.SEVERE, "Test class doesn't have main method: "+clazz.getName(), ex);
+ } catch (Exception ex) {
+ logger.log(Level.SEVERE, "Cannot start test: "+clazz.getName(), ex);
+ ex.printStackTrace();
+ }
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Code to create components and action listeners.
+ *
+ * @param classes
+ * what Classes to show in the list box
+ */
+ private void setup(Vector<Class> classes) {
+ final JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout());
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(mainPanel, BorderLayout.CENTER);
+ mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+
+ final FilteredJList list = new FilteredJList();
+ list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ DefaultListModel model = new DefaultListModel();
+ for (Class c : classes) {
+ model.addElement(c);
+ }
+ list.setModel(model);
+
+ mainPanel.add(createSearchPanel(list), BorderLayout.NORTH);
+ mainPanel.add(new JScrollPane(list), BorderLayout.CENTER);
+
+ list.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ selectedClass = list.getSelectedValues();
+ }
+ });
+ list.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2 && selectedClass != null) {
+ startApp(selectedClass);
+ }
+ }
+ });
+ list.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+ startApp(selectedClass);
+ } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ dispose();
+ }
+ }
+ });
+
+ final JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+ mainPanel.add(buttonPanel, BorderLayout.PAGE_END);
+
+ final JButton okButton = new JButton("Ok");
+ okButton.setMnemonic('O');
+ buttonPanel.add(okButton);
+ getRootPane().setDefaultButton(okButton);
+ okButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ startApp(selectedClass);
+ }
+ });
+
+ final JButton cancelButton = new JButton("Cancel");
+ cancelButton.setMnemonic('C');
+ buttonPanel.add(cancelButton);
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ dispose();
+ }
+ });
+
+ pack();
+ center();
+ }
+
+ private class FilteredJList extends JList {
+ private static final long serialVersionUID = 1L;
+
+ private String filter;
+ private ListModel originalModel;
+
+ public void setModel(ListModel m) {
+ originalModel = m;
+ super.setModel(m);
+ }
+
+ private void update() {
+ if (filter == null || filter.length() == 0) {
+ super.setModel(originalModel);
+ }
+
+ DefaultListModel v = new DefaultListModel();
+ for (int i = 0; i < originalModel.getSize(); i++) {
+ Object o = originalModel.getElementAt(i);
+ String s = String.valueOf(o).toLowerCase();
+ if (s.contains(filter)) {
+ v.addElement(o);
+ }
+ }
+ super.setModel(v);
+ if (v.getSize() == 1) {
+ setSelectedIndex(0);
+ }
+ revalidate();
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+
+ public void setFilter(String filter) {
+ this.filter = filter.toLowerCase();
+ update();
+ }
+ }
+
+ /**
+ * center the frame.
+ */
+ private void center() {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension frameSize = this.getSize();
+ if (frameSize.height > screenSize.height) {
+ frameSize.height = screenSize.height;
+ }
+ if (frameSize.width > screenSize.width) {
+ frameSize.width = screenSize.width;
+ }
+ this.setLocation((screenSize.width - frameSize.width) / 2,
+ (screenSize.height - frameSize.height) / 2);
+ }
+
+ /**
+ * Start the chooser.
+ *
+ * @param args
+ * command line parameters
+ */
+ public static void main(final String[] args) {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ }
+ new TestChooser().start(args);
+ }
+
+ protected void start(String[] args) {
+ final Vector<Class> classes = new Vector<Class>();
+ logger.info("Composing Test list...");
+ addDisplayedClasses(classes);
+ setup(classes);
+ Class<?> cls;
+ setVisible(true);
+ }
+
+ protected void addDisplayedClasses(Vector<Class> classes) {
+ find("jme3test", true, classes);
+ }
+
+ private JPanel createSearchPanel(final FilteredJList classes) {
+ JPanel search = new JPanel();
+ search.setLayout(new BorderLayout());
+ search.add(new JLabel("Choose a Demo to start: Find: "),
+ BorderLayout.WEST);
+ final javax.swing.JTextField jtf = new javax.swing.JTextField();
+ jtf.getDocument().addDocumentListener(new DocumentListener() {
+ public void removeUpdate(DocumentEvent e) {
+ classes.setFilter(jtf.getText());
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ classes.setFilter(jtf.getText());
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ classes.setFilter(jtf.getText());
+ }
+ });
+ jtf.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ selectedClass = classes.getSelectedValues();
+ startApp(selectedClass);
+ }
+ });
+ final JCheckBox showSettingCheck = new JCheckBox("Show Setting");
+ showSettingCheck.setSelected(true);
+ showSettingCheck.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showSetting = showSettingCheck.isSelected();
+ }
+ });
+ jtf.setPreferredSize(new Dimension(100, 25));
+ search.add(jtf, BorderLayout.CENTER);
+ search.add(showSettingCheck, BorderLayout.EAST);
+ return search;
+ }
+}
diff --git a/engine/src/test/jme3test/animation/SubtitleTrack.java b/engine/src/test/jme3test/animation/SubtitleTrack.java
new file mode 100644
index 0000000..afa6e74
--- /dev/null
+++ b/engine/src/test/jme3test/animation/SubtitleTrack.java
@@ -0,0 +1,37 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jme3test.animation;
+
+import com.jme3.cinematic.events.GuiTrack;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.elements.render.TextRenderer;
+
+/**
+ *
+ * @author Nehon
+ */
+public class SubtitleTrack extends GuiTrack{
+ private String text="";
+
+ public SubtitleTrack(Nifty nifty, String screen,float initialDuration, String text) {
+ super(nifty, screen, initialDuration);
+ this.text=text;
+ }
+
+ @Override
+ public void onPlay() {
+ super.onPlay();
+ nifty.getScreen(screen).findElementByName("text").getRenderer(TextRenderer.class).setText(text);
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/engine/src/test/jme3test/animation/TestCameraMotionPath.java b/engine/src/test/jme3test/animation/TestCameraMotionPath.java
new file mode 100644
index 0000000..ba9688b
--- /dev/null
+++ b/engine/src/test/jme3test/animation/TestCameraMotionPath.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.animation;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.cinematic.MotionPath;
+import com.jme3.cinematic.MotionPathListener;
+import com.jme3.cinematic.events.MotionTrack;
+import com.jme3.font.BitmapText;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Spline.SplineType;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.CameraNode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.CameraControl.ControlDirection;
+import com.jme3.scene.shape.Box;
+
+public class TestCameraMotionPath extends SimpleApplication {
+
+ private Spatial teapot;
+ private boolean active = true;
+ private boolean playing = false;
+ private MotionPath path;
+ private MotionTrack cameraMotionControl;
+ private ChaseCamera chaser;
+ private CameraNode camNode;
+
+ public static void main(String[] args) {
+ TestCameraMotionPath app = new TestCameraMotionPath();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ createScene();
+ cam.setLocation(new Vector3f(8.4399185f, 11.189463f, 14.267577f));
+ camNode = new CameraNode("Motion cam", cam);
+ camNode.setControlDir(ControlDirection.SpatialToCamera);
+ camNode.getControl(0).setEnabled(false);
+ path = new MotionPath();
+ path.setCycle(true);
+ path.addWayPoint(new Vector3f(20, 3, 0));
+ path.addWayPoint(new Vector3f(0, 3, 20));
+ path.addWayPoint(new Vector3f(-20, 3, 0));
+ path.addWayPoint(new Vector3f(0, 3, -20));
+ path.setCurveTension(0.83f);
+ path.enableDebugShape(assetManager, rootNode);
+
+ cameraMotionControl = new MotionTrack(camNode, path);
+ cameraMotionControl.setLoopMode(LoopMode.Loop);
+ //cameraMotionControl.setDuration(15f);
+ cameraMotionControl.setLookAt(teapot.getWorldTranslation(), Vector3f.UNIT_Y);
+ cameraMotionControl.setDirectionType(MotionTrack.Direction.LookAt);
+
+ rootNode.attachChild(camNode);
+
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ final BitmapText wayPointsText = new BitmapText(guiFont, false);
+ wayPointsText.setSize(guiFont.getCharSet().getRenderedSize());
+
+ guiNode.attachChild(wayPointsText);
+
+ path.addListener(new MotionPathListener() {
+
+ public void onWayPointReach(MotionTrack control, int wayPointIndex) {
+ if (path.getNbWayPoints() == wayPointIndex + 1) {
+ wayPointsText.setText(control.getSpatial().getName() + " Finish!!! ");
+ } else {
+ wayPointsText.setText(control.getSpatial().getName() + " Reached way point " + wayPointIndex);
+ }
+ wayPointsText.setLocalTranslation((cam.getWidth() - wayPointsText.getLineWidth()) / 2, cam.getHeight(), 0);
+ }
+ });
+
+ flyCam.setEnabled(false);
+ chaser = new ChaseCamera(cam, teapot);
+ chaser.registerWithInput(inputManager);
+ chaser.setSmoothMotion(true);
+ chaser.setMaxDistance(50);
+ chaser.setDefaultDistance(50);
+ initInputs();
+
+ }
+
+ private void createScene() {
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 1f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Black);
+ mat.setColor("Diffuse", ColorRGBA.DarkGray);
+ mat.setColor("Specular", ColorRGBA.White.mult(0.6f));
+ Material matSoil = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ matSoil.setBoolean("UseMaterialColors", true);
+ matSoil.setColor("Ambient", ColorRGBA.Gray);
+ matSoil.setColor("Diffuse", ColorRGBA.Gray);
+ matSoil.setColor("Specular", ColorRGBA.Black);
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalScale(3);
+ teapot.setMaterial(mat);
+
+
+
+ rootNode.attachChild(teapot);
+ Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -1.0f, 0), 50, 1, 50));
+ soil.setMaterial(matSoil);
+ rootNode.attachChild(soil);
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection(new Vector3f(0, -1, 0).normalizeLocal());
+ light.setColor(ColorRGBA.White.mult(1.5f));
+ rootNode.addLight(light);
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("display_hidePath", new KeyTrigger(KeyInput.KEY_P));
+ inputManager.addMapping("SwitchPathInterpolation", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("tensionUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("tensionDown", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("play_stop", new KeyTrigger(KeyInput.KEY_SPACE));
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("display_hidePath") && keyPressed) {
+ if (active) {
+ active = false;
+ path.disableDebugShape();
+ } else {
+ active = true;
+ path.enableDebugShape(assetManager, rootNode);
+ }
+ }
+ if (name.equals("play_stop") && keyPressed) {
+ if (playing) {
+ playing = false;
+ cameraMotionControl.stop();
+ chaser.setEnabled(true);
+ camNode.getControl(0).setEnabled(false);
+ } else {
+ playing = true;
+ chaser.setEnabled(false);
+ camNode.getControl(0).setEnabled(true);
+ cameraMotionControl.play();
+ }
+ }
+
+ if (name.equals("SwitchPathInterpolation") && keyPressed) {
+ if (path.getPathSplineType() == SplineType.CatmullRom){
+ path.setPathSplineType(SplineType.Linear);
+ } else {
+ path.setPathSplineType(SplineType.CatmullRom);
+ }
+ }
+
+ if (name.equals("tensionUp") && keyPressed) {
+ path.setCurveTension(path.getCurveTension() + 0.1f);
+ System.err.println("Tension : " + path.getCurveTension());
+ }
+ if (name.equals("tensionDown") && keyPressed) {
+ path.setCurveTension(path.getCurveTension() - 0.1f);
+ System.err.println("Tension : " + path.getCurveTension());
+ }
+
+
+ }
+ };
+
+ inputManager.addListener(acl, "display_hidePath", "play_stop", "SwitchPathInterpolation", "tensionUp", "tensionDown");
+
+ }
+}
diff --git a/engine/src/test/jme3test/animation/TestCinematic.java b/engine/src/test/jme3test/animation/TestCinematic.java
new file mode 100644
index 0000000..a213828
--- /dev/null
+++ b/engine/src/test/jme3test/animation/TestCinematic.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.animation;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.AnimationFactory;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.MotionPath;
+import com.jme3.cinematic.PlayState;
+import com.jme3.cinematic.events.*;
+import com.jme3.font.BitmapText;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.FadeFilter;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.CameraNode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.shadow.PssmShadowRenderer;
+import de.lessvoid.nifty.Nifty;
+
+public class TestCinematic extends SimpleApplication {
+
+ private Spatial model;
+ private Spatial teapot;
+ private MotionPath path;
+ private MotionTrack cameraMotionTrack;
+ private Cinematic cinematic;
+ private ChaseCamera chaseCam;
+ private FilterPostProcessor fpp;
+ private FadeFilter fade;
+ private float time = 0;
+
+ public static void main(String[] args) {
+ TestCinematic app = new TestCinematic();
+ app.start();
+
+
+
+ }
+
+ @Override
+ public void simpleInitApp() {
+ //just some text
+ NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(getAssetManager(),
+ getInputManager(),
+ getAudioRenderer(),
+ getGuiViewPort());
+ Nifty nifty;
+ nifty = niftyDisplay.getNifty();
+ nifty.fromXmlWithoutStartScreen("Interface/Nifty/CinematicTest.xml");
+ getGuiViewPort().addProcessor(niftyDisplay);
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ final BitmapText text = new BitmapText(guiFont, false);
+ text.setSize(guiFont.getCharSet().getRenderedSize());
+ text.setText("Press enter to play/pause cinematic");
+ text.setLocalTranslation((cam.getWidth() - text.getLineWidth()) / 2, cam.getHeight(), 0);
+ guiNode.attachChild(text);
+
+
+ createScene();
+
+ cinematic = new Cinematic(rootNode, 20);
+ stateManager.attach(cinematic);
+
+ createCameraMotion();
+
+ //creating spatial animation for the teapot
+ AnimationFactory factory = new AnimationFactory(20, "teapotAnim");
+ factory.addTimeTranslation(0, new Vector3f(10, 0, 10));
+ factory.addTimeTranslation(20, new Vector3f(10, 0, -10));
+ factory.addTimeScale(10, new Vector3f(4, 4, 4));
+ factory.addTimeScale(20, new Vector3f(1, 1, 1));
+ factory.addTimeRotationAngles(20, 0, 4 * FastMath.TWO_PI, 0);
+ AnimControl control = new AnimControl();
+ control.addAnim(factory.buildAnimation());
+ teapot.addControl(control);
+
+ //fade in
+ cinematic.addCinematicEvent(0, new FadeEvent(true));
+ // cinematic.activateCamera(0, "aroundCam");
+ cinematic.addCinematicEvent(0, new AnimationTrack(teapot, "teapotAnim", LoopMode.DontLoop));
+ cinematic.addCinematicEvent(0, cameraMotionTrack);
+ cinematic.addCinematicEvent(0, new SoundTrack("Sound/Environment/Nature.ogg", LoopMode.Loop));
+ cinematic.addCinematicEvent(3f, new SoundTrack("Sound/Effects/kick.wav"));
+ cinematic.addCinematicEvent(3, new SubtitleTrack(nifty, "start", 3, "jMonkey engine really kicks A..."));
+ cinematic.addCinematicEvent(5.1f, new SoundTrack("Sound/Effects/Beep.ogg", 1));
+ cinematic.addCinematicEvent(2, new AnimationTrack(model, "Walk", LoopMode.Loop));
+ cinematic.activateCamera(0, "topView");
+ // cinematic.activateCamera(10, "aroundCam");
+
+ //fade out
+ cinematic.addCinematicEvent(19, new FadeEvent(false));
+// cinematic.addCinematicEvent(19, new AbstractCinematicEvent() {
+//
+// @Override
+// public void onPlay() {
+// fade.setDuration(1f / cinematic.getSpeed());
+// fade.fadeOut();
+//
+// }
+//
+// @Override
+// public void onUpdate(float tpf) {
+// }
+//
+// @Override
+// public void onStop() {
+// }
+//
+// @Override
+// public void onPause() {
+// }
+// });
+
+ cinematic.addListener(new CinematicEventListener() {
+
+ public void onPlay(CinematicEvent cinematic) {
+ chaseCam.setEnabled(false);
+ System.out.println("play");
+ }
+
+ public void onPause(CinematicEvent cinematic) {
+ System.out.println("pause");
+ }
+
+ public void onStop(CinematicEvent cinematic) {
+ chaseCam.setEnabled(true);
+ fade.setValue(1);
+ System.out.println("stop");
+ }
+ });
+
+ //cinematic.setSpeed(2);
+ flyCam.setEnabled(false);
+ chaseCam = new ChaseCamera(cam, model, inputManager);
+ initInputs();
+
+ }
+
+ private void createCameraMotion() {
+
+ CameraNode camNode = cinematic.bindCamera("topView", cam);
+ camNode.setLocalTranslation(new Vector3f(0, 50, 0));
+ camNode.lookAt(teapot.getLocalTranslation(), Vector3f.UNIT_Y);
+
+ CameraNode camNode2 = cinematic.bindCamera("aroundCam", cam);
+ path = new MotionPath();
+ path.setCycle(true);
+ path.addWayPoint(new Vector3f(20, 3, 0));
+ path.addWayPoint(new Vector3f(0, 3, 20));
+ path.addWayPoint(new Vector3f(-20, 3, 0));
+ path.addWayPoint(new Vector3f(0, 3, -20));
+ path.setCurveTension(0.83f);
+ cameraMotionTrack = new MotionTrack(camNode2, path);
+ cameraMotionTrack.setLoopMode(LoopMode.Loop);
+ cameraMotionTrack.setLookAt(model.getWorldTranslation(), Vector3f.UNIT_Y);
+ cameraMotionTrack.setDirectionType(MotionTrack.Direction.LookAt);
+
+ }
+
+ private void createScene() {
+
+ model = (Spatial) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ model.center();
+ model.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(model);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Cyan);
+
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalTranslation(10, 0, 10);
+ teapot.setMaterial(mat);
+ teapot.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(teapot);
+
+ Material matSoil = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ matSoil.setBoolean("UseMaterialColors", true);
+ matSoil.setColor("Ambient", ColorRGBA.Gray);
+ matSoil.setColor("Diffuse", ColorRGBA.Green);
+ matSoil.setColor("Specular", ColorRGBA.Black);
+
+ Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -6.0f, 0), 50, 1, 50));
+ soil.setMaterial(matSoil);
+ soil.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(soil);
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection(new Vector3f(0, -1, -1).normalizeLocal());
+ light.setColor(ColorRGBA.White.mult(1.5f));
+ rootNode.addLight(light);
+
+ fpp = new FilterPostProcessor(assetManager);
+ fade = new FadeFilter();
+ fpp.addFilter(fade);
+
+ if (renderer.getCaps().contains(Caps.GLSL100)) {
+ PssmShadowRenderer pssm = new PssmShadowRenderer(assetManager, 512, 1);
+ pssm.setDirection(new Vector3f(0, -1, -1).normalizeLocal());
+ pssm.setShadowIntensity(0.4f);
+ viewPort.addProcessor(pssm);
+ viewPort.addProcessor(fpp);
+ }
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("togglePause", new KeyTrigger(keyInput.KEY_RETURN));
+ inputManager.addMapping("navFwd", new KeyTrigger(keyInput.KEY_RIGHT));
+ inputManager.addMapping("navBack", new KeyTrigger(keyInput.KEY_LEFT));
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("togglePause") && keyPressed) {
+ if (cinematic.getPlayState() == PlayState.Playing) {
+ cinematic.pause();
+ time = cinematic.getTime();
+ } else {
+ cinematic.play();
+ }
+ }
+
+ if (cinematic.getPlayState() != PlayState.Playing) {
+ if (name.equals("navFwd") && keyPressed) {
+ time += 0.25;
+ FastMath.clamp(time, 0, cinematic.getInitialDuration());
+ cinematic.setTime(time);
+ }
+ if (name.equals("navBack") && keyPressed) {
+ time -= 0.25;
+ FastMath.clamp(time, 0, cinematic.getInitialDuration());
+ cinematic.setTime(time);
+ }
+
+ }
+ }
+ };
+ inputManager.addListener(acl, "togglePause", "navFwd", "navBack");
+ }
+
+ private class FadeEvent extends AbstractCinematicEvent {
+
+ boolean in = true;
+ float value = 0;
+
+ public FadeEvent(boolean in) {
+ super(1);
+ this.in = in;
+ value = in ? 0 : 1;
+ }
+
+ @Override
+ public void onPlay() {
+
+ fade.setDuration(1f / cinematic.getSpeed());
+ if (in) {
+ fade.fadeIn();
+ } else {
+ fade.fadeOut();
+ }
+ fade.setValue(value);
+
+ }
+
+ @Override
+ public void setTime(float time) {
+ super.setTime(time);
+ if (time >= fade.getDuration()) {
+ value = in ? 1 : 0;
+ fade.setValue(value);
+ } else {
+ value = time;
+ if (in) {
+ fade.setValue(time / cinematic.getSpeed());
+ } else {
+ fade.setValue(1 - time / cinematic.getSpeed());
+ }
+ }
+ }
+
+ @Override
+ public void onUpdate(float tpf) {
+ }
+
+ @Override
+ public void onStop() {
+ }
+
+ @Override
+ public void onPause() {
+ value = fade.getValue();
+ fade.pause();
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/animation/TestMotionPath.java b/engine/src/test/jme3test/animation/TestMotionPath.java
new file mode 100644
index 0000000..50cafb2
--- /dev/null
+++ b/engine/src/test/jme3test/animation/TestMotionPath.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.animation;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.cinematic.MotionPath;
+import com.jme3.cinematic.MotionPathListener;
+import com.jme3.cinematic.events.MotionTrack;
+import com.jme3.font.BitmapText;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Spline.SplineType;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+
+public class TestMotionPath extends SimpleApplication {
+
+ private Spatial teapot;
+ private boolean active = true;
+ private boolean playing = false;
+ private MotionPath path;
+ private MotionTrack motionControl;
+
+ public static void main(String[] args) {
+ TestMotionPath app = new TestMotionPath();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ createScene();
+ cam.setLocation(new Vector3f(8.4399185f, 11.189463f, 14.267577f));
+ path = new MotionPath();
+ path.addWayPoint(new Vector3f(10, 3, 0));
+ path.addWayPoint(new Vector3f(10, 3, 10));
+ path.addWayPoint(new Vector3f(-40, 3, 10));
+ path.addWayPoint(new Vector3f(-40, 3, 0));
+ path.addWayPoint(new Vector3f(-40, 8, 0));
+ path.addWayPoint(new Vector3f(10, 8, 0));
+ path.addWayPoint(new Vector3f(10, 8, 10));
+ path.addWayPoint(new Vector3f(15, 8, 10));
+ path.enableDebugShape(assetManager, rootNode);
+
+ motionControl = new MotionTrack(teapot,path);
+ motionControl.setDirectionType(MotionTrack.Direction.PathAndRotation);
+ motionControl.setRotation(new Quaternion().fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y));
+ motionControl.setInitialDuration(10f);
+ motionControl.setSpeed(2f);
+
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ final BitmapText wayPointsText = new BitmapText(guiFont, false);
+ wayPointsText.setSize(guiFont.getCharSet().getRenderedSize());
+
+ guiNode.attachChild(wayPointsText);
+
+ path.addListener(new MotionPathListener() {
+
+ public void onWayPointReach(MotionTrack control, int wayPointIndex) {
+ if (path.getNbWayPoints() == wayPointIndex + 1) {
+ wayPointsText.setText(control.getSpatial().getName() + "Finished!!! ");
+ } else {
+ wayPointsText.setText(control.getSpatial().getName() + " Reached way point " + wayPointIndex);
+ }
+ wayPointsText.setLocalTranslation((cam.getWidth() - wayPointsText.getLineWidth()) / 2, cam.getHeight(), 0);
+ }
+ });
+
+ flyCam.setEnabled(false);
+ ChaseCamera chaser = new ChaseCamera(cam, teapot);
+
+ // chaser.setEnabled(false);
+ chaser.registerWithInput(inputManager);
+ initInputs();
+
+ }
+
+ private void createScene() {
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 1f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Black);
+ mat.setColor("Diffuse", ColorRGBA.DarkGray);
+ mat.setColor("Specular", ColorRGBA.White.mult(0.6f));
+ Material matSoil = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ matSoil.setBoolean("UseMaterialColors", true);
+ matSoil.setColor("Ambient", ColorRGBA.Black);
+ matSoil.setColor("Diffuse", ColorRGBA.Black);
+ matSoil.setColor("Specular", ColorRGBA.Black);
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setName("Teapot");
+ teapot.setLocalScale(3);
+ teapot.setMaterial(mat);
+
+
+ rootNode.attachChild(teapot);
+ Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -1.0f, 0), 50, 1, 50));
+ soil.setMaterial(matSoil);
+
+ rootNode.attachChild(soil);
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection(new Vector3f(0, -1, 0).normalizeLocal());
+ light.setColor(ColorRGBA.White.mult(1.5f));
+ rootNode.addLight(light);
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("display_hidePath", new KeyTrigger(KeyInput.KEY_P));
+ inputManager.addMapping("SwitchPathInterpolation", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("tensionUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("tensionDown", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("play_stop", new KeyTrigger(KeyInput.KEY_SPACE));
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("display_hidePath") && keyPressed) {
+ if (active) {
+ active = false;
+ path.disableDebugShape();
+ } else {
+ active = true;
+ path.enableDebugShape(assetManager, rootNode);
+ }
+ }
+ if (name.equals("play_stop") && keyPressed) {
+ if (playing) {
+ playing = false;
+ motionControl.stop();
+ } else {
+ playing = true;
+ motionControl.play();
+ }
+ }
+
+ if (name.equals("SwitchPathInterpolation") && keyPressed) {
+ if (path.getPathSplineType() == SplineType.CatmullRom){
+ path.setPathSplineType(SplineType.Linear);
+ } else {
+ path.setPathSplineType(SplineType.CatmullRom);
+ }
+ }
+
+ if (name.equals("tensionUp") && keyPressed) {
+ path.setCurveTension(path.getCurveTension() + 0.1f);
+ System.err.println("Tension : " + path.getCurveTension());
+ }
+ if (name.equals("tensionDown") && keyPressed) {
+ path.setCurveTension(path.getCurveTension() - 0.1f);
+ System.err.println("Tension : " + path.getCurveTension());
+ }
+
+
+ }
+ };
+
+ inputManager.addListener(acl, "display_hidePath", "play_stop", "SwitchPathInterpolation", "tensionUp", "tensionDown");
+
+ }
+}
diff --git a/engine/src/test/jme3test/app/TestApplication.java b/engine/src/test/jme3test/app/TestApplication.java
new file mode 100644
index 0000000..ae07f8a
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestApplication.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app;
+
+import com.jme3.app.Application;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+
+/**
+ * Test Application functionality, such as create, restart, destroy, etc.
+ * @author Kirill
+ */
+public class TestApplication {
+
+ public static void main(String[] args) throws InterruptedException{
+ System.out.println("Creating application..");
+ Application app = new Application();
+ System.out.println("Starting application in LWJGL mode..");
+ app.start();
+ System.out.println("Waiting 5 seconds");
+ Thread.sleep(5000);
+ System.out.println("Closing application..");
+ app.stop();
+
+ Thread.sleep(2000);
+ System.out.println("Starting in fullscreen mode");
+ app = new Application();
+ AppSettings settings = new AppSettings(true);
+ settings.setFullscreen(true);
+ settings.setResolution(-1,-1); // current width/height
+ app.setSettings(settings);
+ app.start();
+ Thread.sleep(5000);
+ app.stop();
+
+ Thread.sleep(2000);
+ System.out.println("Creating offscreen buffer application");
+ app = new Application();
+ app.start(Type.OffscreenSurface);
+ Thread.sleep(3000);
+ System.out.println("Destroying offscreen buffer");
+ app.stop();
+ }
+
+}
diff --git a/engine/src/test/jme3test/app/TestBareBonesApp.java b/engine/src/test/jme3test/app/TestBareBonesApp.java
new file mode 100644
index 0000000..ee46388
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestBareBonesApp.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app;
+
+import com.jme3.app.Application;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+/**
+ * Test a bare-bones application, without SimpleApplication.
+ */
+public class TestBareBonesApp extends Application {
+
+ private Geometry boxGeom;
+
+ public static void main(String[] args){
+ TestBareBonesApp app = new TestBareBonesApp();
+ app.start();
+ }
+
+ @Override
+ public void initialize(){
+ super.initialize();
+
+ System.out.println("Initialize");
+
+ // create a box
+ boxGeom = new Geometry("Box", new Box(Vector3f.ZERO, 2, 2, 2));
+
+ // load some default material
+ boxGeom.setMaterial(assetManager.loadMaterial("Interface/Logo/Logo.j3m"));
+
+ // attach box to display in primary viewport
+ viewPort.attachScene(boxGeom);
+ }
+
+ @Override
+ public void update(){
+ super.update();
+
+ // do some animation
+ float tpf = timer.getTimePerFrame();
+ boxGeom.rotate(tpf * 2, tpf * 4, tpf * 3);
+
+ // dont forget to update the scenes
+ boxGeom.updateLogicalState(tpf);
+ boxGeom.updateGeometricState();
+
+ // render the viewports
+ renderManager.render(tpf, context.isRenderable());
+ }
+
+ @Override
+ public void destroy(){
+ super.destroy();
+
+ System.out.println("Destroy");
+ }
+}
diff --git a/engine/src/test/jme3test/app/TestChangeAppIcon.java b/engine/src/test/jme3test/app/TestChangeAppIcon.java
new file mode 100644
index 0000000..004bd32
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestChangeAppIcon.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.app;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.system.AppSettings;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.logging.Logger;
+import javax.imageio.ImageIO;
+
+public class TestChangeAppIcon extends SimpleApplication {
+
+ private static final Logger log=Logger.getLogger(TestChangeAppIcon.class.getName());
+
+ public static void main(String[] args) {
+ TestChangeAppIcon app = new TestChangeAppIcon();
+ AppSettings settings = new AppSettings(true);
+
+ try {
+ Class<TestChangeAppIcon> clazz = TestChangeAppIcon.class;
+
+ settings.setIcons(new BufferedImage[]{
+ ImageIO.read(clazz.getResourceAsStream("/Interface/icons/SmartMonkey256.png")),
+ ImageIO.read(clazz.getResourceAsStream("/Interface/icons/SmartMonkey128.png")),
+ ImageIO.read(clazz.getResourceAsStream("/Interface/icons/SmartMonkey32.png")),
+ ImageIO.read(clazz.getResourceAsStream("/Interface/icons/SmartMonkey16.png")),
+ });
+ } catch (IOException e) {
+ log.log(java.util.logging.Level.WARNING, "Unable to load program icons", e);
+ }
+ app.setSettings(settings);
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // Write text on the screen (HUD)
+ guiNode.detachAllChildren();
+ BitmapText helloText = new BitmapText(guiFont);
+ helloText.setText("The icon of the app should be a smart monkey!");
+ helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
+ guiNode.attachChild(helloText);
+ }
+}
diff --git a/engine/src/test/jme3test/app/TestContextRestart.java b/engine/src/test/jme3test/app/TestContextRestart.java
new file mode 100644
index 0000000..b83f336
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestContextRestart.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app;
+
+import com.jme3.app.Application;
+import com.jme3.system.AppSettings;
+
+public class TestContextRestart {
+
+ public static void main(String[] args) throws InterruptedException{
+ AppSettings settings = new AppSettings(true);
+
+ final Application app = new Application();
+ app.setSettings(settings);
+ app.start();
+
+ Thread.sleep(3000);
+
+ settings.setFullscreen(true);
+ settings.setResolution(-1, -1);
+ app.setSettings(settings);
+ app.restart();
+
+ Thread.sleep(3000);
+
+ app.stop();
+ }
+
+}
diff --git a/engine/src/test/jme3test/app/TestIDList.java b/engine/src/test/jme3test/app/TestIDList.java
new file mode 100644
index 0000000..4e33eaa
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestIDList.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app;
+
+import com.jme3.renderer.IDList;
+import java.util.*;
+
+public class TestIDList {
+
+ static class StateCol {
+
+ static Random rand = new Random();
+
+ Map<Integer, Object> objs = new HashMap<Integer, Object>();
+
+ public StateCol(){
+ // populate with free ids
+ List<Integer> freeIds = new ArrayList();
+ for (int i = 0; i < 16; i++){
+ freeIds.add(i);
+ }
+
+ // create random
+ int numStates = rand.nextInt(6) + 1;
+ for (int i = 0; i < numStates; i++){
+ // remove a random id from free id list
+ int idx = rand.nextInt(freeIds.size());
+ int id = freeIds.remove(idx);
+
+ objs.put(id, new Object());
+ }
+ }
+
+ public void print(){
+ System.out.println("-----------------");
+
+ Set<Integer> keys = objs.keySet();
+ Integer[] keysArr = keys.toArray(new Integer[0]);
+ Arrays.sort(keysArr);
+ for (int i = 0; i < keysArr.length; i++){
+ System.out.println(keysArr[i]+" => "+objs.get(keysArr[i]).hashCode());
+ }
+ }
+
+ }
+
+ static IDList list = new IDList();
+ static int boundSlot = 0;
+
+ static Object[] slots = new Object[16];
+ static boolean[] enabledSlots = new boolean[16];
+
+ static void enable(int slot){
+ System.out.println("Enabled SLOT["+slot+"]");
+ if (enabledSlots[slot] == true){
+ System.err.println("FAIL! Extra state change");
+ }
+ enabledSlots[slot] = true;
+ }
+
+ static void disable(int slot){
+ System.out.println("Disabled SLOT["+slot+"]");
+ if (enabledSlots[slot] == false){
+ System.err.println("FAIL! Extra state change");
+ }
+ enabledSlots[slot] = false;
+ }
+
+ static void setSlot(int slot, Object val){
+ if (!list.moveToNew(slot)){
+ enable(slot);
+ }
+ if (slots[slot] != val){
+ System.out.println("SLOT["+slot+"] = "+val.hashCode());
+ slots[slot] = val;
+ }
+ }
+
+ static void checkSlots(StateCol state){
+ for (int i = 0; i < 16; i++){
+ if (slots[i] != null && enabledSlots[i] == false){
+ System.err.println("FAIL! SLOT["+i+"] assigned, but disabled");
+ }
+ if (slots[i] == null && enabledSlots[i] == true){
+ System.err.println("FAIL! SLOT["+i+"] enabled, but not assigned");
+ }
+
+ Object val = state.objs.get(i);
+ if (val != null){
+ if (slots[i] != val)
+ System.err.println("FAIL! SLOT["+i+"] does not contain correct value");
+ if (!enabledSlots[i])
+ System.err.println("FAIL! SLOT["+i+"] is not enabled");
+ }else{
+ if (slots[i] != null)
+ System.err.println("FAIL! SLOT["+i+"] is not set");
+ if (enabledSlots[i])
+ System.err.println("FAIL! SLOT["+i+"] is enabled");
+ }
+ }
+ }
+
+ static void clearSlots(){
+ for (int i = 0; i < list.oldLen; i++){
+ int slot = list.oldList[i];
+ disable(slot);
+ slots[slot] = null;
+ }
+ list.copyNewToOld();
+// context.attribIndexList.print();
+ }
+
+ static void setState(StateCol state){
+ state.print();
+ for (Map.Entry<Integer, Object> entry : state.objs.entrySet()){
+ setSlot(entry.getKey(), entry.getValue());
+ }
+ clearSlots();
+ checkSlots(state);
+ }
+
+ public static void main(String[] args){
+ StateCol[] states = new StateCol[20];
+ for (int i = 0; i < states.length; i++)
+ states[i] = new StateCol();
+
+ // shuffle would be useful here..
+
+ for (int i = 0; i < states.length; i++){
+ setState(states[i]);
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/app/TestReleaseDirectMemory.java b/engine/src/test/jme3test/app/TestReleaseDirectMemory.java
new file mode 100644
index 0000000..84a58ae
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestReleaseDirectMemory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+public class TestReleaseDirectMemory extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestReleaseDirectMemory app = new TestReleaseDirectMemory();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ ByteBuffer buf = BufferUtils.createByteBuffer(500000);
+ BufferUtils.destroyDirectBuffer(buf);
+
+ FloatBuffer buf2 = BufferUtils.createFloatBuffer(500000);
+ BufferUtils.destroyDirectBuffer(buf2);
+ }
+
+}
diff --git a/engine/src/test/jme3test/app/TestTempVars.java b/engine/src/test/jme3test/app/TestTempVars.java
new file mode 100644
index 0000000..7102888
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestTempVars.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.app;
+
+import com.jme3.math.Vector3f;
+import com.jme3.util.TempVars;
+
+public class TestTempVars {
+
+ private static final int ITERATIONS = 10000000;
+ private static final int NANOS_TO_MS = 1000000;
+
+ private static final Vector3f sumCompute = new Vector3f();
+
+ public static void main(String[] args) {
+ long milliseconds, nanos;
+
+ for (int i = 0; i < 4; i++){
+ System.gc();
+ }
+
+// sumCompute.set(0, 0, 0);
+// long nanos = System.nanoTime();
+// for (int i = 0; i < ITERATIONS; i++) {
+// recursiveMethod(0);
+// }
+// long milliseconds = (System.nanoTime() - nanos) / NANOS_TO_MS;
+// System.out.println("100 million TempVars calls with 5 recursions: " + milliseconds + " ms");
+// System.out.println(sumCompute);
+
+ sumCompute.set(0, 0, 0);
+ nanos = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ methodThatUsesTempVars();
+ }
+ milliseconds = (System.nanoTime() - nanos) / NANOS_TO_MS;
+ System.out.println("100 million TempVars calls: " + milliseconds + " ms");
+ System.out.println(sumCompute);
+
+ sumCompute.set(0, 0, 0);
+ nanos = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ methodThatUsesAllocation();
+ }
+ milliseconds = (System.nanoTime() - nanos) / NANOS_TO_MS;
+ System.out.println("100 million allocation calls: " + milliseconds + " ms");
+ System.out.println(sumCompute);
+
+ nanos = System.nanoTime();
+ for (int i = 0; i < 10; i++){
+ System.gc();
+ }
+ milliseconds = (System.nanoTime() - nanos) / NANOS_TO_MS;
+ System.out.println("cleanup time after allocation calls: " + milliseconds + " ms");
+ }
+
+ public static void methodThatUsesAllocation(){
+ Vector3f vector = new Vector3f();
+ vector.set(0.1f, 0.2f, 0.3f);
+ sumCompute.addLocal(vector);
+ }
+
+ public static void recursiveMethod(int recurse) {
+ TempVars vars = TempVars.get();
+ {
+ vars.vect1.set(0.1f, 0.2f, 0.3f);
+
+ if (recurse < 4) {
+ recursiveMethod(recurse + 1);
+ }
+
+ sumCompute.addLocal(vars.vect1);
+ }
+ vars.release();
+ }
+
+ public static void methodThatUsesTempVars() {
+ TempVars vars = TempVars.get();
+ {
+ vars.vect1.set(0.1f, 0.2f, 0.3f);
+ sumCompute.addLocal(vars.vect1);
+ }
+ vars.release();
+ }
+}
diff --git a/engine/src/test/jme3test/app/state/RootNodeState.java b/engine/src/test/jme3test/app/state/RootNodeState.java
new file mode 100644
index 0000000..fddb63a
--- /dev/null
+++ b/engine/src/test/jme3test/app/state/RootNodeState.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app.state;
+
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.scene.Node;
+
+public class RootNodeState extends AbstractAppState {
+
+ private Node rootNode = new Node("Root Node");
+
+ public Node getRootNode(){
+ return rootNode;
+ }
+
+ @Override
+ public void update(float tpf) {
+ super.update(tpf);
+
+ rootNode.updateLogicalState(tpf);
+ rootNode.updateGeometricState();
+ }
+
+}
diff --git a/engine/src/test/jme3test/app/state/TestAppStates.java b/engine/src/test/jme3test/app/state/TestAppStates.java
new file mode 100644
index 0000000..721bc04
--- /dev/null
+++ b/engine/src/test/jme3test/app/state/TestAppStates.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import com.jme3.scene.Spatial;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+
+public class TestAppStates extends Application {
+
+ public static void main(String[] args){
+ TestAppStates app = new TestAppStates();
+ app.start();
+ }
+
+ @Override
+ public void start(JmeContext.Type contextType){
+ AppSettings settings = new AppSettings(true);
+ settings.setResolution(1024, 768);
+ setSettings(settings);
+
+ super.start(contextType);
+ }
+
+ @Override
+ public void initialize(){
+ super.initialize();
+
+ System.out.println("Initialize");
+
+ RootNodeState state = new RootNodeState();
+ viewPort.attachScene(state.getRootNode());
+ stateManager.attach(state);
+
+ Spatial model = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ model.scale(3);
+ model.setMaterial(assetManager.loadMaterial("Interface/Logo/Logo.j3m"));
+ state.getRootNode().attachChild(model);
+
+ NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
+ inputManager,
+ audioRenderer,
+ guiViewPort);
+ niftyDisplay.getNifty().fromXml("Interface/Nifty/HelloJme.xml", "start");
+ guiViewPort.addProcessor(niftyDisplay);
+ }
+
+ @Override
+ public void update(){
+ super.update();
+
+ // do some animation
+ float tpf = timer.getTimePerFrame();
+
+ stateManager.update(tpf);
+ stateManager.render(renderManager);
+
+ // render the viewports
+ renderManager.render(tpf, context.isRenderable());
+ }
+
+ @Override
+ public void destroy(){
+ super.destroy();
+
+ System.out.println("Destroy");
+ }
+}
diff --git a/engine/src/test/jme3test/asset/TestAbsoluteLocators.java b/engine/src/test/jme3test/asset/TestAbsoluteLocators.java
new file mode 100644
index 0000000..b26a98c
--- /dev/null
+++ b/engine/src/test/jme3test/asset/TestAbsoluteLocators.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.asset;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.DesktopAssetManager;
+import com.jme3.asset.plugins.ClasspathLocator;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.plugins.WAVLoader;
+import com.jme3.texture.Texture;
+import com.jme3.texture.plugins.AWTLoader;
+
+public class TestAbsoluteLocators {
+ public static void main(String[] args){
+ AssetManager am = new DesktopAssetManager();
+
+ am.registerLoader(AWTLoader.class.getName(), "jpg");
+ am.registerLoader(WAVLoader.class.getName(), "wav");
+
+ // register absolute locator
+ am.registerLocator("/", ClasspathLocator.class.getName());
+
+ // find a sound
+ AudioData audio = am.loadAudio("Sound/Effects/Gun.wav");
+
+ // find a texture
+ Texture tex = am.loadTexture("Textures/Terrain/Pond/Pond.jpg");
+
+ if (audio == null)
+ throw new RuntimeException("Cannot find audio!");
+ else
+ System.out.println("Audio loaded from Sounds/Effects/Gun.wav");
+
+ if (tex == null)
+ throw new RuntimeException("Cannot find texture!");
+ else
+ System.out.println("Texture loaded from Textures/Terrain/Pond/Pond.jpg");
+
+ System.out.println("Success!");
+ }
+}
diff --git a/engine/src/test/jme3test/asset/TestAssetCache.java b/engine/src/test/jme3test/asset/TestAssetCache.java
new file mode 100644
index 0000000..066fe21
--- /dev/null
+++ b/engine/src/test/jme3test/asset/TestAssetCache.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.asset;
+
+import com.jme3.asset.Asset;
+import com.jme3.asset.AssetCache;
+import com.jme3.asset.AssetKey;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestAssetCache {
+
+ /**
+ * Keep references to loaded assets
+ */
+ private final static boolean KEEP_REFERENCES = false;
+
+ /**
+ * Enable smart cache use
+ */
+ private final static boolean USE_SMART_CACHE = true;
+
+ /**
+ * Enable cloneable asset use
+ */
+ private final static boolean CLONEABLE_ASSET = true;
+
+ private static int counter = 0;
+
+ private static class DummyData implements Asset {
+
+ private AssetKey key;
+ private byte[] data = new byte[10000];
+
+ public byte[] getData(){
+ return data;
+ }
+
+ public AssetKey getKey() {
+ return key;
+ }
+
+ public void setKey(AssetKey key) {
+ this.key = key;
+ }
+ }
+
+ private static class SmartKey extends AssetKey {
+
+ public SmartKey(){
+ super(".");
+ counter++;
+ }
+
+ @Override
+ public int hashCode(){
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object other){
+ return false;
+ }
+
+ @Override
+ public boolean useSmartCache(){
+ return true;
+ }
+
+ @Override
+ public Object createClonedInstance(Object asset){
+ DummyData data = new DummyData();
+ return data;
+ }
+ }
+
+ private static class DumbKey extends AssetKey {
+
+ public DumbKey(){
+ super(".");
+ counter++;
+ }
+
+ @Override
+ public int hashCode(){
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object other){
+ return false;
+ }
+
+ @Override
+ public Object createClonedInstance(Object asset){
+ if (CLONEABLE_ASSET){
+ DummyData data = new DummyData();
+ return data;
+ }else{
+ return asset;
+ }
+ }
+ }
+
+ public static void main(String[] args){
+ List<Object> refs = new ArrayList<Object>(5000);
+
+ AssetCache cache = new AssetCache();
+
+ System.gc();
+ System.gc();
+ System.gc();
+ System.gc();
+
+ long memory = Runtime.getRuntime().freeMemory();
+
+ while (true){
+ AssetKey key;
+
+ if (USE_SMART_CACHE){
+ key = new SmartKey();
+ }else{
+ key = new DumbKey();
+ }
+
+ DummyData data = new DummyData();
+ cache.addToCache(key, data);
+
+ if (KEEP_REFERENCES){
+ refs.add(data);
+ }
+
+ if ((counter % 100) == 0){
+ long newMem = Runtime.getRuntime().freeMemory();
+ System.out.println("Allocated objects: " + counter);
+ System.out.println("Allocated memory: " + ((memory - newMem)/1024) + "K" );
+ memory = newMem;
+ }
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/asset/TestManyLocators.java b/engine/src/test/jme3test/asset/TestManyLocators.java
new file mode 100644
index 0000000..a11741e
--- /dev/null
+++ b/engine/src/test/jme3test/asset/TestManyLocators.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.asset;
+
+import com.jme3.asset.*;
+import com.jme3.asset.plugins.ClasspathLocator;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.UrlLocator;
+import com.jme3.asset.plugins.ZipLocator;
+
+public class TestManyLocators {
+ public static void main(String[] args){
+ AssetManager am = new DesktopAssetManager();
+
+ am.registerLocator("http://www.jmonkeyengine.com/wp-content/uploads/2010/09/",
+ UrlLocator.class);
+
+ am.registerLocator("town.zip", ZipLocator.class);
+ am.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip",
+ HttpZipLocator.class);
+
+
+ am.registerLocator("/", ClasspathLocator.class);
+
+
+
+ // Try loading from Core-Data source package
+ AssetInfo a = am.locateAsset(new AssetKey<Object>("Interface/Fonts/Default.fnt"));
+
+ // Try loading from town scene zip file
+ AssetInfo b = am.locateAsset(new ModelKey("casaamarela.jpg"));
+
+ // Try loading from wildhouse online scene zip file
+ AssetInfo c = am.locateAsset(new ModelKey("glasstile2.png"));
+
+ // Try loading directly from HTTP
+ AssetInfo d = am.locateAsset(new TextureKey("planet-2.jpg"));
+
+ if (a == null)
+ System.out.println("Failed to load from classpath");
+ else
+ System.out.println("Found classpath font: " + a.toString());
+
+ if (b == null)
+ System.out.println("Failed to load from town.zip");
+ else
+ System.out.println("Found zip image: " + b.toString());
+
+ if (c == null)
+ System.out.println("Failed to load from wildhouse.zip on googlecode.com");
+ else
+ System.out.println("Found online zip image: " + c.toString());
+
+ if (d == null)
+ System.out.println("Failed to load from HTTP");
+ else
+ System.out.println("Found HTTP showcase image: " + d.toString());
+ }
+}
diff --git a/engine/src/test/jme3test/asset/TestOnlineJar.java b/engine/src/test/jme3test/asset/TestOnlineJar.java
new file mode 100644
index 0000000..908188d
--- /dev/null
+++ b/engine/src/test/jme3test/asset/TestOnlineJar.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.asset;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture;
+
+/**
+ * This tests loading a file from a jar stored online.
+ * @author Kirill Vainer
+ */
+public class TestOnlineJar extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestOnlineJar app = new TestOnlineJar();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // create a simple plane/quad
+ Quad quadMesh = new Quad(1, 1);
+ quadMesh.updateGeometry(1, 1, true);
+
+ Geometry quad = new Geometry("Textured Quad", quadMesh);
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/town.zip",
+ HttpZipLocator.class);
+
+ TextureKey key = new TextureKey("grass.jpg", false);
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", tex);
+ quad.setMaterial(mat);
+
+ float aspect = tex.getImage().getWidth() / (float) tex.getImage().getHeight();
+ quad.setLocalScale(new Vector3f(aspect * 1.5f, 1.5f, 1));
+ quad.center();
+
+ rootNode.attachChild(quad);
+ }
+
+}
diff --git a/engine/src/test/jme3test/asset/TestUrlLoading.java b/engine/src/test/jme3test/asset/TestUrlLoading.java
new file mode 100644
index 0000000..6afd4df
--- /dev/null
+++ b/engine/src/test/jme3test/asset/TestUrlLoading.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.asset;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.asset.plugins.UrlLocator;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture;
+
+/**
+ * Load an image and display it from the internet using the UrlLocator.
+ * @author Kirill Vainer
+ */
+public class TestUrlLoading extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestUrlLoading app = new TestUrlLoading();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // create a simple plane/quad
+ Quad quadMesh = new Quad(1, 1);
+ quadMesh.updateGeometry(1, 1, true);
+
+ Geometry quad = new Geometry("Textured Quad", quadMesh);
+
+ assetManager.registerLocator("http://www.jmonkeyengine.com/wp-content/uploads/2010/09/",
+ UrlLocator.class);
+ TextureKey key = new TextureKey("planet-2.jpg", false);
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", tex);
+ quad.setMaterial(mat);
+
+ float aspect = tex.getImage().getWidth() / (float) tex.getImage().getHeight();
+ quad.setLocalScale(new Vector3f(aspect * 1.5f, 1.5f, 1));
+ quad.center();
+
+ rootNode.attachChild(quad);
+ }
+
+}
diff --git a/engine/src/test/jme3test/audio/TestAmbient.java b/engine/src/test/jme3test/audio/TestAmbient.java
new file mode 100644
index 0000000..c6b38c2
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestAmbient.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.audio;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.Environment;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+public class TestAmbient extends SimpleApplication {
+
+ private AudioNode nature, waves;
+
+ public static void main(String[] args) {
+ TestAmbient test = new TestAmbient();
+ test.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ float[] eax = new float[]{15, 38.0f, 0.300f, -1000, -3300, 0,
+ 1.49f, 0.54f, 1.00f, -2560, 0.162f, 0.00f, 0.00f,
+ 0.00f, -229, 0.088f, 0.00f, 0.00f, 0.00f, 0.125f, 1.000f,
+ 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f};
+ Environment env = new Environment(eax);
+ audioRenderer.setEnvironment(env);
+
+ waves = new AudioNode(assetManager, "Sound/Environment/Ocean Waves.ogg", false);
+ waves.setPositional(true);
+ waves.setLocalTranslation(new Vector3f(0, 0,0));
+ waves.setMaxDistance(100);
+ waves.setRefDistance(5);
+
+ nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true);
+ nature.setVolume(3);
+
+ waves.playInstance();
+ nature.play();
+
+ // just a blue box to mark the spot
+ Box box1 = new Box(Vector3f.ZERO, .5f, .5f, .5f);
+ Geometry player = new Geometry("Player", box1);
+ Material mat1 = new Material(assetManager,
+ "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.Blue);
+ player.setMaterial(mat1);
+ rootNode.attachChild(player);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ }
+}
diff --git a/engine/src/test/jme3test/audio/TestDoppler.java b/engine/src/test/jme3test/audio/TestDoppler.java
new file mode 100644
index 0000000..3368874
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestDoppler.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.audio;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.Environment;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import org.lwjgl.openal.AL10;
+import org.lwjgl.openal.AL11;
+
+/**
+ * Test Doppler Effect
+ */
+public class TestDoppler extends SimpleApplication {
+
+ private AudioNode ufo;
+
+ private float x = 20, z = 0;
+ private float rate = -0.05f;
+ private float xDist = 20;
+ private float zDist = 5;
+ private float angle = FastMath.TWO_PI;
+
+ public static void main(String[] args){
+ TestDoppler test = new TestDoppler();
+ test.start();
+ }
+
+ @Override
+ public void simpleInitApp(){
+ audioRenderer.setEnvironment(Environment.Dungeon);
+ AL10.alDistanceModel(AL11.AL_EXPONENT_DISTANCE);
+
+ ufo = new AudioNode(assetManager, "Sound/Effects/Beep.ogg", false);
+ ufo.setPositional(true);
+ ufo.setLooping(true);
+ ufo.setReverbEnabled(true);
+ ufo.setRefDistance(100000000);
+ ufo.setMaxDistance(100000000);
+ ufo.play();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ //float x = (float) (Math.cos(angle) * xDist);
+ float dx = (float) Math.sin(angle) * xDist;
+
+ //float z = (float) (Math.sin(angle) * zDist);
+ float dz = (float)(-Math.cos(angle) * zDist);
+
+ x += dx * tpf * 0.05f;
+ z += dz * tpf * 0.05f;
+
+ angle += tpf * rate;
+
+ if (angle > FastMath.TWO_PI){
+ angle = FastMath.TWO_PI;
+ rate = -rate;
+ }else if (angle < -0){
+ angle = -0;
+ rate = -rate;
+ }
+
+ ufo.setVelocity(new Vector3f(dx, 0, dz));
+ ufo.setLocalTranslation(x, 0, z);
+ ufo.updateGeometricState();
+
+ System.out.println("LOC: " + (int)x +", " + (int)z +
+ ", VEL: " + (int)dx + ", " + (int)dz);
+ }
+
+}
diff --git a/engine/src/test/jme3test/audio/TestMusicPlayer.form b/engine/src/test/jme3test/audio/TestMusicPlayer.form
new file mode 100644
index 0000000..dced8a4
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestMusicPlayer.form
@@ -0,0 +1,117 @@
+<?xml version="1.1" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
+ <Properties>
+ <Property name="defaultCloseOperation" type="int" value="3"/>
+ </Properties>
+ <SyntheticProperties>
+ <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+ </SyntheticProperties>
+ <Events>
+ <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosing"/>
+ </Events>
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,82,0,0,1,-112"/>
+ </AuxValues>
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+ <SubComponents>
+ <Container class="javax.swing.JPanel" name="pnlButtons">
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+ <BorderConstraints direction="Center"/>
+ </Constraint>
+ </Constraints>
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
+ <SubComponents>
+ <Component class="javax.swing.JSlider" name="sldVolume">
+ <Properties>
+ <Property name="majorTickSpacing" type="int" value="20"/>
+ <Property name="orientation" type="int" value="1"/>
+ <Property name="paintTicks" type="boolean" value="true"/>
+ <Property name="value" type="int" value="100"/>
+ </Properties>
+ <Events>
+ <EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="sldVolumeStateChanged"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JButton" name="btnRewind">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="&lt;&lt;"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JButton" name="btnStop">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="[ ]"/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnStopActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JButton" name="btnPlay">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="II / &gt;"/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnPlayActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JButton" name="btnFF">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="&gt;&gt;"/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnFFActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JButton" name="btnOpen">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="Open ..."/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnOpenActionPerformed"/>
+ </Events>
+ </Component>
+ </SubComponents>
+ </Container>
+ <Container class="javax.swing.JPanel" name="pnlBar">
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+ <BorderConstraints direction="First"/>
+ </Constraint>
+ </Constraints>
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
+ <SubComponents>
+ <Component class="javax.swing.JLabel" name="lblTime">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="0:00-0:00"/>
+ <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+ <Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
+ <EmptyBorder bottom="3" left="3" right="3" top="3"/>
+ </Border>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JSlider" name="sldBar">
+ <Properties>
+ <Property name="value" type="int" value="0"/>
+ </Properties>
+ <Events>
+ <EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="sldBarStateChanged"/>
+ </Events>
+ </Component>
+ </SubComponents>
+ </Container>
+ </SubComponents>
+</Form>
diff --git a/engine/src/test/jme3test/audio/TestMusicPlayer.java b/engine/src/test/jme3test/audio/TestMusicPlayer.java
new file mode 100644
index 0000000..9edafad
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestMusicPlayer.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.audio;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.audio.AudioNode.Status;
+import com.jme3.audio.*;
+import com.jme3.audio.plugins.OGGLoader;
+import com.jme3.audio.plugins.WAVLoader;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeSystem;
+import java.io.*;
+import javax.swing.JFileChooser;
+
+public class TestMusicPlayer extends javax.swing.JFrame {
+
+ private AudioRenderer ar;
+ private AudioData musicData;
+ private AudioNode musicSource;
+ private float musicLength = 0;
+ private float curTime = 0;
+ private Listener listener = new Listener();
+
+ public TestMusicPlayer() {
+ initComponents();
+ setLocationRelativeTo(null);
+ initAudioPlayer();
+ }
+
+ private void initAudioPlayer(){
+ AppSettings settings = new AppSettings(true);
+ settings.setRenderer(null); // disable rendering
+ settings.setAudioRenderer("LWJGL");
+ ar = JmeSystem.newAudioRenderer(settings);
+ ar.initialize();
+ ar.setListener(listener);
+ AudioContext.setAudioRenderer(ar);
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ pnlButtons = new javax.swing.JPanel();
+ sldVolume = new javax.swing.JSlider();
+ btnRewind = new javax.swing.JButton();
+ btnStop = new javax.swing.JButton();
+ btnPlay = new javax.swing.JButton();
+ btnFF = new javax.swing.JButton();
+ btnOpen = new javax.swing.JButton();
+ pnlBar = new javax.swing.JPanel();
+ lblTime = new javax.swing.JLabel();
+ sldBar = new javax.swing.JSlider();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
+ addWindowListener(new java.awt.event.WindowAdapter() {
+ public void windowClosing(java.awt.event.WindowEvent evt) {
+ formWindowClosing(evt);
+ }
+ });
+
+ pnlButtons.setLayout(new javax.swing.BoxLayout(pnlButtons, javax.swing.BoxLayout.LINE_AXIS));
+
+ sldVolume.setMajorTickSpacing(20);
+ sldVolume.setOrientation(javax.swing.JSlider.VERTICAL);
+ sldVolume.setPaintTicks(true);
+ sldVolume.setValue(100);
+ sldVolume.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ sldVolumeStateChanged(evt);
+ }
+ });
+ pnlButtons.add(sldVolume);
+
+ btnRewind.setText("<<");
+ pnlButtons.add(btnRewind);
+
+ btnStop.setText("[ ]");
+ btnStop.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnStopActionPerformed(evt);
+ }
+ });
+ pnlButtons.add(btnStop);
+
+ btnPlay.setText("II / >");
+ btnPlay.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnPlayActionPerformed(evt);
+ }
+ });
+ pnlButtons.add(btnPlay);
+
+ btnFF.setText(">>");
+ btnFF.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnFFActionPerformed(evt);
+ }
+ });
+ pnlButtons.add(btnFF);
+
+ btnOpen.setText("Open ...");
+ btnOpen.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnOpenActionPerformed(evt);
+ }
+ });
+ pnlButtons.add(btnOpen);
+
+ getContentPane().add(pnlButtons, java.awt.BorderLayout.CENTER);
+
+ pnlBar.setLayout(new javax.swing.BoxLayout(pnlBar, javax.swing.BoxLayout.LINE_AXIS));
+
+ lblTime.setText("0:00-0:00");
+ lblTime.setBorder(javax.swing.BorderFactory.createEmptyBorder(3, 3, 3, 3));
+ pnlBar.add(lblTime);
+
+ sldBar.setValue(0);
+ sldBar.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ sldBarStateChanged(evt);
+ }
+ });
+ pnlBar.add(sldBar);
+
+ getContentPane().add(pnlBar, java.awt.BorderLayout.PAGE_START);
+
+ pack();
+ }// </editor-fold>//GEN-END:initComponents
+
+ private void btnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOpenActionPerformed
+ JFileChooser chooser = new JFileChooser();
+ chooser.setAcceptAllFileFilterUsed(true);
+ chooser.setDialogTitle("Select OGG file");
+ chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ chooser.setMultiSelectionEnabled(false);
+ if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){
+ btnStopActionPerformed(null);
+
+ final File selected = chooser.getSelectedFile();
+ AssetLoader loader = null;
+ if(selected.getName().endsWith(".wav")){
+ loader = new WAVLoader();
+ }else{
+ loader = new OGGLoader();
+ }
+
+ AudioKey key = new AudioKey(selected.getName(), true, true);
+ try{
+ musicData = (AudioData) loader.load(new AssetInfo(null, key) {
+ @Override
+ public InputStream openStream() {
+ try{
+ return new FileInputStream(selected);
+ }catch (FileNotFoundException ex){
+ ex.printStackTrace();
+ }
+ return null;
+ }
+ });
+ }catch (IOException ex){
+ ex.printStackTrace();
+ }
+
+ musicSource = new AudioNode(musicData, key);
+ musicLength = musicData.getDuration();
+ updateTime();
+ }
+ }//GEN-LAST:event_btnOpenActionPerformed
+
+ private void updateTime(){
+ int max = (int) (musicLength * 100);
+ int pos = (int) (curTime * 100);
+ sldBar.setMaximum(max);
+ sldBar.setValue(pos);
+
+ int minutesTotal = (int) (musicLength / 60);
+ int secondsTotal = (int) (musicLength % 60);
+ int minutesNow = (int) (curTime / 60);
+ int secondsNow = (int) (curTime % 60);
+ String txt = String.format("%01d:%02d-%01d:%02d", minutesNow, secondsNow,
+ minutesTotal, secondsTotal);
+ lblTime.setText(txt);
+ }
+
+ private void btnPlayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlayActionPerformed
+ if (musicSource == null){
+ btnOpenActionPerformed(evt);
+ return;
+ }
+
+ if (musicSource.getStatus() == Status.Playing){
+ musicSource.setPitch(1);
+ ar.pauseSource(musicSource);
+ }else{
+ musicSource.setPitch(1);
+ musicSource.play();
+ }
+ }//GEN-LAST:event_btnPlayActionPerformed
+
+ private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
+ ar.cleanup();
+ }//GEN-LAST:event_formWindowClosing
+
+ private void sldVolumeStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sldVolumeStateChanged
+ listener.setVolume( (float) sldVolume.getValue() / 100f);
+ ar.setListener(listener);
+ }//GEN-LAST:event_sldVolumeStateChanged
+
+ private void btnStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStopActionPerformed
+ if (musicSource != null){
+ musicSource.setPitch(1);
+ ar.stopSource(musicSource);
+ }
+ }//GEN-LAST:event_btnStopActionPerformed
+
+ private void btnFFActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFFActionPerformed
+ if (musicSource.getStatus() == Status.Playing){
+ musicSource.setPitch(2);
+ }
+ }//GEN-LAST:event_btnFFActionPerformed
+
+ private void sldBarStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sldBarStateChanged
+ if (musicSource != null && !sldBar.getValueIsAdjusting()){
+ curTime = sldBar.getValue() / 100f;
+ if (curTime < 0)
+ curTime = 0;
+
+ musicSource.setTimeOffset(curTime);
+// if (musicSource.getStatus() == Status.Playing){
+// musicSource.stop();
+// musicSource.play();
+// }
+ updateTime();
+ }
+ }//GEN-LAST:event_sldBarStateChanged
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String args[]) {
+ java.awt.EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ new TestMusicPlayer().setVisible(true);
+ }
+ });
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btnFF;
+ private javax.swing.JButton btnOpen;
+ private javax.swing.JButton btnPlay;
+ private javax.swing.JButton btnRewind;
+ private javax.swing.JButton btnStop;
+ private javax.swing.JLabel lblTime;
+ private javax.swing.JPanel pnlBar;
+ private javax.swing.JPanel pnlButtons;
+ private javax.swing.JSlider sldBar;
+ private javax.swing.JSlider sldVolume;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/engine/src/test/jme3test/audio/TestMusicStreaming.java b/engine/src/test/jme3test/audio/TestMusicStreaming.java
new file mode 100644
index 0000000..49159af
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestMusicStreaming.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.audio;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.UrlLocator;
+import com.jme3.audio.AudioNode;
+
+public class TestMusicStreaming extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestMusicStreaming test = new TestMusicStreaming();
+ test.start();
+ }
+
+ @Override
+ public void simpleInitApp(){
+ assetManager.registerLocator("http://www.vorbis.com/music/", UrlLocator.class);
+ AudioNode audioSource = new AudioNode(assetManager, "Lumme-Badloop.ogg", true);
+ audioSource.play();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){}
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/audio/TestOgg.java b/engine/src/test/jme3test/audio/TestOgg.java
new file mode 100644
index 0000000..59b8d04
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestOgg.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.audio;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.LowPassFilter;
+
+public class TestOgg extends SimpleApplication {
+
+ private AudioNode audioSource;
+
+ public static void main(String[] args){
+ TestOgg test = new TestOgg();
+ test.start();
+ }
+
+ @Override
+ public void simpleInitApp(){
+ System.out.println("Playing without filter");
+ audioSource = new AudioNode(assetManager, "Sound/Effects/Foot steps.ogg", true);
+ audioSource.play();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ if (audioSource.getStatus() != AudioNode.Status.Playing){
+ audioRenderer.deleteAudioData(audioSource.getAudioData());
+
+ System.out.println("Playing with low pass filter");
+ audioSource = new AudioNode(assetManager, "Sound/Effects/Foot steps.ogg", true);
+ audioSource.setDryFilter(new LowPassFilter(1f, .1f));
+ audioSource.setVolume(3);
+ audioSource.play();
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/audio/TestReverb.java b/engine/src/test/jme3test/audio/TestReverb.java
new file mode 100644
index 0000000..0a20096
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestReverb.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.audio;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.Environment;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+
+public class TestReverb extends SimpleApplication {
+
+ private AudioNode audioSource;
+ private float time = 0;
+ private float nextTime = 1;
+
+ public static void main(String[] args) {
+ TestReverb test = new TestReverb();
+ test.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ audioSource = new AudioNode(assetManager, "Sound/Effects/Bang.wav");
+
+ float[] eax = new float[]{15, 38.0f, 0.300f, -1000, -3300, 0,
+ 1.49f, 0.54f, 1.00f, -2560, 0.162f, 0.00f, 0.00f, 0.00f,
+ -229, 0.088f, 0.00f, 0.00f, 0.00f, 0.125f, 1.000f, 0.250f,
+ 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f};
+ audioRenderer.setEnvironment(new Environment(eax));
+ Environment env = Environment.Cavern;
+ audioRenderer.setEnvironment(env);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ time += tpf;
+
+ if (time > nextTime) {
+ Vector3f v = new Vector3f();
+ v.setX(FastMath.nextRandomFloat());
+ v.setY(FastMath.nextRandomFloat());
+ v.setZ(FastMath.nextRandomFloat());
+ v.multLocal(40, 2, 40);
+ v.subtractLocal(20, 1, 20);
+
+ audioSource.setLocalTranslation(v);
+ audioSource.playInstance();
+ time = 0;
+ nextTime = FastMath.nextRandomFloat() * 2 + 0.5f;
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/audio/TestWav.java b/engine/src/test/jme3test/audio/TestWav.java
new file mode 100644
index 0000000..7e73ded
--- /dev/null
+++ b/engine/src/test/jme3test/audio/TestWav.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.audio;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+
+public class TestWav extends SimpleApplication {
+
+ private float time = 0;
+ private AudioNode audioSource;
+
+ public static void main(String[] args) {
+ TestWav test = new TestWav();
+ test.start();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ time += tpf;
+ if (time > 1f) {
+ audioSource.playInstance();
+ time = 0;
+ }
+
+ }
+
+ @Override
+ public void simpleInitApp() {
+ audioSource = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
+ audioSource.setLooping(false);
+ }
+}
diff --git a/engine/src/test/jme3test/awt/AppHarness.java b/engine/src/test/jme3test/awt/AppHarness.java
new file mode 100644
index 0000000..4a30d78
--- /dev/null
+++ b/engine/src/test/jme3test/awt/AppHarness.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.awt;
+
+import com.jme3.app.Application;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeSystem;
+import java.applet.Applet;
+import java.awt.Canvas;
+import java.awt.Graphics;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.swing.SwingUtilities;
+
+/**
+ *
+ * @author Kirill
+ */
+public class AppHarness extends Applet {
+
+ private JmeCanvasContext context;
+ private Canvas canvas;
+ private Application app;
+
+ private String appClass;
+ private URL appCfg = null;
+
+ private void createCanvas(){
+ AppSettings settings = new AppSettings(true);
+
+ // load app cfg
+ if (appCfg != null){
+ try {
+ InputStream in = appCfg.openStream();
+ settings.load(in);
+ in.close();
+ } catch (IOException ex){
+ ex.printStackTrace();
+ }
+ }
+
+ settings.setWidth(getWidth());
+ settings.setHeight(getHeight());
+ settings.setAudioRenderer(null);
+
+ JmeSystem.setLowPermissions(true);
+
+ try{
+ Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }catch (ClassNotFoundException ex){
+ ex.printStackTrace();
+ }catch (InstantiationException ex){
+ ex.printStackTrace();
+ }catch (IllegalAccessException ex){
+ ex.printStackTrace();
+ }
+
+ app.setSettings(settings);
+ app.createCanvas();
+
+ context = (JmeCanvasContext) app.getContext();
+ canvas = context.getCanvas();
+ canvas.setSize(getWidth(), getHeight());
+
+ add(canvas);
+ app.startCanvas();
+ }
+
+ @Override
+ public final void update(Graphics g) {
+ canvas.setSize(getWidth(), getHeight());
+ }
+
+ @Override
+ public void init(){
+ appClass = getParameter("AppClass");
+ if (appClass == null)
+ throw new RuntimeException("The required parameter AppClass isn't specified!");
+
+ try {
+ appCfg = new URL(getParameter("AppSettingsURL"));
+ } catch (MalformedURLException ex) {
+ ex.printStackTrace();
+ appCfg = null;
+ }
+
+ createCanvas();
+ System.out.println("applet:init");
+ }
+
+ @Override
+ public void start(){
+ context.setAutoFlushFrames(true);
+ System.out.println("applet:start");
+ }
+
+ @Override
+ public void stop(){
+ context.setAutoFlushFrames(false);
+ System.out.println("applet:stop");
+ }
+
+ @Override
+ public void destroy(){
+ System.out.println("applet:destroyStart");
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ removeAll();
+ System.out.println("applet:destroyRemoved");
+ }
+ });
+ app.stop(true);
+ System.out.println("applet:destroyDone");
+ }
+
+}
diff --git a/engine/src/test/jme3test/awt/TestApplet.java b/engine/src/test/jme3test/awt/TestApplet.java
new file mode 100644
index 0000000..5606e74
--- /dev/null
+++ b/engine/src/test/jme3test/awt/TestApplet.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.awt;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeSystem;
+import java.applet.Applet;
+import java.awt.Canvas;
+import java.awt.Graphics;
+import java.util.concurrent.Callable;
+import javax.swing.SwingUtilities;
+
+public class TestApplet extends Applet {
+
+ private static JmeCanvasContext context;
+ private static Application app;
+ private static Canvas canvas;
+ private static TestApplet applet;
+
+ public TestApplet(){
+ }
+
+ public static void createCanvas(String appClass){
+ AppSettings settings = new AppSettings(true);
+ settings.setWidth(640);
+ settings.setHeight(480);
+// settings.setRenderer(AppSettings.JOGL);
+
+ JmeSystem.setLowPermissions(true);
+
+ try{
+ Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }catch (ClassNotFoundException ex){
+ ex.printStackTrace();
+ }catch (InstantiationException ex){
+ ex.printStackTrace();
+ }catch (IllegalAccessException ex){
+ ex.printStackTrace();
+ }
+
+ app.setSettings(settings);
+ app.createCanvas();
+
+ context = (JmeCanvasContext) app.getContext();
+ canvas = context.getCanvas();
+ canvas.setSize(settings.getWidth(), settings.getHeight());
+ }
+
+ public static void startApp(){
+ applet.add(canvas);
+ app.startCanvas();
+
+ app.enqueue(new Callable<Void>(){
+ public Void call(){
+ if (app instanceof SimpleApplication){
+ SimpleApplication simpleApp = (SimpleApplication) app;
+ simpleApp.getFlyByCamera().setDragToRotate(true);
+ simpleApp.getInputManager().setCursorVisible(true);
+ }
+ return null;
+ }
+ });
+ }
+
+ public void freezeApp(){
+ remove(canvas);
+ }
+
+ public void unfreezeApp(){
+ add(canvas);
+ }
+
+ @Override
+ public final void update(Graphics g) {
+// canvas.setSize(getWidth(), getHeight());
+ }
+
+ @Override
+ public void init(){
+ applet = this;
+ createCanvas("jme3test.model.shape.TestBox");
+ startApp();
+ app.setPauseOnLostFocus(false);
+ System.out.println("applet:init");
+ }
+
+ @Override
+ public void start(){
+// context.setAutoFlushFrames(true);
+ System.out.println("applet:start");
+ }
+
+ @Override
+ public void stop(){
+// context.setAutoFlushFrames(false);
+ System.out.println("applet:stop");
+ }
+
+ @Override
+ public void destroy(){
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ removeAll();
+ System.out.println("applet:destroyStart");
+ }
+ });
+ app.stop(true);
+ System.out.println("applet:destroyEnd");
+ }
+
+}
diff --git a/engine/src/test/jme3test/awt/TestAwtPanels.java b/engine/src/test/jme3test/awt/TestAwtPanels.java
new file mode 100644
index 0000000..2c67867
--- /dev/null
+++ b/engine/src/test/jme3test/awt/TestAwtPanels.java
@@ -0,0 +1,88 @@
+package jme3test.awt;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.system.awt.AwtPanel;
+import com.jme3.system.awt.AwtPanelsContext;
+import com.jme3.system.awt.PaintMode;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+public class TestAwtPanels extends SimpleApplication {
+
+ private static TestAwtPanels app;
+ private static AwtPanel panel, panel2;
+ private static int panelsClosed = 0;
+
+ private static void createWindowForPanel(AwtPanel panel, int location){
+ JFrame frame = new JFrame("Render Display " + location);
+ frame.getContentPane().setLayout(new BorderLayout());
+ frame.getContentPane().add(panel, BorderLayout.CENTER);
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosed(WindowEvent e) {
+ if (++panelsClosed == 2){
+ app.stop();
+ }
+ }
+ });
+ frame.pack();
+ frame.setLocation(location, Toolkit.getDefaultToolkit().getScreenSize().height - 400);
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args){
+ Logger.getLogger("com.jme3").setLevel(Level.WARNING);
+
+ app = new TestAwtPanels();
+ app.setShowSettings(false);
+ AppSettings settings = new AppSettings(true);
+ settings.setCustomRenderer(AwtPanelsContext.class);
+ settings.setFrameRate(60);
+ app.setSettings(settings);
+ app.start();
+
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ final AwtPanelsContext ctx = (AwtPanelsContext) app.getContext();
+ panel = ctx.createPanel(PaintMode.Accelerated);
+ panel.setPreferredSize(new Dimension(400, 300));
+ ctx.setInputSource(panel);
+
+ panel2 = ctx.createPanel(PaintMode.Accelerated);
+ panel2.setPreferredSize(new Dimension(400, 300));
+
+ createWindowForPanel(panel, 300);
+ createWindowForPanel(panel2, 700);
+ }
+ });
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setDragToRotate(true);
+
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+
+ panel.attachTo(true, viewPort);
+ guiViewPort.setClearFlags(true, true, true);
+ panel2.attachTo(false, guiViewPort);
+ }
+}
diff --git a/engine/src/test/jme3test/awt/TestCanvas.java b/engine/src/test/jme3test/awt/TestCanvas.java
new file mode 100644
index 0000000..6351ee3
--- /dev/null
+++ b/engine/src/test/jme3test/awt/TestCanvas.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.awt;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.util.JmeFormatter;
+import java.awt.BorderLayout;
+import java.awt.Canvas;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.concurrent.Callable;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Logger;
+import javax.swing.*;
+
+public class TestCanvas {
+
+ private static JmeCanvasContext context;
+ private static Canvas canvas;
+ private static Application app;
+ private static JFrame frame;
+ private static Container canvasPanel1, canvasPanel2;
+ private static Container currentPanel;
+ private static JTabbedPane tabbedPane;
+ private static final String appClass = "jme3test.post.TestRenderToTexture";
+
+ private static void createTabs(){
+ tabbedPane = new JTabbedPane();
+
+ canvasPanel1 = new JPanel();
+ canvasPanel1.setLayout(new BorderLayout());
+ tabbedPane.addTab("jME3 Canvas 1", canvasPanel1);
+
+ canvasPanel2 = new JPanel();
+ canvasPanel2.setLayout(new BorderLayout());
+ tabbedPane.addTab("jME3 Canvas 2", canvasPanel2);
+
+ frame.getContentPane().add(tabbedPane);
+
+ currentPanel = canvasPanel1;
+ }
+
+ private static void createMenu(){
+ JMenuBar menuBar = new JMenuBar();
+ frame.setJMenuBar(menuBar);
+
+ JMenu menuTortureMethods = new JMenu("Canvas Torture Methods");
+ menuBar.add(menuTortureMethods);
+
+ final JMenuItem itemRemoveCanvas = new JMenuItem("Remove Canvas");
+ menuTortureMethods.add(itemRemoveCanvas);
+ itemRemoveCanvas.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (itemRemoveCanvas.getText().equals("Remove Canvas")){
+ currentPanel.remove(canvas);
+
+ itemRemoveCanvas.setText("Add Canvas");
+ }else if (itemRemoveCanvas.getText().equals("Add Canvas")){
+ currentPanel.add(canvas, BorderLayout.CENTER);
+
+ itemRemoveCanvas.setText("Remove Canvas");
+ }
+ }
+ });
+
+ final JMenuItem itemHideCanvas = new JMenuItem("Hide Canvas");
+ menuTortureMethods.add(itemHideCanvas);
+ itemHideCanvas.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (itemHideCanvas.getText().equals("Hide Canvas")){
+ canvas.setVisible(false);
+ itemHideCanvas.setText("Show Canvas");
+ }else if (itemHideCanvas.getText().equals("Show Canvas")){
+ canvas.setVisible(true);
+ itemHideCanvas.setText("Hide Canvas");
+ }
+ }
+ });
+
+ final JMenuItem itemSwitchTab = new JMenuItem("Switch to tab #2");
+ menuTortureMethods.add(itemSwitchTab);
+ itemSwitchTab.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e){
+ if (itemSwitchTab.getText().equals("Switch to tab #2")){
+ canvasPanel1.remove(canvas);
+ canvasPanel2.add(canvas, BorderLayout.CENTER);
+ currentPanel = canvasPanel2;
+ itemSwitchTab.setText("Switch to tab #1");
+ }else if (itemSwitchTab.getText().equals("Switch to tab #1")){
+ canvasPanel2.remove(canvas);
+ canvasPanel1.add(canvas, BorderLayout.CENTER);
+ currentPanel = canvasPanel1;
+ itemSwitchTab.setText("Switch to tab #2");
+ }
+ }
+ });
+
+ JMenuItem itemSwitchLaf = new JMenuItem("Switch Look and Feel");
+ menuTortureMethods.add(itemSwitchLaf);
+ itemSwitchLaf.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e){
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Throwable t){
+ t.printStackTrace();
+ }
+ SwingUtilities.updateComponentTreeUI(frame);
+ frame.pack();
+ }
+ });
+
+ JMenuItem itemSmallSize = new JMenuItem("Set size to (0, 0)");
+ menuTortureMethods.add(itemSmallSize);
+ itemSmallSize.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e){
+ Dimension preferred = frame.getPreferredSize();
+ frame.setPreferredSize(new Dimension(0, 0));
+ frame.pack();
+ frame.setPreferredSize(preferred);
+ }
+ });
+
+ JMenuItem itemKillCanvas = new JMenuItem("Stop/Start Canvas");
+ menuTortureMethods.add(itemKillCanvas);
+ itemKillCanvas.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ currentPanel.remove(canvas);
+ app.stop(true);
+
+ createCanvas(appClass);
+ currentPanel.add(canvas, BorderLayout.CENTER);
+ frame.pack();
+ startApp();
+ }
+ });
+
+ JMenuItem itemExit = new JMenuItem("Exit");
+ menuTortureMethods.add(itemExit);
+ itemExit.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ae) {
+ frame.dispose();
+ app.stop();
+ }
+ });
+ }
+
+ private static void createFrame(){
+ frame = new JFrame("Test");
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter(){
+ @Override
+ public void windowClosed(WindowEvent e) {
+ app.stop();
+ }
+ });
+
+ createTabs();
+ createMenu();
+ }
+
+ public static void createCanvas(String appClass){
+ AppSettings settings = new AppSettings(true);
+ settings.setWidth(640);
+ settings.setHeight(480);
+
+ try{
+ Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }catch (ClassNotFoundException ex){
+ ex.printStackTrace();
+ }catch (InstantiationException ex){
+ ex.printStackTrace();
+ }catch (IllegalAccessException ex){
+ ex.printStackTrace();
+ }
+
+ app.setPauseOnLostFocus(false);
+ app.setSettings(settings);
+ app.createCanvas();
+ app.startCanvas();
+
+ context = (JmeCanvasContext) app.getContext();
+ canvas = context.getCanvas();
+ canvas.setSize(settings.getWidth(), settings.getHeight());
+ }
+
+ public static void startApp(){
+ app.startCanvas();
+ app.enqueue(new Callable<Void>(){
+ public Void call(){
+ if (app instanceof SimpleApplication){
+ SimpleApplication simpleApp = (SimpleApplication) app;
+ simpleApp.getFlyByCamera().setDragToRotate(true);
+ }
+ return null;
+ }
+ });
+
+ }
+
+ public static void main(String[] args){
+ JmeFormatter formatter = new JmeFormatter();
+
+ Handler consoleHandler = new ConsoleHandler();
+ consoleHandler.setFormatter(formatter);
+
+ Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
+ Logger.getLogger("").addHandler(consoleHandler);
+
+ createCanvas(appClass);
+
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ }
+
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ JPopupMenu.setDefaultLightWeightPopupEnabled(false);
+
+ createFrame();
+
+ currentPanel.add(canvas, BorderLayout.CENTER);
+ frame.pack();
+ startApp();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+ });
+ }
+
+}
diff --git a/engine/src/test/jme3test/awt/TestSafeCanvas.java b/engine/src/test/jme3test/awt/TestSafeCanvas.java
new file mode 100644
index 0000000..88451f5
--- /dev/null
+++ b/engine/src/test/jme3test/awt/TestSafeCanvas.java
@@ -0,0 +1,69 @@
+package jme3test.awt;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import java.awt.Canvas;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import javax.swing.JFrame;
+
+public class TestSafeCanvas extends SimpleApplication {
+
+ public static void main(String[] args) throws InterruptedException{
+ AppSettings settings = new AppSettings(true);
+ settings.setWidth(640);
+ settings.setHeight(480);
+
+ final TestSafeCanvas app = new TestSafeCanvas();
+ app.setPauseOnLostFocus(false);
+ app.setSettings(settings);
+ app.createCanvas();
+ app.startCanvas(true);
+
+ JmeCanvasContext context = (JmeCanvasContext) app.getContext();
+ Canvas canvas = context.getCanvas();
+ canvas.setSize(settings.getWidth(), settings.getHeight());
+
+
+
+ Thread.sleep(3000);
+
+ JFrame frame = new JFrame("Test");
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ app.stop();
+ }
+ });
+ frame.getContentPane().add(canvas);
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+
+ Thread.sleep(3000);
+
+ frame.getContentPane().remove(canvas);
+
+ Thread.sleep(3000);
+
+ frame.getContentPane().add(canvas);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setDragToRotate(true);
+
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+ }
+}
diff --git a/engine/src/test/jme3test/batching/TestBatchNode.java b/engine/src/test/jme3test/batching/TestBatchNode.java
new file mode 100644
index 0000000..861536c
--- /dev/null
+++ b/engine/src/test/jme3test/batching/TestBatchNode.java
@@ -0,0 +1,97 @@
+/*
+ * To change this template, choose Tools | Templates and open the template in
+ * the editor.
+ */
+package jme3test.batching;
+
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.BatchNode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.NanoTimer;
+import com.jme3.util.TangentBinormalGenerator;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TestBatchNode extends SimpleApplication {
+
+ public static void main(String[] args) {
+
+ TestBatchNode app = new TestBatchNode();
+ app.start();
+ }
+ BatchNode batch;
+
+ @Override
+ public void simpleInitApp() {
+ timer = new NanoTimer();
+ batch = new BatchNode("theBatchNode");
+
+ /**
+ * A cube with a color "bleeding" through transparent texture. Uses
+ * Texture from jme3-test-data library!
+ */
+ Box boxshape4 = new Box(Vector3f.ZERO, 1f, 1f, 1f );
+ cube = new Geometry("cube1", boxshape4);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ cube.setMaterial(mat);
+// Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+// mat.setColor("Diffuse", ColorRGBA.Blue);
+// mat.setBoolean("UseMaterialColors", true);
+ /**
+ * A cube with a color "bleeding" through transparent texture. Uses
+ * Texture from jme3-test-data library!
+ */
+ Box box = new Box(Vector3f.ZERO, 1f, 1f, 1f);
+ cube2 = new Geometry("cube2", box);
+ cube2.setMaterial(mat);
+
+ TangentBinormalGenerator.generate(cube);
+ TangentBinormalGenerator.generate(cube2);
+
+
+ n = new Node("aNode");
+ // n.attachChild(cube2);
+ batch.attachChild(cube);
+ batch.attachChild(cube2);
+ // batch.setMaterial(mat);
+ batch.batch();
+ rootNode.attachChild(batch);
+ cube.setLocalTranslation(3, 0, 0);
+ cube2.setLocalTranslation(0, 3, 0);
+
+
+ dl=new DirectionalLight();
+ dl.setColor(ColorRGBA.White.mult(2));
+ dl.setDirection(new Vector3f(1, -1, -1));
+ rootNode.addLight(dl);
+ flyCam.setMoveSpeed(10);
+ }
+ Node n;
+ Geometry cube;
+ Geometry cube2;
+ float time = 0;
+ DirectionalLight dl;
+ @Override
+ public void simpleUpdate(float tpf) {
+ time += tpf;
+ dl.setDirection(cam.getDirection());
+ cube2.setLocalTranslation(FastMath.sin(-time)*3, FastMath.cos(time)*3, 0);
+ cube2.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Z));
+ cube2.setLocalScale(Math.max(FastMath.sin(time),0.5f));
+
+ batch.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Z));
+
+ }
+//
+}
diff --git a/engine/src/test/jme3test/batching/TestBatchNodeCluster.java b/engine/src/test/jme3test/batching/TestBatchNodeCluster.java
new file mode 100644
index 0000000..1da3003
--- /dev/null
+++ b/engine/src/test/jme3test/batching/TestBatchNodeCluster.java
@@ -0,0 +1,345 @@
+/*
+ * To change this template, choose Tools | Templates and open the template in
+ * the editor.
+ */
+package jme3test.batching;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.scene.*;
+import com.jme3.scene.debug.Arrow;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.system.NanoTimer;
+import java.util.ArrayList;
+import java.util.Random;
+
+public class TestBatchNodeCluster extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestBatchNodeCluster app = new TestBatchNodeCluster();
+ settingst = new AppSettings(true);
+ //settingst.setFrameRate(75);
+ settingst.setResolution(640, 480);
+ settingst.setVSync(false);
+ settingst.setFullscreen(false);
+ app.setSettings(settingst);
+ app.setShowSettings(false);
+ app.start();
+ }
+ private ActionListener al = new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("Start Game")) {
+// randomGenerator();
+ }
+ }
+ };
+ protected Random rand = new Random();
+ protected int maxCubes = 2000;
+ protected int startAt = 0;
+ protected static int xPositions = 0, yPositions = 0, zPositions = 0;
+ protected int returner = 0;
+ protected ArrayList<Integer> xPosition = new ArrayList<Integer>();
+ protected ArrayList<Integer> yPosition = new ArrayList<Integer>();
+ protected ArrayList<Integer> zPosition = new ArrayList<Integer>();
+ protected int xLimitf = 60, xLimits = -60, yLimitf = 60, yLimits = -20, zLimitf = 60, zLimits = -60;
+ protected int circ = 8;//increases by 8 every time.
+ protected int dynamic = 4;
+ protected static AppSettings settingst;
+ protected boolean isTrue = true;
+ private int lineLength = 50;
+ protected BatchNode batchNode;
+ Material mat1;
+ Material mat2;
+ Material mat3;
+ Material mat4;
+ Node terrain;
+ //protected
+// protected Geometry player;
+
+ @Override
+ public void simpleInitApp() {
+ timer = new NanoTimer();
+
+ batchNode = new SimpleBatchNode("BatchNode");
+
+
+ xPosition.add(0);
+ yPosition.add(0);
+ zPosition.add(0);
+
+ mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.White);
+ mat1.setColor("GlowColor", ColorRGBA.Blue.mult(10));
+
+ mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.setColor("Color", ColorRGBA.White);
+ mat2.setColor("GlowColor", ColorRGBA.Red.mult(10));
+
+ mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat3.setColor("Color", ColorRGBA.White);
+ mat3.setColor("GlowColor", ColorRGBA.Yellow.mult(10));
+
+ mat4 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat4.setColor("Color", ColorRGBA.White);
+ mat4.setColor("GlowColor", ColorRGBA.Orange.mult(10));
+
+ randomGenerator();
+
+ //rootNode.attachChild(SkyFactory.createSky(
+ // assetManager, "Textures/SKY02.zip", false));
+ inputManager.addMapping("Start Game", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addListener(al, new String[]{"Start Game"});
+
+
+ cam.setLocation(new Vector3f(-34.403286f, 126.65158f, 434.791f));
+ cam.setRotation(new Quaternion(0.022630932f, 0.9749435f, -0.18736298f, 0.11776358f));
+
+
+ batchNode.batch();
+
+
+ terrain = new Node("terrain");
+ terrain.setLocalTranslation(50, 0, 50);
+ terrain.attachChild(batchNode);
+
+ flyCam.setMoveSpeed(100);
+ rootNode.attachChild(terrain);
+ Vector3f pos = new Vector3f(-40, 0, -40);
+ batchNode.setLocalTranslation(pos);
+
+
+ Arrow a = new Arrow(new Vector3f(0, 50, 0));
+ Geometry g = new Geometry("a", a);
+ g.setLocalTranslation(terrain.getLocalTranslation());
+ Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ m.setColor("Color", ColorRGBA.Blue);
+ g.setMaterial(m);
+
+
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ fpp.addFilter(new BloomFilter(BloomFilter.GlowMode.Objects));
+// SSAOFilter ssao = new SSAOFilter(8.630104f,22.970434f,2.9299977f,0.2999997f);
+// fpp.addFilter(ssao);
+ viewPort.addProcessor(fpp);
+ // viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+ }
+
+ public void randomGenerator() {
+ for (int i = startAt; i < maxCubes - 1; i++) {
+ randomize();
+ Geometry box = new Geometry("Box" + i, new Box(Vector3f.ZERO, 1, 1, 1));
+ box.setLocalTranslation(new Vector3f(xPosition.get(xPosition.size() - 1),
+ yPosition.get(yPosition.size() - 1),
+ zPosition.get(zPosition.size() - 1)));
+ batchNode.attachChild(box);
+ if (i < 500) {
+ box.setMaterial(mat1);
+ } else if (i < 1000) {
+
+ box.setMaterial(mat2);
+ } else if (i < 1500) {
+
+ box.setMaterial(mat3);
+ } else {
+
+ box.setMaterial(mat4);
+ }
+
+ }
+ }
+
+// public BatchNode randomBatch() {
+//
+// int randomn = rand.nextInt(4);
+// if (randomn == 0) {
+// return blue;
+// } else if (randomn == 1) {
+// return brown;
+// } else if (randomn == 2) {
+// return pink;
+// } else if (randomn == 3) {
+// return orange;
+// }
+// return null;
+// }
+ public ColorRGBA randomColor() {
+ ColorRGBA color = ColorRGBA.Black;
+ int randomn = rand.nextInt(4);
+ if (randomn == 0) {
+ color = ColorRGBA.Orange;
+ } else if (randomn == 1) {
+ color = ColorRGBA.Blue;
+ } else if (randomn == 2) {
+ color = ColorRGBA.Brown;
+ } else if (randomn == 3) {
+ color = ColorRGBA.Magenta;
+ }
+ return color;
+ }
+
+ public void randomize() {
+ int xpos = xPosition.get(xPosition.size() - 1);
+ int ypos = yPosition.get(yPosition.size() - 1);
+ int zpos = zPosition.get(zPosition.size() - 1);
+ int x = 0;
+ int y = 0;
+ int z = 0;
+ boolean unTrue = true;
+ while (unTrue) {
+ unTrue = false;
+ boolean xChanged = false;
+ x = 0;
+ y = 0;
+ z = 0;
+ if (xpos >= lineLength * 2) {
+ x = 2;
+ xChanged = true;
+ } else {
+ x = xPosition.get(xPosition.size() - 1) + 2;
+ }
+ if (xChanged) {
+ //y = yPosition.get(yPosition.size() - lineLength) + 2;
+ } else {
+ y = rand.nextInt(3);
+ if (yPosition.size() > lineLength) {
+ if (yPosition.size() > 51) {
+ if (y == 0 && ypos < yLimitf && getym(lineLength) > ypos - 2) {
+ y = ypos + 2;
+ } else if (y == 1 && ypos > yLimits && getym(lineLength) < ypos + 2) {
+ y = ypos - 2;
+ } else if (y == 2 && getym(lineLength) > ypos - 2 && getym(lineLength) < ypos + 2) {
+ y = ypos;
+ } else {
+ if (ypos >= yLimitf) {
+ y = ypos - 2;
+ } else if (ypos <= yLimits) {
+ y = ypos + 2;
+ } else if (y == 0 && getym(lineLength) >= ypos - 4) {
+ y = ypos - 2;
+ } else if (y == 0 && getym(lineLength) >= ypos - 2) {
+ y = ypos;
+ } else if (y == 1 && getym(lineLength) >= ypos + 4) {
+ y = ypos + 2;
+ } else if (y == 1 && getym(lineLength) >= ypos + 2) {
+ y = ypos;
+ } else if (y == 2 && getym(lineLength) <= ypos - 2) {
+ y = ypos - 2;
+ } else if (y == 2 && getym(lineLength) >= ypos + 2) {
+ y = ypos + 2;
+ } else {
+ System.out.println("wtf");
+ }
+ }
+ } else if (yPosition.size() == lineLength) {
+ if (y == 0 && ypos < yLimitf) {
+ y = getym(lineLength) + 2;
+ } else if (y == 1 && ypos > yLimits) {
+ y = getym(lineLength) - 2;
+ }
+ }
+ } else {
+ if (y == 0 && ypos < yLimitf) {
+ y = ypos + 2;
+ } else if (y == 1 && ypos > yLimits) {
+ y = ypos - 2;
+ } else if (y == 2) {
+ y = ypos;
+ } else if (y == 0 && ypos >= yLimitf) {
+ y = ypos - 2;
+ } else if (y == 1 && ypos <= yLimits) {
+ y = ypos + 2;
+ }
+ }
+ }
+ if (xChanged) {
+ z = zpos + 2;
+ } else {
+ z = zpos;
+ }
+// for (int i = 0; i < xPosition.size(); i++)
+// {
+// if (x - xPosition.get(i) <= 1 && x - xPosition.get(i) >= -1 &&
+// y - yPosition.get(i) <= 1 && y - yPosition.get(i) >= -1
+// &&z - zPosition.get(i) <= 1 && z - zPosition.get(i) >=
+// -1)
+// {
+// unTrue = true;
+// }
+// }
+ }
+ xPosition.add(x);
+ yPosition.add(y);
+ zPosition.add(z);
+ }
+
+ public int getxm(int i) {
+ return xPosition.get(xPosition.size() - i);
+ }
+
+ public int getym(int i) {
+ return yPosition.get(yPosition.size() - i);
+ }
+
+ public int getzm(int i) {
+ return zPosition.get(zPosition.size() - i);
+ }
+
+ public int getx(int i) {
+ return xPosition.get(i);
+ }
+
+ public int gety(int i) {
+ return yPosition.get(i);
+ }
+
+ public int getz(int i) {
+ return zPosition.get(i);
+ }
+ long nbFrames = 0;
+ long cullTime = 0;
+ float time = 0;
+ Vector3f lookAtPos = new Vector3f(0, 0, 0);
+ float xpos = 0;
+ Spatial box;
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ time += tpf;
+ int random = rand.nextInt(2000);
+ float mult1 = 1.0f;
+ float mult2 = 1.0f;
+ if (random < 500) {
+ mult1 = 1.0f;
+ mult2 = 1.0f;
+ } else if (random < 1000) {
+ mult1 = -1.0f;
+ mult2 = 1.0f;
+ } else if (random < 1500) {
+ mult1 = 1.0f;
+ mult2 = -1.0f;
+ } else if (random <= 2000) {
+ mult1 = -1.0f;
+ mult2 = -1.0f;
+ }
+ box = batchNode.getChild("Box" + random);
+ if (box != null) {
+ Vector3f v = box.getLocalTranslation();
+ box.setLocalTranslation(v.x + FastMath.sin(time * mult1) * 20, v.y + (FastMath.sin(time * mult1) * FastMath.cos(time * mult1) * 20), v.z + FastMath.cos(time * mult2) * 20);
+ }
+ terrain.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Y));
+
+
+ }
+}
diff --git a/engine/src/test/jme3test/batching/TestBatchNodeTower.java b/engine/src/test/jme3test/batching/TestBatchNodeTower.java
new file mode 100644
index 0000000..57285d0
--- /dev/null
+++ b/engine/src/test/jme3test/batching/TestBatchNodeTower.java
@@ -0,0 +1,251 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jme3test.batching;
+
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.BatchNode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.shadow.PssmShadowRenderer;
+import com.jme3.shadow.PssmShadowRenderer.CompareMode;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+import com.jme3.system.AppSettings;
+import com.jme3.system.NanoTimer;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import jme3test.bullet.BombControl;
+
+/**
+ *
+ * @author double1984 (tower mod by atom)
+ */
+public class TestBatchNodeTower extends SimpleApplication {
+
+ int bricksPerLayer = 8;
+ int brickLayers = 30;
+
+ static float brickWidth = .75f, brickHeight = .25f, brickDepth = .25f;
+ float radius = 3f;
+ float angle = 0;
+
+
+ Material mat;
+ Material mat2;
+ Material mat3;
+ PssmShadowRenderer bsr;
+ private Sphere bullet;
+ private Box brick;
+ private SphereCollisionShape bulletCollisionShape;
+
+ private BulletAppState bulletAppState;
+ BatchNode batchNode = new BatchNode("batch Node");
+
+ public static void main(String args[]) {
+ TestBatchNodeTower f = new TestBatchNodeTower();
+ AppSettings s = new AppSettings(true);
+ f.setSettings(s);
+ f.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ timer = new NanoTimer();
+ bulletAppState = new BulletAppState();
+ bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+ // bulletAppState.setEnabled(false);
+ stateManager.attach(bulletAppState);
+ bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(0.4f);
+
+ brick = new Box(Vector3f.ZERO, brickWidth, brickHeight, brickDepth);
+ brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
+ //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ initMaterial();
+ initTower();
+ initFloor();
+ initCrossHairs();
+ this.cam.setLocation(new Vector3f(0, 25f, 8f));
+ cam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0));
+ cam.setFrustumFar(80);
+ inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(actionListener, "shoot");
+ rootNode.setShadowMode(ShadowMode.Off);
+
+ batchNode.batch();
+ batchNode.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(batchNode);
+
+
+ bsr = new PssmShadowRenderer(assetManager, 1024, 2);
+ bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ bsr.setLambda(0.55f);
+ bsr.setShadowIntensity(0.6f);
+ bsr.setCompareMode(CompareMode.Hardware);
+ bsr.setFilterMode(FilterMode.PCF4);
+ viewPort.addProcessor(bsr);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("shoot") && !keyPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat2);
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.setLocalTranslation(cam.getLocation());
+ RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
+// RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, 1);
+ bulletNode.setLinearVelocity(cam.getDirection().mult(25));
+ bulletg.addControl(bulletNode);
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletNode);
+ }
+ }
+ };
+
+ public void initTower() {
+ double tempX = 0;
+ double tempY = 0;
+ double tempZ = 0;
+ angle = 0f;
+ for (int i = 0; i < brickLayers; i++){
+ // Increment rows
+ if(i!=0)
+ tempY+=brickHeight*2;
+ else
+ tempY=brickHeight;
+ // Alternate brick seams
+ angle = 360.0f / bricksPerLayer * i/2f;
+ for (int j = 0; j < bricksPerLayer; j++){
+ tempZ = Math.cos(Math.toRadians(angle))*radius;
+ tempX = Math.sin(Math.toRadians(angle))*radius;
+ System.out.println("x="+((float)(tempX))+" y="+((float)(tempY))+" z="+(float)(tempZ));
+ Vector3f vt = new Vector3f((float)(tempX), (float)(tempY), (float)(tempZ));
+ // Add crenelation
+ if (i==brickLayers-1){
+ if (j%2 == 0){
+ addBrick(vt);
+ }
+ }
+ // Create main tower
+ else {
+ addBrick(vt);
+ }
+ angle += 360.0/bricksPerLayer;
+ }
+ }
+
+ }
+
+ public void initFloor() {
+ Box floorBox = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
+ floorBox.scaleTextureCoordinates(new Vector2f(3, 6));
+
+ Geometry floor = new Geometry("floor", floorBox);
+ floor.setMaterial(mat3);
+ floor.setShadowMode(ShadowMode.Receive);
+ floor.setLocalTranslation(0, 0, 0);
+ floor.addControl(new RigidBodyControl(0));
+ this.rootNode.attachChild(floor);
+ this.getPhysicsSpace().add(floor);
+ }
+
+ public void initMaterial() {
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ mat.setTexture("ColorMap", tex);
+
+ mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = assetManager.loadTexture(key2);
+ mat2.setTexture("ColorMap", tex2);
+
+ mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
+ key3.setGenerateMips(true);
+ Texture tex3 = assetManager.loadTexture(key3);
+ tex3.setWrap(WrapMode.Repeat);
+ mat3.setTexture("ColorMap", tex3);
+ }
+int nbBrick =0;
+ public void addBrick(Vector3f ori) {
+ Geometry reBoxg = new Geometry("brick", brick);
+ reBoxg.setMaterial(mat);
+ reBoxg.setLocalTranslation(ori);
+ reBoxg.rotate(0f, (float)Math.toRadians(angle) , 0f );
+ reBoxg.addControl(new RigidBodyControl(1.5f));
+ reBoxg.setShadowMode(ShadowMode.CastAndReceive);
+ reBoxg.getControl(RigidBodyControl.class).setFriction(1.6f);
+ this.batchNode.attachChild(reBoxg);
+ this.getPhysicsSpace().add(reBoxg);
+ nbBrick++;
+ }
+
+ protected void initCrossHairs() {
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/blender/TestBlenderLoader.java b/engine/src/test/jme3test/blender/TestBlenderLoader.java
new file mode 100644
index 0000000..4575aee
--- /dev/null
+++ b/engine/src/test/jme3test/blender/TestBlenderLoader.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.blender;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+
+public class TestBlenderLoader extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestBlenderLoader app = new TestBlenderLoader();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+
+ //load model with packed images
+ Spatial ogre = assetManager.loadModel("Blender/2.4x/Sinbad.blend");
+ rootNode.attachChild(ogre);
+
+ //load model with referenced images
+ Spatial track = assetManager.loadModel("Blender/2.4x/MountainValley_Track.blend");
+ rootNode.attachChild(track);
+
+ // sunset light
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f,-0.7f,1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.44f, 0.30f, 0.20f, 1.0f));
+ rootNode.addLight(dl);
+
+ // skylight
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.6f,-1,-0.6f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.10f, 0.22f, 0.44f, 1.0f));
+ rootNode.addLight(dl);
+
+ // white ambient light
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, -0.5f,-0.1f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.80f, 0.70f, 0.80f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ }
+
+}
diff --git a/engine/src/test/jme3test/bounding/TestRayCollision.java b/engine/src/test/jme3test/bounding/TestRayCollision.java
new file mode 100644
index 0000000..922c2ed
--- /dev/null
+++ b/engine/src/test/jme3test/bounding/TestRayCollision.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bounding;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.collision.CollisionResults;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+
+/**
+ * Tests picking/collision between bounds and shapes.
+ */
+public class TestRayCollision {
+
+ public static void main(String[] args){
+ Ray r = new Ray(Vector3f.ZERO, Vector3f.UNIT_X);
+ BoundingBox bbox = new BoundingBox(new Vector3f(5, 0, 0), 1, 1, 1);
+
+ CollisionResults res = new CollisionResults();
+ bbox.collideWith(r, res);
+
+ System.out.println("Bounding:" +bbox);
+ System.out.println("Ray: "+r);
+
+ System.out.println("Num collisions: "+res.size());
+ for (int i = 0; i < res.size(); i++){
+ System.out.println("--- Collision #"+i+" ---");
+ float dist = res.getCollision(i).getDistance();
+ Vector3f pt = res.getCollision(i).getContactPoint();
+ System.out.println("distance: "+dist);
+ System.out.println("point: "+pt);
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/bullet/BombControl.java b/engine/src/test/jme3test/bullet/BombControl.java
new file mode 100644
index 0000000..29d49c4
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/BombControl.java
@@ -0,0 +1,189 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.bullet;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.PhysicsTickListener;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionListener;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.objects.PhysicsGhostObject;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.shapes.EmitterSphereShape;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BombControl extends RigidBodyControl implements PhysicsCollisionListener, PhysicsTickListener {
+
+ private float explosionRadius = 10;
+ private PhysicsGhostObject ghostObject;
+ private Vector3f vector = new Vector3f();
+ private Vector3f vector2 = new Vector3f();
+ private float forceFactor = 1;
+ private ParticleEmitter effect;
+ private float fxTime = 0.5f;
+ private float maxTime = 4f;
+ private float curTime = -1.0f;
+ private float timer;
+
+ public BombControl(CollisionShape shape, float mass) {
+ super(shape, mass);
+ createGhostObject();
+ }
+
+ public BombControl(AssetManager manager, CollisionShape shape, float mass) {
+ super(shape, mass);
+ createGhostObject();
+ prepareEffect(manager);
+ }
+
+ public void setPhysicsSpace(PhysicsSpace space) {
+ super.setPhysicsSpace(space);
+ if (space != null) {
+ space.addCollisionListener(this);
+ }
+ }
+
+ private void prepareEffect(AssetManager assetManager) {
+ int COUNT_FACTOR = 1;
+ float COUNT_FACTOR_F = 1f;
+ effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
+ effect.setSelectRandomImage(true);
+ effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (float) (1f / COUNT_FACTOR_F)));
+ effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
+ effect.setStartSize(1.3f);
+ effect.setEndSize(2f);
+ effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
+ effect.setParticlesPerSec(0);
+ effect.setGravity(0, -5f, 0);
+ effect.setLowLife(.4f);
+ effect.setHighLife(.5f);
+ effect.setInitialVelocity(new Vector3f(0, 7, 0));
+ effect.setVelocityVariation(1f);
+ effect.setImagesX(2);
+ effect.setImagesY(2);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+ effect.setMaterial(mat);
+ }
+
+ protected void createGhostObject() {
+ ghostObject = new PhysicsGhostObject(new SphereCollisionShape(explosionRadius));
+ }
+
+ public void collision(PhysicsCollisionEvent event) {
+ if (space == null) {
+ return;
+ }
+ if (event.getObjectA() == this || event.getObjectB() == this) {
+ space.add(ghostObject);
+ ghostObject.setPhysicsLocation(getPhysicsLocation(vector));
+ space.addTickListener(this);
+ if (effect != null && spatial.getParent() != null) {
+ curTime = 0;
+ effect.setLocalTranslation(spatial.getLocalTranslation());
+ spatial.getParent().attachChild(effect);
+ effect.emitAllParticles();
+ }
+ space.remove(this);
+ spatial.removeFromParent();
+ }
+ }
+
+ public void prePhysicsTick(PhysicsSpace space, float f) {
+ space.removeCollisionListener(this);
+ }
+
+ public void physicsTick(PhysicsSpace space, float f) {
+ //get all overlapping objects and apply impulse to them
+ for (Iterator<PhysicsCollisionObject> it = ghostObject.getOverlappingObjects().iterator(); it.hasNext();) {
+ PhysicsCollisionObject physicsCollisionObject = it.next();
+ if (physicsCollisionObject instanceof PhysicsRigidBody) {
+ PhysicsRigidBody rBody = (PhysicsRigidBody) physicsCollisionObject;
+ rBody.getPhysicsLocation(vector2);
+ vector2.subtractLocal(vector);
+ float force = explosionRadius - vector2.length();
+ force *= forceFactor;
+ force = force > 0 ? force : 0;
+ vector2.normalizeLocal();
+ vector2.multLocal(force);
+ ((PhysicsRigidBody) physicsCollisionObject).applyImpulse(vector2, Vector3f.ZERO);
+ }
+ }
+ space.removeTickListener(this);
+ space.remove(ghostObject);
+ }
+
+ @Override
+ public void update(float tpf) {
+ super.update(tpf);
+ if(enabled){
+ timer+=tpf;
+ if(timer>maxTime){
+ if(spatial.getParent()!=null){
+ space.removeCollisionListener(this);
+ space.remove(this);
+ spatial.removeFromParent();
+ }
+ }
+ }
+ if (enabled && curTime >= 0) {
+ curTime += tpf;
+ if (curTime > fxTime) {
+ curTime = -1;
+ effect.removeFromParent();
+ }
+ }
+ }
+
+ /**
+ * @return the explosionRadius
+ */
+ public float getExplosionRadius() {
+ return explosionRadius;
+ }
+
+ /**
+ * @param explosionRadius the explosionRadius to set
+ */
+ public void setExplosionRadius(float explosionRadius) {
+ this.explosionRadius = explosionRadius;
+ createGhostObject();
+ }
+
+ public float getForceFactor() {
+ return forceFactor;
+ }
+
+ public void setForceFactor(float forceFactor) {
+ this.forceFactor = forceFactor;
+ }
+
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ throw new UnsupportedOperationException("Reading not supported.");
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ throw new UnsupportedOperationException("Saving not supported.");
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/PhysicsHoverControl.java b/engine/src/test/jme3test/bullet/PhysicsHoverControl.java
new file mode 100644
index 0000000..1464eec
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/PhysicsHoverControl.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.PhysicsTickListener;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.PhysicsControl;
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import java.io.IOException;
+
+/**
+ * PhysicsHoverControl uses a RayCast Vehicle with "slippery wheels" to simulate a hovering tank
+ * @author normenhansen
+ */
+public class PhysicsHoverControl extends PhysicsVehicle implements PhysicsControl, PhysicsTickListener {
+
+ protected Spatial spatial;
+ protected boolean enabled = true;
+ protected PhysicsSpace space = null;
+ protected float steeringValue = 0;
+ protected float accelerationValue = 0;
+ protected int xw = 3;
+ protected int zw = 5;
+ protected int yw = 2;
+ protected Vector3f HOVER_HEIGHT_LF_START = new Vector3f(xw, 1, zw);
+ protected Vector3f HOVER_HEIGHT_RF_START = new Vector3f(-xw, 1, zw);
+ protected Vector3f HOVER_HEIGHT_LR_START = new Vector3f(xw, 1, -zw);
+ protected Vector3f HOVER_HEIGHT_RR_START = new Vector3f(-xw, 1, -zw);
+ protected Vector3f HOVER_HEIGHT_LF = new Vector3f(xw, -yw, zw);
+ protected Vector3f HOVER_HEIGHT_RF = new Vector3f(-xw, -yw, zw);
+ protected Vector3f HOVER_HEIGHT_LR = new Vector3f(xw, -yw, -zw);
+ protected Vector3f HOVER_HEIGHT_RR = new Vector3f(-xw, -yw, -zw);
+ protected Vector3f tempVect1 = new Vector3f(0, 0, 0);
+ protected Vector3f tempVect2 = new Vector3f(0, 0, 0);
+ protected Vector3f tempVect3 = new Vector3f(0, 0, 0);
+// protected float rotationCounterForce = 10000f;
+// protected float speedCounterMult = 2000f;
+// protected float multiplier = 1000f;
+
+ public PhysicsHoverControl() {
+ }
+
+ /**
+ * Creates a new PhysicsNode with the supplied collision shape
+ * @param shape
+ */
+ public PhysicsHoverControl(CollisionShape shape) {
+ super(shape);
+ createWheels();
+ }
+
+ public PhysicsHoverControl(CollisionShape shape, float mass) {
+ super(shape, mass);
+ createWheels();
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setSpatial(Spatial spatial) {
+ this.spatial = spatial;
+ setUserObject(spatial);
+ if (spatial == null) {
+ return;
+ }
+ setPhysicsLocation(spatial.getWorldTranslation());
+ setPhysicsRotation(spatial.getWorldRotation().toRotationMatrix());
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ private void createWheels() {
+ addWheel(HOVER_HEIGHT_LF_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false);
+ addWheel(HOVER_HEIGHT_RF_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false);
+ addWheel(HOVER_HEIGHT_LR_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false);
+ addWheel(HOVER_HEIGHT_RR_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false);
+ for (int i = 0; i < 4; i++) {
+ getWheel(i).setFrictionSlip(0.001f);
+ }
+ }
+
+ public void prePhysicsTick(PhysicsSpace space, float f) {
+ Vector3f angVel = getAngularVelocity();
+ float rotationVelocity = angVel.getY();
+ Vector3f dir = getForwardVector(tempVect2).multLocal(1, 0, 1).normalizeLocal();
+ getLinearVelocity(tempVect3);
+ Vector3f linearVelocity = tempVect3.multLocal(1, 0, 1);
+
+ if (steeringValue != 0) {
+ if (rotationVelocity < 1 && rotationVelocity > -1) {
+ applyTorque(tempVect1.set(0, steeringValue, 0));
+ }
+ } else {
+ // counter the steering value!
+ if (rotationVelocity > 0.2f) {
+ applyTorque(tempVect1.set(0, -mass * 20, 0));
+ } else if (rotationVelocity < -0.2f) {
+ applyTorque(tempVect1.set(0, mass * 20, 0));
+ }
+ }
+ if (accelerationValue > 0) {
+ // counter force that will adjust velocity
+ // if we are not going where we want to go.
+ // this will prevent "drifting" and thus improve control
+ // of the vehicle
+ float d = dir.dot(linearVelocity.normalize());
+ Vector3f counter = dir.project(linearVelocity).normalizeLocal().negateLocal().multLocal(1 - d);
+ applyForce(counter.multLocal(mass * 10), Vector3f.ZERO);
+
+ if (linearVelocity.length() < 30) {
+ applyForce(dir.multLocal(accelerationValue), Vector3f.ZERO);
+ }
+ } else {
+ // counter the acceleration value
+ if (linearVelocity.length() > FastMath.ZERO_TOLERANCE) {
+ linearVelocity.normalizeLocal().negateLocal();
+ applyForce(linearVelocity.mult(mass * 10), Vector3f.ZERO);
+ }
+ }
+ }
+
+ public void physicsTick(PhysicsSpace space, float f) {
+ }
+
+ public void update(float tpf) {
+ if (enabled && spatial != null) {
+ getMotionState().applyTransform(spatial);
+ }
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ if (enabled && space != null && space.getDebugManager() != null) {
+ if (debugShape == null) {
+ attachDebugShape(space.getDebugManager());
+ }
+ debugShape.setLocalTranslation(motionState.getWorldLocation());
+ debugShape.setLocalRotation(motionState.getWorldRotation());
+ debugShape.updateLogicalState(0);
+ debugShape.updateGeometricState();
+ rm.renderScene(debugShape, vp);
+ }
+ }
+
+ public void setPhysicsSpace(PhysicsSpace space) {
+ if (space == null) {
+ if (this.space != null) {
+ this.space.removeCollisionObject(this);
+ this.space.removeTickListener(this);
+ }
+ this.space = space;
+ } else {
+ space.addCollisionObject(this);
+ space.addTickListener(this);
+ }
+ this.space = space;
+ }
+
+ public PhysicsSpace getPhysicsSpace() {
+ return space;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(enabled, "enabled", true);
+ oc.write(spatial, "spatial", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ enabled = ic.readBoolean("enabled", true);
+ spatial = (Spatial) ic.readSavable("spatial", null);
+ }
+
+ /**
+ * @param steeringValue the steeringValue to set
+ */
+ @Override
+ public void steer(float steeringValue) {
+ this.steeringValue = steeringValue * getMass();
+ }
+
+ /**
+ * @param accelerationValue the accelerationValue to set
+ */
+ @Override
+ public void accelerate(float accelerationValue) {
+ this.accelerationValue = accelerationValue * getMass();
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/PhysicsTestHelper.java b/engine/src/test/jme3test/bullet/PhysicsTestHelper.java
new file mode 100644
index 0000000..173a5a5
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/PhysicsTestHelper.java
@@ -0,0 +1,205 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.Application;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.texture.Texture;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class PhysicsTestHelper {
+
+ /**
+ * creates a simple physics test world with a floor, an obstacle and some test boxes
+ * @param rootNode
+ * @param assetManager
+ * @param space
+ */
+ public static void createPhysicsTestWorld(Node rootNode, AssetManager assetManager, PhysicsSpace space) {
+ AmbientLight light = new AmbientLight();
+ light.setColor(ColorRGBA.LightGray);
+ rootNode.addLight(light);
+
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+
+ Box floorBox = new Box(140, 0.25f, 140);
+ Geometry floorGeometry = new Geometry("Floor", floorBox);
+ floorGeometry.setMaterial(material);
+ floorGeometry.setLocalTranslation(0, -5, 0);
+// Plane plane = new Plane();
+// plane.setOriginNormal(new Vector3f(0, 0.25f, 0), Vector3f.UNIT_Y);
+// floorGeometry.addControl(new RigidBodyControl(new PlaneCollisionShape(plane), 0));
+ floorGeometry.addControl(new RigidBodyControl(0));
+ rootNode.attachChild(floorGeometry);
+ space.add(floorGeometry);
+
+ //movable boxes
+ for (int i = 0; i < 12; i++) {
+ Box box = new Box(0.25f, 0.25f, 0.25f);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ boxGeometry.setLocalTranslation(i, 5, -3);
+ //RigidBodyControl automatically uses box collision shapes when attached to single geometry with box mesh
+ boxGeometry.addControl(new RigidBodyControl(2));
+ rootNode.attachChild(boxGeometry);
+ space.add(boxGeometry);
+ }
+
+ //immovable sphere with mesh collision shape
+ Sphere sphere = new Sphere(8, 8, 1);
+ Geometry sphereGeometry = new Geometry("Sphere", sphere);
+ sphereGeometry.setMaterial(material);
+ sphereGeometry.setLocalTranslation(4, -4, 2);
+ sphereGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(sphere), 0));
+ rootNode.attachChild(sphereGeometry);
+ space.add(sphereGeometry);
+
+ }
+
+ public static void createPhysicsTestWorldSoccer(Node rootNode, AssetManager assetManager, PhysicsSpace space) {
+ AmbientLight light = new AmbientLight();
+ light.setColor(ColorRGBA.LightGray);
+ rootNode.addLight(light);
+
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+
+ Box floorBox = new Box(140, 0.25f, 140);
+ Geometry floorGeometry = new Geometry("Floor", floorBox);
+ floorGeometry.setMaterial(material);
+ floorGeometry.setLocalTranslation(0, -0.25f, 0);
+// Plane plane = new Plane();
+// plane.setOriginNormal(new Vector3f(0, 0.25f, 0), Vector3f.UNIT_Y);
+// floorGeometry.addControl(new RigidBodyControl(new PlaneCollisionShape(plane), 0));
+ floorGeometry.addControl(new RigidBodyControl(0));
+ rootNode.attachChild(floorGeometry);
+ space.add(floorGeometry);
+
+ //movable spheres
+ for (int i = 0; i < 5; i++) {
+ Sphere sphere = new Sphere(16, 16, .5f);
+ Geometry ballGeometry = new Geometry("Soccer ball", sphere);
+ ballGeometry.setMaterial(material);
+ ballGeometry.setLocalTranslation(i, 2, -3);
+ //RigidBodyControl automatically uses Sphere collision shapes when attached to single geometry with sphere mesh
+ ballGeometry.addControl(new RigidBodyControl(.001f));
+ ballGeometry.getControl(RigidBodyControl.class).setRestitution(1);
+ rootNode.attachChild(ballGeometry);
+ space.add(ballGeometry);
+ }
+
+ //immovable Box with mesh collision shape
+ Box box = new Box(1, 1, 1);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ boxGeometry.setLocalTranslation(4, 1, 2);
+ boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0));
+ rootNode.attachChild(boxGeometry);
+ space.add(boxGeometry);
+
+ }
+
+ /**
+ * creates a box geometry with a RigidBodyControl
+ * @param assetManager
+ * @return
+ */
+ public static Geometry createPhysicsTestBox(AssetManager assetManager) {
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Box box = new Box(0.25f, 0.25f, 0.25f);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ //RigidBodyControl automatically uses box collision shapes when attached to single geometry with box mesh
+ boxGeometry.addControl(new RigidBodyControl(2));
+ return boxGeometry;
+ }
+
+ /**
+ * creates a sphere geometry with a RigidBodyControl
+ * @param assetManager
+ * @return
+ */
+ public static Geometry createPhysicsTestSphere(AssetManager assetManager) {
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Sphere sphere = new Sphere(8, 8, 0.25f);
+ Geometry boxGeometry = new Geometry("Sphere", sphere);
+ boxGeometry.setMaterial(material);
+ //RigidBodyControl automatically uses sphere collision shapes when attached to single geometry with sphere mesh
+ boxGeometry.addControl(new RigidBodyControl(2));
+ return boxGeometry;
+ }
+
+ /**
+ * creates an empty node with a RigidBodyControl
+ * @param manager
+ * @param shape
+ * @param mass
+ * @return
+ */
+ public static Node createPhysicsTestNode(AssetManager manager, CollisionShape shape, float mass) {
+ Node node = new Node("PhysicsNode");
+ RigidBodyControl control = new RigidBodyControl(shape, mass);
+ node.addControl(control);
+ return node;
+ }
+
+ /**
+ * creates the necessary inputlistener and action to shoot balls from teh camera
+ * @param app
+ * @param rootNode
+ * @param space
+ */
+ public static void createBallShooter(final Application app, final Node rootNode, final PhysicsSpace space) {
+ ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ Sphere bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ Material mat2 = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = app.getAssetManager().loadTexture(key2);
+ mat2.setTexture("ColorMap", tex2);
+ if (name.equals("shoot") && !keyPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat2);
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.setLocalTranslation(app.getCamera().getLocation());
+ RigidBodyControl bulletControl = new RigidBodyControl(1);
+ bulletg.addControl(bulletControl);
+ bulletControl.setLinearVelocity(app.getCamera().getDirection().mult(25));
+ bulletg.addControl(bulletControl);
+ rootNode.attachChild(bulletg);
+ space.add(bulletControl);
+ }
+ }
+ };
+ app.getInputManager().addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ app.getInputManager().addListener(actionListener, "shoot");
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestAttachDriver.java b/engine/src/test/jme3test/bullet/TestAttachDriver.java
new file mode 100644
index 0000000..31f5f2f
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestAttachDriver.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.control.VehicleControl;
+import com.jme3.bullet.joints.SliderJoint;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Cylinder;
+import com.jme3.texture.Texture;
+
+/**
+ * Tests attaching/detaching nodes via joints
+ * @author normenhansen
+ */
+public class TestAttachDriver extends SimpleApplication implements ActionListener {
+
+ private VehicleControl vehicle;
+ private RigidBodyControl driver;
+ private RigidBodyControl bridge;
+ private SliderJoint slider;
+ private final float accelerationForce = 1000.0f;
+ private final float brakeForce = 100.0f;
+ private float steeringValue = 0;
+ private float accelerationValue = 0;
+ private Vector3f jumpForce = new Vector3f(0, 3000, 0);
+ private BulletAppState bulletAppState;
+
+ public static void main(String[] args) {
+ TestAttachDriver app = new TestAttachDriver();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ setupKeys();
+ setupFloor();
+ buildPlayer();
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "Lefts");
+ inputManager.addListener(this, "Rights");
+ inputManager.addListener(this, "Ups");
+ inputManager.addListener(this, "Downs");
+ inputManager.addListener(this, "Space");
+ inputManager.addListener(this, "Reset");
+ }
+
+ public void setupFloor() {
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Interface/Logo/Monkey.jpg", true);
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ tex.setMinFilter(Texture.MinFilter.Trilinear);
+ mat.setTexture("ColorMap", tex);
+
+ Box floor = new Box(Vector3f.ZERO, 100, 1f, 100);
+ Geometry floorGeom = new Geometry("Floor", floor);
+ floorGeom.setMaterial(mat);
+ floorGeom.setLocalTranslation(new Vector3f(0f, -3, 0f));
+
+ floorGeom.addControl(new RigidBodyControl(new MeshCollisionShape(floorGeom.getMesh()), 0));
+ rootNode.attachChild(floorGeom);
+ getPhysicsSpace().add(floorGeom);
+ }
+
+ private void buildPlayer() {
+ Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Red);
+
+ //create a compound shape and attach the BoxCollisionShape for the car body at 0,1,0
+ //this shifts the effective center of mass of the BoxCollisionShape to 0,-1,0
+ CompoundCollisionShape compoundShape = new CompoundCollisionShape();
+ BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1.2f, 0.5f, 2.4f));
+ compoundShape.addChildShape(box, new Vector3f(0, 1, 0));
+
+ //create vehicle node
+ Node vehicleNode=new Node("vehicleNode");
+ vehicle = new VehicleControl(compoundShape, 800);
+ vehicleNode.addControl(vehicle);
+
+ //setting suspension values for wheels, this can be a bit tricky
+ //see also https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&hl=en
+ float stiffness = 60.0f;//200=f1 car
+ float compValue = .3f; //(should be lower than damp)
+ float dampValue = .4f;
+ vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
+ vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
+ vehicle.setSuspensionStiffness(stiffness);
+ vehicle.setMaxSuspensionForce(10000.0f);
+
+ //Create four wheels and add them at their locations
+ Vector3f wheelDirection = new Vector3f(0, -1, 0); // was 0, -1, 0
+ Vector3f wheelAxle = new Vector3f(-1, 0, 0); // was -1, 0, 0
+ float radius = 0.5f;
+ float restLength = 0.3f;
+ float yOff = 0.5f;
+ float xOff = 1f;
+ float zOff = 2f;
+
+ Cylinder wheelMesh = new Cylinder(16, 16, radius, radius * 0.6f, true);
+
+ Node node1 = new Node("wheel 1 node");
+ Geometry wheels1 = new Geometry("wheel 1", wheelMesh);
+ node1.attachChild(wheels1);
+ wheels1.rotate(0, FastMath.HALF_PI, 0);
+ wheels1.setMaterial(mat);
+ vehicle.addWheel(node1, new Vector3f(-xOff, yOff, zOff),
+ wheelDirection, wheelAxle, restLength, radius, true);
+
+ Node node2 = new Node("wheel 2 node");
+ Geometry wheels2 = new Geometry("wheel 2", wheelMesh);
+ node2.attachChild(wheels2);
+ wheels2.rotate(0, FastMath.HALF_PI, 0);
+ wheels2.setMaterial(mat);
+ vehicle.addWheel(node2, new Vector3f(xOff, yOff, zOff),
+ wheelDirection, wheelAxle, restLength, radius, true);
+
+ Node node3 = new Node("wheel 3 node");
+ Geometry wheels3 = new Geometry("wheel 3", wheelMesh);
+ node3.attachChild(wheels3);
+ wheels3.rotate(0, FastMath.HALF_PI, 0);
+ wheels3.setMaterial(mat);
+ vehicle.addWheel(node3, new Vector3f(-xOff, yOff, -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ Node node4 = new Node("wheel 4 node");
+ Geometry wheels4 = new Geometry("wheel 4", wheelMesh);
+ node4.attachChild(wheels4);
+ wheels4.rotate(0, FastMath.HALF_PI, 0);
+ wheels4.setMaterial(mat);
+ vehicle.addWheel(node4, new Vector3f(xOff, yOff, -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ vehicleNode.attachChild(node1);
+ vehicleNode.attachChild(node2);
+ vehicleNode.attachChild(node3);
+ vehicleNode.attachChild(node4);
+
+ rootNode.attachChild(vehicleNode);
+ getPhysicsSpace().add(vehicle);
+
+ //driver
+ Node driverNode=new Node("driverNode");
+ driverNode.setLocalTranslation(0,2,0);
+ driver=new RigidBodyControl(new BoxCollisionShape(new Vector3f(0.2f,.5f,0.2f)));
+ driverNode.addControl(driver);
+
+ rootNode.attachChild(driverNode);
+ getPhysicsSpace().add(driver);
+
+ //joint
+ slider=new SliderJoint(driver, vehicle, Vector3f.UNIT_Y.negate(), Vector3f.UNIT_Y, true);
+ slider.setUpperLinLimit(.1f);
+ slider.setLowerLinLimit(-.1f);
+
+ getPhysicsSpace().add(slider);
+
+ Node pole1Node=new Node("pole1Node");
+ Node pole2Node=new Node("pole1Node");
+ Node bridgeNode=new Node("pole1Node");
+ pole1Node.setLocalTranslation(new Vector3f(-2,-1,4));
+ pole2Node.setLocalTranslation(new Vector3f(2,-1,4));
+ bridgeNode.setLocalTranslation(new Vector3f(0,1.4f,4));
+
+ RigidBodyControl pole1=new RigidBodyControl(new BoxCollisionShape(new Vector3f(0.2f,1.25f,0.2f)),0);
+ pole1Node.addControl(pole1);
+ RigidBodyControl pole2=new RigidBodyControl(new BoxCollisionShape(new Vector3f(0.2f,1.25f,0.2f)),0);
+ pole2Node.addControl(pole2);
+ bridge=new RigidBodyControl(new BoxCollisionShape(new Vector3f(2.5f,0.2f,0.2f)));
+ bridgeNode.addControl(bridge);
+
+ rootNode.attachChild(pole1Node);
+ rootNode.attachChild(pole2Node);
+ rootNode.attachChild(bridgeNode);
+ getPhysicsSpace().add(pole1);
+ getPhysicsSpace().add(pole2);
+ getPhysicsSpace().add(bridge);
+
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ Quaternion quat=new Quaternion();
+ cam.lookAt(vehicle.getPhysicsLocation(), Vector3f.UNIT_Y);
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Lefts")) {
+ if (value) {
+ steeringValue += .5f;
+ } else {
+ steeringValue += -.5f;
+ }
+ vehicle.steer(steeringValue);
+ } else if (binding.equals("Rights")) {
+ if (value) {
+ steeringValue += -.5f;
+ } else {
+ steeringValue += .5f;
+ }
+ vehicle.steer(steeringValue);
+ } else if (binding.equals("Ups")) {
+ if (value) {
+ accelerationValue += accelerationForce;
+ } else {
+ accelerationValue -= accelerationForce;
+ }
+ vehicle.accelerate(accelerationValue);
+ } else if (binding.equals("Downs")) {
+ if (value) {
+ vehicle.brake(brakeForce);
+ } else {
+ vehicle.brake(0f);
+ }
+ } else if (binding.equals("Space")) {
+ if (value) {
+ getPhysicsSpace().remove(slider);
+ slider.destroy();
+ vehicle.applyImpulse(jumpForce, Vector3f.ZERO);
+ }
+ } else if (binding.equals("Reset")) {
+ if (value) {
+ System.out.println("Reset");
+ vehicle.setPhysicsLocation(new Vector3f(0, 0, 0));
+ vehicle.setPhysicsRotation(new Matrix3f());
+ vehicle.setLinearVelocity(Vector3f.ZERO);
+ vehicle.setAngularVelocity(Vector3f.ZERO);
+ vehicle.resetSuspension();
+ bridge.setPhysicsLocation(new Vector3f(0,1.4f,4));
+ bridge.setPhysicsRotation(Quaternion.DIRECTION_Z.toRotationMatrix());
+ }
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestAttachGhostObject.java b/engine/src/test/jme3test/bullet/TestAttachGhostObject.java
new file mode 100644
index 0000000..2a117f2
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestAttachGhostObject.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.GhostControl;
+import com.jme3.bullet.control.PhysicsControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.joints.HingeJoint;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+
+/**
+ * Tests attaching ghost nodes to physicsnodes via the scenegraph
+ * @author normenhansen
+ */
+public class TestAttachGhostObject extends SimpleApplication implements AnalogListener {
+
+ private HingeJoint joint;
+ private GhostControl ghostControl;
+ private Node collisionNode;
+ private Node hammerNode;
+ private Vector3f tempVec = new Vector3f();
+ private BulletAppState bulletAppState;
+
+ public static void main(String[] args) {
+ TestAttachGhostObject app = new TestAttachGhostObject();
+ app.start();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this, "Lefts", "Rights", "Space");
+ }
+
+ public void onAnalog(String binding, float value, float tpf) {
+ if (binding.equals("Lefts")) {
+ joint.enableMotor(true, 1, .1f);
+ } else if (binding.equals("Rights")) {
+ joint.enableMotor(true, -1, .1f);
+ } else if (binding.equals("Space")) {
+ joint.enableMotor(false, 0, 0);
+ }
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ setupKeys();
+ setupJoint();
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ public void setupJoint() {
+ Node holderNode = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(.1f, .1f, .1f)), 0);
+ holderNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, 0, 0f));
+ rootNode.attachChild(holderNode);
+ getPhysicsSpace().add(holderNode);
+
+ Node hammerNode = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(.3f, .3f, .3f)), 1);
+ hammerNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -1, 0f));
+ rootNode.attachChild(hammerNode);
+ getPhysicsSpace().add(hammerNode);
+
+ //immovable
+ collisionNode = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(.3f, .3f, .3f)), 0);
+ collisionNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(1.8f, 0, 0f));
+ rootNode.attachChild(collisionNode);
+ getPhysicsSpace().add(collisionNode);
+
+ //ghost node
+ ghostControl = new GhostControl(new SphereCollisionShape(0.7f));
+
+ hammerNode.addControl(ghostControl);
+ getPhysicsSpace().add(ghostControl);
+
+ joint = new HingeJoint(holderNode.getControl(RigidBodyControl.class), hammerNode.getControl(RigidBodyControl.class), Vector3f.ZERO, new Vector3f(0f, -1, 0f), Vector3f.UNIT_Z, Vector3f.UNIT_Z);
+ getPhysicsSpace().add(joint);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ if (ghostControl.getOverlappingObjects().contains(collisionNode.getControl(PhysicsControl.class))) {
+ fpsText.setText("collide");
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestBoneRagdoll.java b/engine/src/test/jme3test/bullet/TestBoneRagdoll.java
new file mode 100644
index 0000000..cf62ca2
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestBoneRagdoll.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.animation.*;
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.RagdollCollisionListener;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.KinematicRagdollControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.debug.SkeletonDebugger;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.texture.Texture;
+
+/**
+ * PHYSICS RAGDOLLS ARE NOT WORKING PROPERLY YET!
+ * @author normenhansen
+ */
+public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisionListener, AnimEventListener {
+
+ private BulletAppState bulletAppState;
+ Material matBullet;
+ Node model;
+ KinematicRagdollControl ragdoll;
+ float bulletSize = 1f;
+ Material mat;
+ Material mat3;
+ private Sphere bullet;
+ private SphereCollisionShape bulletCollisionShape;
+
+ public static void main(String[] args) {
+ TestBoneRagdoll app = new TestBoneRagdoll();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ initCrossHairs();
+ initMaterial();
+
+ cam.setLocation(new Vector3f(0.26924422f, 6.646658f, 22.265987f));
+ cam.setRotation(new Quaternion(-2.302544E-4f, 0.99302495f, -0.117888905f, -0.0019395084f));
+
+
+ bulletAppState = new BulletAppState();
+ bulletAppState.setEnabled(true);
+ stateManager.attach(bulletAppState);
+ bullet = new Sphere(32, 32, 1.0f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(1.0f);
+
+// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ setupLight();
+
+ model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
+
+ // model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
+
+ //debug view
+ AnimControl control = model.getControl(AnimControl.class);
+ SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton());
+ Material mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.getAdditionalRenderState().setWireframe(true);
+ mat2.setColor("Color", ColorRGBA.Green);
+ mat2.getAdditionalRenderState().setDepthTest(false);
+ skeletonDebug.setMaterial(mat2);
+ skeletonDebug.setLocalTranslation(model.getLocalTranslation());
+
+ //Note: PhysicsRagdollControl is still TODO, constructor will change
+ ragdoll = new KinematicRagdollControl(0.5f);
+ setupSinbad(ragdoll);
+ ragdoll.addCollisionListener(this);
+ model.addControl(ragdoll);
+
+ float eighth_pi = FastMath.PI * 0.125f;
+ ragdoll.setJointLimit("Waist", eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi);
+ ragdoll.setJointLimit("Chest", eighth_pi, eighth_pi, 0, 0, eighth_pi, eighth_pi);
+
+
+ //Oto's head is almost rigid
+ // ragdoll.setJointLimit("head", 0, 0, eighth_pi, -eighth_pi, 0, 0);
+
+ getPhysicsSpace().add(ragdoll);
+ speed = 1.3f;
+
+ rootNode.attachChild(model);
+ // rootNode.attachChild(skeletonDebug);
+ flyCam.setMoveSpeed(50);
+
+
+ animChannel = control.createChannel();
+ animChannel.setAnim("Dance");
+ control.addListener(this);
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("toggle") && isPressed) {
+
+ Vector3f v = new Vector3f();
+ v.set(model.getLocalTranslation());
+ v.y = 0;
+ model.setLocalTranslation(v);
+ Quaternion q = new Quaternion();
+ float[] angles = new float[3];
+ model.getLocalRotation().toAngles(angles);
+ q.fromAngleAxis(angles[1], Vector3f.UNIT_Y);
+ model.setLocalRotation(q);
+ if (angles[0] < 0) {
+ animChannel.setAnim("StandUpBack");
+ ragdoll.blendToKinematicMode(0.5f);
+ } else {
+ animChannel.setAnim("StandUpFront");
+ ragdoll.blendToKinematicMode(0.5f);
+ }
+
+ }
+ if (name.equals("bullet+") && isPressed) {
+ bulletSize += 0.1f;
+
+ }
+ if (name.equals("bullet-") && isPressed) {
+ bulletSize -= 0.1f;
+
+ }
+
+ if (name.equals("stop") && isPressed) {
+ ragdoll.setEnabled(!ragdoll.isEnabled());
+ ragdoll.setRagdollMode();
+ }
+
+ if (name.equals("shoot") && !isPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(matBullet);
+ bulletg.setLocalTranslation(cam.getLocation());
+ bulletg.setLocalScale(bulletSize);
+ bulletCollisionShape = new SphereCollisionShape(bulletSize);
+ RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, bulletSize * 10);
+ bulletNode.setCcdMotionThreshold(0.001f);
+ bulletNode.setLinearVelocity(cam.getDirection().mult(80));
+ bulletg.addControl(bulletNode);
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletNode);
+ }
+ if (name.equals("boom") && !isPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(matBullet);
+ bulletg.setLocalTranslation(cam.getLocation());
+ bulletg.setLocalScale(bulletSize);
+ bulletCollisionShape = new SphereCollisionShape(bulletSize);
+ BombControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
+ bulletNode.setForceFactor(8);
+ bulletNode.setExplosionRadius(20);
+ bulletNode.setCcdMotionThreshold(0.001f);
+ bulletNode.setLinearVelocity(cam.getDirection().mult(180));
+ bulletg.addControl(bulletNode);
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletNode);
+ }
+ }
+ }, "toggle", "shoot", "stop", "bullet+", "bullet-", "boom");
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addMapping("boom", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
+ inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("bullet-", new KeyTrigger(KeyInput.KEY_COMMA));
+ inputManager.addMapping("bullet+", new KeyTrigger(KeyInput.KEY_PERIOD));
+
+
+ }
+
+ private void setupLight() {
+ // AmbientLight al = new AmbientLight();
+ // al.setColor(ColorRGBA.White.mult(1));
+ // rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ public void initMaterial() {
+
+ matBullet = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = assetManager.loadTexture(key2);
+ matBullet.setTexture("ColorMap", tex2);
+ }
+
+ protected void initCrossHairs() {
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+
+ public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event) {
+
+ if (object.getUserObject() != null && object.getUserObject() instanceof Geometry) {
+ Geometry geom = (Geometry) object.getUserObject();
+ if ("Floor".equals(geom.getName())) {
+ return;
+ }
+ }
+
+ ragdoll.setRagdollMode();
+
+ }
+
+ private void setupSinbad(KinematicRagdollControl ragdoll) {
+ ragdoll.addBoneName("Ulna.L");
+ ragdoll.addBoneName("Ulna.R");
+ ragdoll.addBoneName("Chest");
+ ragdoll.addBoneName("Foot.L");
+ ragdoll.addBoneName("Foot.R");
+ ragdoll.addBoneName("Hand.R");
+ ragdoll.addBoneName("Hand.L");
+ ragdoll.addBoneName("Neck");
+ ragdoll.addBoneName("Root");
+ ragdoll.addBoneName("Stomach");
+ ragdoll.addBoneName("Waist");
+ ragdoll.addBoneName("Humerus.L");
+ ragdoll.addBoneName("Humerus.R");
+ ragdoll.addBoneName("Thigh.L");
+ ragdoll.addBoneName("Thigh.R");
+ ragdoll.addBoneName("Calf.L");
+ ragdoll.addBoneName("Calf.R");
+ ragdoll.addBoneName("Clavicle.L");
+ ragdoll.addBoneName("Clavicle.R");
+
+ }
+ float elTime = 0;
+ boolean forward = true;
+ AnimControl animControl;
+ AnimChannel animChannel;
+ Vector3f direction = new Vector3f(0, 0, 1);
+ Quaternion rotate = new Quaternion().fromAngleAxis(FastMath.PI / 8, Vector3f.UNIT_Y);
+ boolean dance = true;
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ // System.out.println(((BoundingBox) model.getWorldBound()).getYExtent());
+// elTime += tpf;
+// if (elTime > 3) {
+// elTime = 0;
+// if (dance) {
+// rotate.multLocal(direction);
+// }
+// if (Math.random() > 0.80) {
+// dance = true;
+// animChannel.setAnim("Dance");
+// } else {
+// dance = false;
+// animChannel.setAnim("RunBase");
+// rotate.fromAngleAxis(FastMath.QUARTER_PI * ((float) Math.random() - 0.5f), Vector3f.UNIT_Y);
+// rotate.multLocal(direction);
+// }
+// }
+// if (!ragdoll.hasControl() && !dance) {
+// if (model.getLocalTranslation().getZ() < -10) {
+// direction.z = 1;
+// direction.normalizeLocal();
+// } else if (model.getLocalTranslation().getZ() > 10) {
+// direction.z = -1;
+// direction.normalizeLocal();
+// }
+// if (model.getLocalTranslation().getX() < -10) {
+// direction.x = 1;
+// direction.normalizeLocal();
+// } else if (model.getLocalTranslation().getX() > 10) {
+// direction.x = -1;
+// direction.normalizeLocal();
+// }
+// model.move(direction.multLocal(tpf * 8));
+// direction.normalizeLocal();
+// model.lookAt(model.getLocalTranslation().add(direction), Vector3f.UNIT_Y);
+// }
+ }
+
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+// if(channel.getAnimationName().equals("StandUpFront")){
+// channel.setAnim("Dance");
+// }
+
+ if (channel.getAnimationName().equals("StandUpBack") || channel.getAnimationName().equals("StandUpFront")) {
+ channel.setLoopMode(LoopMode.DontLoop);
+ channel.setAnim("IdleTop", 5);
+ channel.setLoopMode(LoopMode.Loop);
+ }
+// if(channel.getAnimationName().equals("IdleTop")){
+// channel.setAnim("StandUpFront");
+// }
+
+ }
+
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestBrickTower.java b/engine/src/test/jme3test/bullet/TestBrickTower.java
new file mode 100644
index 0000000..0e58714
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestBrickTower.java
@@ -0,0 +1,236 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jme3test.bullet;
+
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.shadow.PssmShadowRenderer;
+import com.jme3.shadow.PssmShadowRenderer.CompareMode;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+/**
+ *
+ * @author double1984 (tower mod by atom)
+ */
+public class TestBrickTower extends SimpleApplication {
+
+ int bricksPerLayer = 8;
+ int brickLayers = 30;
+
+ static float brickWidth = .75f, brickHeight = .25f, brickDepth = .25f;
+ float radius = 3f;
+ float angle = 0;
+
+
+ Material mat;
+ Material mat2;
+ Material mat3;
+ PssmShadowRenderer bsr;
+ private Sphere bullet;
+ private Box brick;
+ private SphereCollisionShape bulletCollisionShape;
+
+ private BulletAppState bulletAppState;
+
+ public static void main(String args[]) {
+ TestBrickTower f = new TestBrickTower();
+ f.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+ // bulletAppState.setEnabled(false);
+ stateManager.attach(bulletAppState);
+ bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(0.4f);
+
+ brick = new Box(Vector3f.ZERO, brickWidth, brickHeight, brickDepth);
+ brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
+ //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ initMaterial();
+ initTower();
+ initFloor();
+ initCrossHairs();
+ this.cam.setLocation(new Vector3f(0, 25f, 8f));
+ cam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0));
+ cam.setFrustumFar(80);
+ inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(actionListener, "shoot");
+ rootNode.setShadowMode(ShadowMode.Off);
+ bsr = new PssmShadowRenderer(assetManager, 1024, 2);
+ bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ bsr.setLambda(0.55f);
+ bsr.setShadowIntensity(0.6f);
+ bsr.setCompareMode(CompareMode.Hardware);
+ bsr.setFilterMode(FilterMode.PCF4);
+ viewPort.addProcessor(bsr);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("shoot") && !keyPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat2);
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.setLocalTranslation(cam.getLocation());
+ RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
+// RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, 1);
+ bulletNode.setLinearVelocity(cam.getDirection().mult(25));
+ bulletg.addControl(bulletNode);
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletNode);
+ }
+ }
+ };
+
+ public void initTower() {
+ double tempX = 0;
+ double tempY = 0;
+ double tempZ = 0;
+ angle = 0f;
+ for (int i = 0; i < brickLayers; i++){
+ // Increment rows
+ if(i!=0)
+ tempY+=brickHeight*2;
+ else
+ tempY=brickHeight;
+ // Alternate brick seams
+ angle = 360.0f / bricksPerLayer * i/2f;
+ for (int j = 0; j < bricksPerLayer; j++){
+ tempZ = Math.cos(Math.toRadians(angle))*radius;
+ tempX = Math.sin(Math.toRadians(angle))*radius;
+ System.out.println("x="+((float)(tempX))+" y="+((float)(tempY))+" z="+(float)(tempZ));
+ Vector3f vt = new Vector3f((float)(tempX), (float)(tempY), (float)(tempZ));
+ // Add crenelation
+ if (i==brickLayers-1){
+ if (j%2 == 0){
+ addBrick(vt);
+ }
+ }
+ // Create main tower
+ else {
+ addBrick(vt);
+ }
+ angle += 360.0/bricksPerLayer;
+ }
+ }
+
+ }
+
+ public void initFloor() {
+ Box floorBox = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
+ floorBox.scaleTextureCoordinates(new Vector2f(3, 6));
+
+ Geometry floor = new Geometry("floor", floorBox);
+ floor.setMaterial(mat3);
+ floor.setShadowMode(ShadowMode.Receive);
+ floor.setLocalTranslation(0, 0, 0);
+ floor.addControl(new RigidBodyControl(0));
+ this.rootNode.attachChild(floor);
+ this.getPhysicsSpace().add(floor);
+ }
+
+ public void initMaterial() {
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ mat.setTexture("ColorMap", tex);
+
+ mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = assetManager.loadTexture(key2);
+ mat2.setTexture("ColorMap", tex2);
+
+ mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
+ key3.setGenerateMips(true);
+ Texture tex3 = assetManager.loadTexture(key3);
+ tex3.setWrap(WrapMode.Repeat);
+ mat3.setTexture("ColorMap", tex3);
+ }
+
+ public void addBrick(Vector3f ori) {
+ Geometry reBoxg = new Geometry("brick", brick);
+ reBoxg.setMaterial(mat);
+ reBoxg.setLocalTranslation(ori);
+ reBoxg.rotate(0f, (float)Math.toRadians(angle) , 0f );
+ reBoxg.addControl(new RigidBodyControl(1.5f));
+ reBoxg.setShadowMode(ShadowMode.CastAndReceive);
+ reBoxg.getControl(RigidBodyControl.class).setFriction(1.6f);
+ this.rootNode.attachChild(reBoxg);
+ this.getPhysicsSpace().add(reBoxg);
+ }
+
+ protected void initCrossHairs() {
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/bullet/TestBrickWall.java b/engine/src/test/jme3test/bullet/TestBrickWall.java
new file mode 100644
index 0000000..1b9a7d7
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestBrickWall.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.shadow.BasicShadowRenderer;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+/**
+ *
+ * @author double1984
+ */
+public class TestBrickWall extends SimpleApplication {
+
+ static float bLength = 0.48f;
+ static float bWidth = 0.24f;
+ static float bHeight = 0.12f;
+ Material mat;
+ Material mat2;
+ Material mat3;
+ BasicShadowRenderer bsr;
+ private static Sphere bullet;
+ private static Box brick;
+ private static SphereCollisionShape bulletCollisionShape;
+
+ private BulletAppState bulletAppState;
+
+ public static void main(String args[]) {
+ TestBrickWall f = new TestBrickWall();
+ f.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ bulletAppState = new BulletAppState();
+ bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+ stateManager.attach(bulletAppState);
+
+ bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(0.4f);
+ brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
+ brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
+
+ initMaterial();
+ initWall();
+ initFloor();
+ initCrossHairs();
+ this.cam.setLocation(new Vector3f(0, 6f, 6f));
+ cam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0));
+ cam.setFrustumFar(15);
+ inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(actionListener, "shoot");
+ inputManager.addMapping("gc", new KeyTrigger(KeyInput.KEY_X));
+ inputManager.addListener(actionListener, "gc");
+
+ rootNode.setShadowMode(ShadowMode.Off);
+ bsr = new BasicShadowRenderer(assetManager, 256);
+ bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ viewPort.addProcessor(bsr);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("shoot") && !keyPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat2);
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.setLocalTranslation(cam.getLocation());
+
+ SphereCollisionShape bulletCollisionShape = new SphereCollisionShape(0.4f);
+ RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
+// RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, 1);
+ bulletNode.setLinearVelocity(cam.getDirection().mult(25));
+ bulletg.addControl(bulletNode);
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletNode);
+ }
+ if (name.equals("gc") && !keyPressed) {
+ System.gc();
+ }
+ }
+ };
+
+ public void initWall() {
+ float startpt = bLength / 4;
+ float height = 0;
+ for (int j = 0; j < 15; j++) {
+ for (int i = 0; i < 4; i++) {
+ Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight + height, 0);
+ addBrick(vt);
+ }
+ startpt = -startpt;
+ height += 2 * bHeight;
+ }
+ }
+
+ public void initFloor() {
+ Box floorBox = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
+ floorBox.scaleTextureCoordinates(new Vector2f(3, 6));
+
+ Geometry floor = new Geometry("floor", floorBox);
+ floor.setMaterial(mat3);
+ floor.setShadowMode(ShadowMode.Receive);
+ floor.setLocalTranslation(0, -0.1f, 0);
+ floor.addControl(new RigidBodyControl(new BoxCollisionShape(new Vector3f(10f, 0.1f, 5f)), 0));
+ this.rootNode.attachChild(floor);
+ this.getPhysicsSpace().add(floor);
+ }
+
+ public void initMaterial() {
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ mat.setTexture("ColorMap", tex);
+
+ mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = assetManager.loadTexture(key2);
+ mat2.setTexture("ColorMap", tex2);
+
+ mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
+ key3.setGenerateMips(true);
+ Texture tex3 = assetManager.loadTexture(key3);
+ tex3.setWrap(WrapMode.Repeat);
+ mat3.setTexture("ColorMap", tex3);
+ }
+
+ public void addBrick(Vector3f ori) {
+
+ Geometry reBoxg = new Geometry("brick", brick);
+ reBoxg.setMaterial(mat);
+ reBoxg.setLocalTranslation(ori);
+ //for geometry with sphere mesh the physics system automatically uses a sphere collision shape
+ reBoxg.addControl(new RigidBodyControl(1.5f));
+ reBoxg.setShadowMode(ShadowMode.CastAndReceive);
+ reBoxg.getControl(RigidBodyControl.class).setFriction(0.6f);
+ this.rootNode.attachChild(reBoxg);
+ this.getPhysicsSpace().add(reBoxg);
+ }
+
+ protected void initCrossHairs() {
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestCcd.java b/engine/src/test/jme3test/bullet/TestCcd.java
new file mode 100644
index 0000000..1e9949a
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestCcd.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TestCcd extends SimpleApplication implements ActionListener {
+
+ private Material mat;
+ private Material mat2;
+ private Sphere bullet;
+ private SphereCollisionShape bulletCollisionShape;
+ private BulletAppState bulletAppState;
+
+ public static void main(String[] args) {
+ TestCcd app = new TestCcd();
+ app.start();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addMapping("shoot2", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
+ inputManager.addListener(this, "shoot");
+ inputManager.addListener(this, "shoot2");
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(0.1f);
+ setupKeys();
+
+ mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Green);
+
+ mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.getAdditionalRenderState().setWireframe(true);
+ mat2.setColor("Color", ColorRGBA.Red);
+
+ // An obstacle mesh, does not move (mass=0)
+ Node node2 = new Node();
+ node2.setName("mesh");
+ node2.setLocalTranslation(new Vector3f(2.5f, 0, 0f));
+ node2.addControl(new RigidBodyControl(new MeshCollisionShape(new Box(Vector3f.ZERO, 4, 4, 0.1f)), 0));
+ rootNode.attachChild(node2);
+ getPhysicsSpace().add(node2);
+
+ // The floor, does not move (mass=0)
+ Node node3 = new Node();
+ node3.setLocalTranslation(new Vector3f(0f, -6, 0f));
+ node3.addControl(new RigidBodyControl(new BoxCollisionShape(new Vector3f(100, 1, 100)), 0));
+ rootNode.attachChild(node3);
+ getPhysicsSpace().add(node3);
+
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ //TODO: add update code
+ }
+
+ @Override
+ public void simpleRender(RenderManager rm) {
+ //TODO: add render code
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("shoot") && !value) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat);
+ bulletg.setName("bullet");
+ bulletg.setLocalTranslation(cam.getLocation());
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.addControl(new RigidBodyControl(bulletCollisionShape, 1));
+ bulletg.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.1f);
+ bulletg.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40));
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletg);
+ } else if (binding.equals("shoot2") && !value) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat2);
+ bulletg.setName("bullet");
+ bulletg.setLocalTranslation(cam.getLocation());
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.addControl(new RigidBodyControl(bulletCollisionShape, 1));
+ bulletg.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40));
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletg);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestCollisionGroups.java b/engine/src/test/jme3test/bullet/TestCollisionGroups.java
new file mode 100644
index 0000000..68b11d1
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestCollisionGroups.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TestCollisionGroups extends SimpleApplication {
+
+ private BulletAppState bulletAppState;
+
+ public static void main(String[] args) {
+ TestCollisionGroups app = new TestCollisionGroups();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+
+ // Add a physics sphere to the world
+ Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
+ rootNode.attachChild(physicsSphere);
+ getPhysicsSpace().add(physicsSphere);
+
+ // Add a physics sphere to the world
+ Node physicsSphere2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(4, 8, 0));
+ physicsSphere2.getControl(RigidBodyControl.class).addCollideWithGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
+ rootNode.attachChild(physicsSphere2);
+ getPhysicsSpace().add(physicsSphere2);
+
+ // an obstacle mesh, does not move (mass=0)
+ Node node2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new MeshCollisionShape(new Sphere(16, 16, 1.2f)), 0);
+ node2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2.5f, -4, 0f));
+ node2.getControl(RigidBodyControl.class).setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
+ node2.getControl(RigidBodyControl.class).setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);
+ rootNode.attachChild(node2);
+ getPhysicsSpace().add(node2);
+
+ // the floor, does not move (mass=0)
+ Node node3 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new MeshCollisionShape(new Box(Vector3f.ZERO, 100f, 0.2f, 100f)), 0);
+ node3.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -6, 0f));
+ rootNode.attachChild(node3);
+ getPhysicsSpace().add(node3);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ //TODO: add update code
+ }
+
+ @Override
+ public void simpleRender(RenderManager rm) {
+ //TODO: add render code
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestCollisionListener.java b/engine/src/test/jme3test/bullet/TestCollisionListener.java
new file mode 100644
index 0000000..bad8794
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestCollisionListener.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionListener;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TestCollisionListener extends SimpleApplication implements PhysicsCollisionListener {
+
+ private BulletAppState bulletAppState;
+ private Sphere bullet;
+ private SphereCollisionShape bulletCollisionShape;
+
+ public static void main(String[] args) {
+ TestCollisionListener app = new TestCollisionListener();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(0.4f);
+
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ PhysicsTestHelper.createBallShooter(this, rootNode, bulletAppState.getPhysicsSpace());
+
+ // add ourselves as collision listener
+ getPhysicsSpace().addCollisionListener(this);
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ //TODO: add update code
+ }
+
+ @Override
+ public void simpleRender(RenderManager rm) {
+ //TODO: add render code
+ }
+
+ public void collision(PhysicsCollisionEvent event) {
+ if ("Box".equals(event.getNodeA().getName()) || "Box".equals(event.getNodeB().getName())) {
+ if ("bullet".equals(event.getNodeA().getName()) || "bullet".equals(event.getNodeB().getName())) {
+ fpsText.setText("You hit the box!");
+ }
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java b/engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java
new file mode 100644
index 0000000..3702091
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Cylinder;
+import com.jme3.scene.shape.Torus;
+
+/**
+ * This is a basic Test of jbullet-jme functions
+ *
+ * @author normenhansen
+ */
+public class TestCollisionShapeFactory extends SimpleApplication {
+
+ private BulletAppState bulletAppState;
+ private Material mat1;
+ private Material mat2;
+ private Material mat3;
+
+ public static void main(String[] args) {
+ TestCollisionShapeFactory app = new TestCollisionShapeFactory();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ createMaterial();
+
+ Node node = new Node("node1");
+ attachRandomGeometry(node, mat1);
+ randomizeTransform(node);
+
+ Node node2 = new Node("node2");
+ attachRandomGeometry(node2, mat2);
+ randomizeTransform(node2);
+
+ node.attachChild(node2);
+ rootNode.attachChild(node);
+
+ RigidBodyControl control = new RigidBodyControl(0);
+ node.addControl(control);
+ getPhysicsSpace().add(control);
+
+ //test single geometry too
+ Geometry myGeom = new Geometry("cylinder", new Cylinder(16, 16, 0.5f, 1));
+ myGeom.setMaterial(mat3);
+ randomizeTransform(myGeom);
+ rootNode.attachChild(myGeom);
+ RigidBodyControl control3 = new RigidBodyControl(0);
+ myGeom.addControl(control3);
+ getPhysicsSpace().add(control3);
+ }
+
+ private void attachRandomGeometry(Node node, Material mat) {
+ Box box = new Box(0.25f, 0.25f, 0.25f);
+ Torus torus = new Torus(16, 16, 0.2f, 0.8f);
+ Geometry[] boxes = new Geometry[]{
+ new Geometry("box1", box),
+ new Geometry("box2", box),
+ new Geometry("box3", box),
+ new Geometry("torus1", torus),
+ new Geometry("torus2", torus),
+ new Geometry("torus3", torus)
+ };
+ for (int i = 0; i < boxes.length; i++) {
+ Geometry geometry = boxes[i];
+ geometry.setLocalTranslation((float) Math.random() * 10 -10, (float) Math.random() * 10 -10, (float) Math.random() * 10 -10);
+ geometry.setLocalRotation(new Quaternion().fromAngles((float) Math.random() * FastMath.PI, (float) Math.random() * FastMath.PI, (float) Math.random() * FastMath.PI));
+ geometry.setLocalScale((float) Math.random() * 10 -10, (float) Math.random() * 10 -10, (float) Math.random() * 10 -10);
+ geometry.setMaterial(mat);
+ node.attachChild(geometry);
+ }
+ }
+
+ private void randomizeTransform(Spatial spat){
+ spat.setLocalTranslation((float) Math.random() * 10, (float) Math.random() * 10, (float) Math.random() * 10);
+ spat.setLocalTranslation((float) Math.random() * 10, (float) Math.random() * 10, (float) Math.random() * 10);
+ spat.setLocalScale((float) Math.random() * 2, (float) Math.random() * 2, (float) Math.random() * 2);
+ }
+
+ private void createMaterial() {
+ mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.Green);
+ mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.setColor("Color", ColorRGBA.Red);
+ mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat3.setColor("Color", ColorRGBA.Yellow);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestFancyCar.java b/engine/src/test/jme3test/bullet/TestFancyCar.java
new file mode 100644
index 0000000..9d9a6a5
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestFancyCar.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.VehicleControl;
+import com.jme3.bullet.objects.VehicleWheel;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.shadow.BasicShadowRenderer;
+
+public class TestFancyCar extends SimpleApplication implements ActionListener {
+
+ private BulletAppState bulletAppState;
+ private VehicleControl player;
+ private VehicleWheel fr, fl, br, bl;
+ private Node node_fr, node_fl, node_br, node_bl;
+ private float wheelRadius;
+ private float steeringValue = 0;
+ private float accelerationValue = 0;
+ private Node carNode;
+
+ public static void main(String[] args) {
+ TestFancyCar app = new TestFancyCar();
+ app.start();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "Lefts");
+ inputManager.addListener(this, "Rights");
+ inputManager.addListener(this, "Ups");
+ inputManager.addListener(this, "Downs");
+ inputManager.addListener(this, "Space");
+ inputManager.addListener(this, "Reset");
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ if (settings.getRenderer().startsWith("LWJGL")) {
+ BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 512);
+ bsr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal());
+ viewPort.addProcessor(bsr);
+ }
+ cam.setFrustumFar(150f);
+ flyCam.setMoveSpeed(10);
+
+ setupKeys();
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+// setupFloor();
+ buildPlayer();
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.5f, -1f, -0.3f).normalizeLocal());
+ rootNode.addLight(dl);
+
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(0.5f, -0.1f, 0.3f).normalizeLocal());
+ rootNode.addLight(dl);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+// public void setupFloor() {
+// Material mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m");
+// mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+//// mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+//// mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
+//
+// Box floor = new Box(Vector3f.ZERO, 140, 1f, 140);
+// floor.scaleTextureCoordinates(new Vector2f(112.0f, 112.0f));
+// Geometry floorGeom = new Geometry("Floor", floor);
+// floorGeom.setShadowMode(ShadowMode.Receive);
+// floorGeom.setMaterial(mat);
+//
+// PhysicsNode tb = new PhysicsNode(floorGeom, new MeshCollisionShape(floorGeom.getMesh()), 0);
+// tb.setLocalTranslation(new Vector3f(0f, -6, 0f));
+//// tb.attachDebugShape(assetManager);
+// rootNode.attachChild(tb);
+// getPhysicsSpace().add(tb);
+// }
+
+ private Geometry findGeom(Spatial spatial, String name) {
+ if (spatial instanceof Node) {
+ Node node = (Node) spatial;
+ for (int i = 0; i < node.getQuantity(); i++) {
+ Spatial child = node.getChild(i);
+ Geometry result = findGeom(child, name);
+ if (result != null) {
+ return result;
+ }
+ }
+ } else if (spatial instanceof Geometry) {
+ if (spatial.getName().startsWith(name)) {
+ return (Geometry) spatial;
+ }
+ }
+ return null;
+ }
+
+ private void buildPlayer() {
+ float stiffness = 120.0f;//200=f1 car
+ float compValue = 0.2f; //(lower than damp!)
+ float dampValue = 0.3f;
+ final float mass = 400;
+
+ //Load model and get chassis Geometry
+ carNode = (Node)assetManager.loadModel("Models/Ferrari/Car.scene");
+ carNode.setShadowMode(ShadowMode.Cast);
+ Geometry chasis = findGeom(carNode, "Car");
+ BoundingBox box = (BoundingBox) chasis.getModelBound();
+
+ //Create a hull collision shape for the chassis
+ CollisionShape carHull = CollisionShapeFactory.createDynamicMeshShape(chasis);
+
+ //Create a vehicle control
+ player = new VehicleControl(carHull, mass);
+ carNode.addControl(player);
+
+ //Setting default values for wheels
+ player.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
+ player.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
+ player.setSuspensionStiffness(stiffness);
+ player.setMaxSuspensionForce(10000);
+
+ //Create four wheels and add them at their locations
+ //note that our fancy car actually goes backwards..
+ Vector3f wheelDirection = new Vector3f(0, -1, 0);
+ Vector3f wheelAxle = new Vector3f(-1, 0, 0);
+
+ Geometry wheel_fr = findGeom(carNode, "WheelFrontRight");
+ wheel_fr.center();
+ box = (BoundingBox) wheel_fr.getModelBound();
+ wheelRadius = box.getYExtent();
+ float back_wheel_h = (wheelRadius * 1.7f) - 1f;
+ float front_wheel_h = (wheelRadius * 1.9f) - 1f;
+ player.addWheel(wheel_fr.getParent(), box.getCenter().add(0, -front_wheel_h, 0),
+ wheelDirection, wheelAxle, 0.2f, wheelRadius, true);
+
+ Geometry wheel_fl = findGeom(carNode, "WheelFrontLeft");
+ wheel_fl.center();
+ box = (BoundingBox) wheel_fl.getModelBound();
+ player.addWheel(wheel_fl.getParent(), box.getCenter().add(0, -front_wheel_h, 0),
+ wheelDirection, wheelAxle, 0.2f, wheelRadius, true);
+
+ Geometry wheel_br = findGeom(carNode, "WheelBackRight");
+ wheel_br.center();
+ box = (BoundingBox) wheel_br.getModelBound();
+ player.addWheel(wheel_br.getParent(), box.getCenter().add(0, -back_wheel_h, 0),
+ wheelDirection, wheelAxle, 0.2f, wheelRadius, false);
+
+ Geometry wheel_bl = findGeom(carNode, "WheelBackLeft");
+ wheel_bl.center();
+ box = (BoundingBox) wheel_bl.getModelBound();
+ player.addWheel(wheel_bl.getParent(), box.getCenter().add(0, -back_wheel_h, 0),
+ wheelDirection, wheelAxle, 0.2f, wheelRadius, false);
+
+ player.getWheel(2).setFrictionSlip(4);
+ player.getWheel(3).setFrictionSlip(4);
+
+ rootNode.attachChild(carNode);
+ getPhysicsSpace().add(player);
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Lefts")) {
+ if (value) {
+ steeringValue += .5f;
+ } else {
+ steeringValue += -.5f;
+ }
+ player.steer(steeringValue);
+ } else if (binding.equals("Rights")) {
+ if (value) {
+ steeringValue += -.5f;
+ } else {
+ steeringValue += .5f;
+ }
+ player.steer(steeringValue);
+ } //note that our fancy car actually goes backwards..
+ else if (binding.equals("Ups")) {
+ if (value) {
+ accelerationValue -= 800;
+ } else {
+ accelerationValue += 800;
+ }
+ player.accelerate(accelerationValue);
+ player.setCollisionShape(CollisionShapeFactory.createDynamicMeshShape(findGeom(carNode, "Car")));
+ } else if (binding.equals("Downs")) {
+ if (value) {
+ player.brake(40f);
+ } else {
+ player.brake(0f);
+ }
+ } else if (binding.equals("Reset")) {
+ if (value) {
+ System.out.println("Reset");
+ player.setPhysicsLocation(Vector3f.ZERO);
+ player.setPhysicsRotation(new Matrix3f());
+ player.setLinearVelocity(Vector3f.ZERO);
+ player.setAngularVelocity(Vector3f.ZERO);
+ player.resetSuspension();
+ } else {
+ }
+ }
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ cam.lookAt(carNode.getWorldTranslation(), Vector3f.UNIT_Y);
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestGhostObject.java b/engine/src/test/jme3test/bullet/TestGhostObject.java
new file mode 100644
index 0000000..9539379
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestGhostObject.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.GhostControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+
+/**
+ *
+ * @author tim8dev [at] gmail [dot com]
+ */
+public class TestGhostObject extends SimpleApplication {
+
+ private BulletAppState bulletAppState;
+ private GhostControl ghostControl;
+
+ public static void main(String[] args) {
+ Application app = new TestGhostObject();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+
+ // Mesh to be shared across several boxes.
+ Box boxGeom = new Box(Vector3f.ZERO, 1f, 1f, 1f);
+ // CollisionShape to be shared across several boxes.
+ CollisionShape shape = new BoxCollisionShape(new Vector3f(1, 1, 1));
+
+ Node physicsBox = PhysicsTestHelper.createPhysicsTestNode(assetManager, shape, 1);
+ physicsBox.setName("box0");
+ physicsBox.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(.6f, 4, .5f));
+ rootNode.attachChild(physicsBox);
+ getPhysicsSpace().add(physicsBox);
+
+ Node physicsBox1 = PhysicsTestHelper.createPhysicsTestNode(assetManager, shape, 1);
+ physicsBox1.setName("box1");
+ physicsBox1.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0, 40, 0));
+ rootNode.attachChild(physicsBox1);
+ getPhysicsSpace().add(physicsBox1);
+
+ Node physicsBox2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
+ physicsBox2.setName("box0");
+ physicsBox2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(.5f, 80, -.8f));
+ rootNode.attachChild(physicsBox2);
+ getPhysicsSpace().add(physicsBox2);
+
+ // the floor, does not move (mass=0)
+ Node node = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(100, 1, 100)), 0);
+ node.setName("floor");
+ node.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -6, 0f));
+ rootNode.attachChild(node);
+ getPhysicsSpace().add(node);
+
+ initGhostObject();
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void initGhostObject() {
+ Vector3f halfExtents = new Vector3f(3, 4.2f, 1);
+ ghostControl = new GhostControl(new BoxCollisionShape(halfExtents));
+ Node node=new Node("Ghost Object");
+ node.addControl(ghostControl);
+ rootNode.attachChild(node);
+ getPhysicsSpace().add(ghostControl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ fpsText.setText("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestHoveringTank.java b/engine/src/test/jme3test/bullet/TestHoveringTank.java
new file mode 100644
index 0000000..7e5fd4a
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestHoveringTank.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.font.BitmapText;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.shadow.PssmShadowRenderer;
+import com.jme3.shadow.PssmShadowRenderer.CompareMode;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestHoveringTank extends SimpleApplication implements AnalogListener,
+ ActionListener {
+
+ private BulletAppState bulletAppState;
+ private PhysicsHoverControl hoverControl;
+ private Spatial spaceCraft;
+ TerrainQuad terrain;
+ Material matRock;
+ boolean wireframe = false;
+ protected BitmapText hintText;
+ PointLight pl;
+ Geometry lightMdl;
+ Geometry collisionMarker;
+
+ public static void main(String[] args) {
+ TestHoveringTank app = new TestHoveringTank();
+ app.start();
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "Lefts");
+ inputManager.addListener(this, "Rights");
+ inputManager.addListener(this, "Ups");
+ inputManager.addListener(this, "Downs");
+ inputManager.addListener(this, "Space");
+ inputManager.addListener(this, "Reset");
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+ stateManager.attach(bulletAppState);
+// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ bulletAppState.getPhysicsSpace().setAccuracy(1f/30f);
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+ PssmShadowRenderer pssmr = new PssmShadowRenderer(assetManager, 2048, 3);
+ pssmr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal());
+ pssmr.setLambda(0.55f);
+ pssmr.setShadowIntensity(0.6f);
+ pssmr.setCompareMode(CompareMode.Hardware);
+ pssmr.setFilterMode(FilterMode.Bilinear);
+ viewPort.addProcessor(pssmr);
+
+ setupKeys();
+ createTerrain();
+ buildPlayer();
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(new ColorRGBA(1.0f, 0.94f, 0.8f, 1f).multLocal(1.3f));
+ dl.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal());
+ rootNode.addLight(dl);
+
+ Vector3f lightDir2 = new Vector3f(0.70518064f, 0.5902297f, -0.39287305f);
+ DirectionalLight dl2 = new DirectionalLight();
+ dl2.setColor(new ColorRGBA(0.7f, 0.85f, 1.0f, 1f));
+ dl2.setDirection(lightDir2);
+ rootNode.addLight(dl2);
+ }
+
+ private void buildPlayer() {
+ spaceCraft = assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml");
+ CollisionShape colShape = CollisionShapeFactory.createDynamicMeshShape(spaceCraft);
+ spaceCraft.setShadowMode(ShadowMode.CastAndReceive);
+ spaceCraft.setLocalTranslation(new Vector3f(-140, 14, -23));
+ spaceCraft.setLocalRotation(new Quaternion(new float[]{0, 0.01f, 0}));
+
+ hoverControl = new PhysicsHoverControl(colShape, 500);
+ hoverControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
+
+ spaceCraft.addControl(hoverControl);
+
+
+ rootNode.attachChild(spaceCraft);
+ getPhysicsSpace().add(hoverControl);
+
+ ChaseCamera chaseCam = new ChaseCamera(cam, inputManager);
+ spaceCraft.addControl(chaseCam);
+
+ flyCam.setEnabled(false);
+ }
+
+ public void makeMissile() {
+ Vector3f pos = spaceCraft.getWorldTranslation().clone();
+ Quaternion rot = spaceCraft.getWorldRotation();
+ Vector3f dir = rot.getRotationColumn(2);
+
+ Spatial missile = assetManager.loadModel("Models/SpaceCraft/Rocket.mesh.xml");
+ missile.scale(0.5f);
+ missile.rotate(0, FastMath.PI, 0);
+ missile.updateGeometricState();
+
+ BoundingBox box = (BoundingBox) missile.getWorldBound();
+ final Vector3f extent = box.getExtent(null);
+
+ BoxCollisionShape boxShape = new BoxCollisionShape(extent);
+
+ missile.setName("Missile");
+ missile.rotate(rot);
+ missile.setLocalTranslation(pos.addLocal(0, extent.y * 4.5f, 0));
+ missile.setLocalRotation(hoverControl.getPhysicsRotation());
+ missile.setShadowMode(ShadowMode.Cast);
+ RigidBodyControl control = new BombControl(assetManager, boxShape, 20);
+ control.setLinearVelocity(dir.mult(100));
+ control.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_03);
+ missile.addControl(control);
+
+
+ rootNode.attachChild(missile);
+ getPhysicsSpace().add(missile);
+ }
+
+ public void onAnalog(String binding, float value, float tpf) {
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Lefts")) {
+ hoverControl.steer(value ? 50f : 0);
+ } else if (binding.equals("Rights")) {
+ hoverControl.steer(value ? -50f : 0);
+ } else if (binding.equals("Ups")) {
+ hoverControl.accelerate(value ? 100f : 0);
+ } else if (binding.equals("Downs")) {
+ hoverControl.accelerate(value ? -100f : 0);
+ } else if (binding.equals("Reset")) {
+ if (value) {
+ System.out.println("Reset");
+ hoverControl.setPhysicsLocation(new Vector3f(-140, 14, -23));
+ hoverControl.setPhysicsRotation(new Matrix3f());
+ hoverControl.clearForces();
+ } else {
+ }
+ } else if (binding.equals("Space") && value) {
+ makeMissile();
+ }
+ }
+
+ public void updateCamera() {
+ rootNode.updateGeometricState();
+
+ Vector3f pos = spaceCraft.getWorldTranslation().clone();
+ Quaternion rot = spaceCraft.getWorldRotation();
+ Vector3f dir = rot.getRotationColumn(2);
+
+ // make it XZ only
+ Vector3f camPos = new Vector3f(dir);
+ camPos.setY(0);
+ camPos.normalizeLocal();
+
+ // negate and multiply by distance from object
+ camPos.negateLocal();
+ camPos.multLocal(15);
+
+ // add Y distance
+ camPos.setY(2);
+ camPos.addLocal(pos);
+ cam.setLocation(camPos);
+
+ Vector3f lookAt = new Vector3f(dir);
+ lookAt.multLocal(7); // look at dist
+ lookAt.addLocal(pos);
+ cam.lookAt(lookAt, Vector3f.UNIT_Y);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ }
+
+ private void createTerrain() {
+ matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matRock.setBoolean("useTriPlanarMapping", false);
+ matRock.setBoolean("WardIso", true);
+ matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap", grass);
+ matRock.setFloat("DiffuseMap_0_scale", 64);
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_1", dirt);
+ matRock.setFloat("DiffuseMap_1_scale", 16);
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_2", rock);
+ matRock.setFloat("DiffuseMap_2_scale", 128);
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matRock.setTexture("NormalMap", normalMap0);
+ matRock.setTexture("NormalMap_1", normalMap2);
+ matRock.setTexture("NormalMap_2", normalMap2);
+
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
+ heightmap.load();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ List<Camera> cameras = new ArrayList<Camera>();
+ cameras.add(getCamera());
+ TerrainLodControl control = new TerrainLodControl(terrain, cameras);
+ terrain.addControl(control);
+ terrain.setMaterial(matRock);
+ terrain.setLocalScale(new Vector3f(2, 2, 2));
+ terrain.setLocked(false); // unlock it so we can edit the height
+
+ terrain.setShadowMode(ShadowMode.CastAndReceive);
+ terrain.addControl(new RigidBodyControl(0));
+ rootNode.attachChild(terrain);
+ getPhysicsSpace().addAll(terrain);
+
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestKinematicAddToPhysicsSpaceIssue.java b/engine/src/test/jme3test/bullet/TestKinematicAddToPhysicsSpaceIssue.java
new file mode 100644
index 0000000..8947d8a
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestKinematicAddToPhysicsSpaceIssue.java
@@ -0,0 +1,80 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TestKinematicAddToPhysicsSpaceIssue extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestKinematicAddToPhysicsSpaceIssue app = new TestKinematicAddToPhysicsSpaceIssue();
+ app.start();
+ }
+ BulletAppState bulletAppState;
+
+ @Override
+ public void simpleInitApp() {
+
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ // Add a physics sphere to the world
+ Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
+ rootNode.attachChild(physicsSphere);
+
+ //Setting the rigidBody to kinematic before adding it to the physic space
+ physicsSphere.getControl(RigidBodyControl.class).setKinematic(true);
+ //adding it to the physic space
+ getPhysicsSpace().add(physicsSphere);
+ //Making it not kinematic again, it should fall under gravity, it doesn't
+ physicsSphere.getControl(RigidBodyControl.class).setKinematic(false);
+
+ // Add a physics sphere to the world using the collision shape from sphere one
+ Node physicsSphere2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(5, 6, 0));
+ rootNode.attachChild(physicsSphere2);
+
+ //Adding the rigid body to physic space
+ getPhysicsSpace().add(physicsSphere2);
+ //making it kinematic
+ physicsSphere2.getControl(RigidBodyControl.class).setKinematic(false);
+ //Making it not kinematic again, it works properly, the rigidbody is affected by grvity.
+ physicsSphere2.getControl(RigidBodyControl.class).setKinematic(false);
+
+
+
+ // an obstacle mesh, does not move (mass=0)
+ Node node2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new MeshCollisionShape(new Sphere(16, 16, 1.2f)), 0);
+ node2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2.5f, -4, 0f));
+ rootNode.attachChild(node2);
+ getPhysicsSpace().add(node2);
+
+ // the floor mesh, does not move (mass=0)
+ Node node3 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new PlaneCollisionShape(new Plane(new Vector3f(0, 1, 0), 0)), 0);
+ node3.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -6, 0f));
+ rootNode.attachChild(node3);
+ getPhysicsSpace().add(node3);
+
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestLocalPhysics.java b/engine/src/test/jme3test/bullet/TestLocalPhysics.java
new file mode 100644
index 0000000..cba8f86
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestLocalPhysics.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.*;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+
+/**
+ * This is a basic Test of jbullet-jme functions
+ *
+ * @author normenhansen
+ */
+public class TestLocalPhysics extends SimpleApplication {
+
+ private BulletAppState bulletAppState;
+
+ public static void main(String[] args) {
+ TestLocalPhysics app = new TestLocalPhysics();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+
+ // Add a physics sphere to the world
+ Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
+ physicsSphere.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);
+ rootNode.attachChild(physicsSphere);
+ getPhysicsSpace().add(physicsSphere);
+
+ // Add a physics sphere to the world using the collision shape from sphere one
+ Node physicsSphere2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, physicsSphere.getControl(RigidBodyControl.class).getCollisionShape(), 1);
+ physicsSphere2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(4, 8, 0));
+ physicsSphere2.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);
+ rootNode.attachChild(physicsSphere2);
+ getPhysicsSpace().add(physicsSphere2);
+
+ // Add a physics box to the world
+ Node physicsBox = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
+ physicsBox.getControl(RigidBodyControl.class).setFriction(0.1f);
+ physicsBox.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(.6f, 4, .5f));
+ physicsBox.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);
+ rootNode.attachChild(physicsBox);
+ getPhysicsSpace().add(physicsBox);
+
+ // Add a physics cylinder to the world
+ Node physicsCylinder = PhysicsTestHelper.createPhysicsTestNode(assetManager, new CylinderCollisionShape(new Vector3f(1f, 1f, 1.5f)), 1);
+ physicsCylinder.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2, 2, 0));
+ physicsCylinder.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);
+ rootNode.attachChild(physicsCylinder);
+ getPhysicsSpace().add(physicsCylinder);
+
+ // an obstacle mesh, does not move (mass=0)
+ Node node2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new MeshCollisionShape(new Sphere(16, 16, 1.2f)), 0);
+ node2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2.5f, -4, 0f));
+ node2.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);
+ rootNode.attachChild(node2);
+ getPhysicsSpace().add(node2);
+
+ // the floor mesh, does not move (mass=0)
+ Node node3 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new PlaneCollisionShape(new Plane(new Vector3f(0, 1, 0), 0)), 0);
+ node3.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -6, 0f));
+ node3.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);
+ rootNode.attachChild(node3);
+ getPhysicsSpace().add(node3);
+
+ // Join the physics objects with a Point2Point joint
+// PhysicsPoint2PointJoint joint=new PhysicsPoint2PointJoint(physicsSphere, physicsBox, new Vector3f(-2,0,0), new Vector3f(2,0,0));
+// PhysicsHingeJoint joint=new PhysicsHingeJoint(physicsSphere, physicsBox, new Vector3f(-2,0,0), new Vector3f(2,0,0), Vector3f.UNIT_Z,Vector3f.UNIT_Z);
+// getPhysicsSpace().add(joint);
+
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ rootNode.rotate(tpf, 0, 0);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestPhysicsCar.java b/engine/src/test/jme3test/bullet/TestPhysicsCar.java
new file mode 100644
index 0000000..11847ba
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestPhysicsCar.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
+import com.jme3.bullet.control.VehicleControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Cylinder;
+
+public class TestPhysicsCar extends SimpleApplication implements ActionListener {
+
+ private BulletAppState bulletAppState;
+ private VehicleControl vehicle;
+ private final float accelerationForce = 1000.0f;
+ private final float brakeForce = 100.0f;
+ private float steeringValue = 0;
+ private float accelerationValue = 0;
+ private Vector3f jumpForce = new Vector3f(0, 3000, 0);
+
+ public static void main(String[] args) {
+ TestPhysicsCar app = new TestPhysicsCar();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ setupKeys();
+ buildPlayer();
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "Lefts");
+ inputManager.addListener(this, "Rights");
+ inputManager.addListener(this, "Ups");
+ inputManager.addListener(this, "Downs");
+ inputManager.addListener(this, "Space");
+ inputManager.addListener(this, "Reset");
+ }
+
+ private void buildPlayer() {
+ Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Red);
+
+ //create a compound shape and attach the BoxCollisionShape for the car body at 0,1,0
+ //this shifts the effective center of mass of the BoxCollisionShape to 0,-1,0
+ CompoundCollisionShape compoundShape = new CompoundCollisionShape();
+ BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1.2f, 0.5f, 2.4f));
+ compoundShape.addChildShape(box, new Vector3f(0, 1, 0));
+
+ //create vehicle node
+ Node vehicleNode=new Node("vehicleNode");
+ vehicle = new VehicleControl(compoundShape, 400);
+ vehicleNode.addControl(vehicle);
+
+ //setting suspension values for wheels, this can be a bit tricky
+ //see also https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&hl=en
+ float stiffness = 60.0f;//200=f1 car
+ float compValue = .3f; //(should be lower than damp)
+ float dampValue = .4f;
+ vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
+ vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
+ vehicle.setSuspensionStiffness(stiffness);
+ vehicle.setMaxSuspensionForce(10000.0f);
+
+ //Create four wheels and add them at their locations
+ Vector3f wheelDirection = new Vector3f(0, -1, 0); // was 0, -1, 0
+ Vector3f wheelAxle = new Vector3f(-1, 0, 0); // was -1, 0, 0
+ float radius = 0.5f;
+ float restLength = 0.3f;
+ float yOff = 0.5f;
+ float xOff = 1f;
+ float zOff = 2f;
+
+ Cylinder wheelMesh = new Cylinder(16, 16, radius, radius * 0.6f, true);
+
+ Node node1 = new Node("wheel 1 node");
+ Geometry wheels1 = new Geometry("wheel 1", wheelMesh);
+ node1.attachChild(wheels1);
+ wheels1.rotate(0, FastMath.HALF_PI, 0);
+ wheels1.setMaterial(mat);
+ vehicle.addWheel(node1, new Vector3f(-xOff, yOff, zOff),
+ wheelDirection, wheelAxle, restLength, radius, true);
+
+ Node node2 = new Node("wheel 2 node");
+ Geometry wheels2 = new Geometry("wheel 2", wheelMesh);
+ node2.attachChild(wheels2);
+ wheels2.rotate(0, FastMath.HALF_PI, 0);
+ wheels2.setMaterial(mat);
+ vehicle.addWheel(node2, new Vector3f(xOff, yOff, zOff),
+ wheelDirection, wheelAxle, restLength, radius, true);
+
+ Node node3 = new Node("wheel 3 node");
+ Geometry wheels3 = new Geometry("wheel 3", wheelMesh);
+ node3.attachChild(wheels3);
+ wheels3.rotate(0, FastMath.HALF_PI, 0);
+ wheels3.setMaterial(mat);
+ vehicle.addWheel(node3, new Vector3f(-xOff, yOff, -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ Node node4 = new Node("wheel 4 node");
+ Geometry wheels4 = new Geometry("wheel 4", wheelMesh);
+ node4.attachChild(wheels4);
+ wheels4.rotate(0, FastMath.HALF_PI, 0);
+ wheels4.setMaterial(mat);
+ vehicle.addWheel(node4, new Vector3f(xOff, yOff, -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ vehicleNode.attachChild(node1);
+ vehicleNode.attachChild(node2);
+ vehicleNode.attachChild(node3);
+ vehicleNode.attachChild(node4);
+ rootNode.attachChild(vehicleNode);
+
+ getPhysicsSpace().add(vehicle);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ cam.lookAt(vehicle.getPhysicsLocation(), Vector3f.UNIT_Y);
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Lefts")) {
+ if (value) {
+ steeringValue += .5f;
+ } else {
+ steeringValue += -.5f;
+ }
+ vehicle.steer(steeringValue);
+ } else if (binding.equals("Rights")) {
+ if (value) {
+ steeringValue += -.5f;
+ } else {
+ steeringValue += .5f;
+ }
+ vehicle.steer(steeringValue);
+ } else if (binding.equals("Ups")) {
+ if (value) {
+ accelerationValue += accelerationForce;
+ } else {
+ accelerationValue -= accelerationForce;
+ }
+ vehicle.accelerate(accelerationValue);
+ } else if (binding.equals("Downs")) {
+ if (value) {
+ vehicle.brake(brakeForce);
+ } else {
+ vehicle.brake(0f);
+ }
+ } else if (binding.equals("Space")) {
+ if (value) {
+ vehicle.applyImpulse(jumpForce, Vector3f.ZERO);
+ }
+ } else if (binding.equals("Reset")) {
+ if (value) {
+ System.out.println("Reset");
+ vehicle.setPhysicsLocation(Vector3f.ZERO);
+ vehicle.setPhysicsRotation(new Matrix3f());
+ vehicle.setLinearVelocity(Vector3f.ZERO);
+ vehicle.setAngularVelocity(Vector3f.ZERO);
+ vehicle.resetSuspension();
+ } else {
+ }
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestPhysicsCharacter.java b/engine/src/test/jme3test/bullet/TestPhysicsCharacter.java
new file mode 100644
index 0000000..c58d112
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestPhysicsCharacter.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved. <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer. <p/> * Redistributions
+ * in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. <p/> * Neither the name of
+ * 'jMonkeyEngine' nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written
+ * permission. <p/> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+ * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.CameraNode;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.CameraControl.ControlDirection;
+
+/**
+ * A walking physical character followed by a 3rd person camera. (No animation.)
+ * @author normenhansen, zathras
+ */
+public class TestPhysicsCharacter extends SimpleApplication implements ActionListener {
+
+ private BulletAppState bulletAppState;
+ private CharacterControl physicsCharacter;
+ private Node characterNode;
+ private CameraNode camNode;
+ boolean rotate = false;
+ private Vector3f walkDirection = new Vector3f(0,0,0);
+ private Vector3f viewDirection = new Vector3f(0,0,0);
+ boolean leftStrafe = false, rightStrafe = false, forward = false, backward = false,
+ leftRotate = false, rightRotate = false;
+
+ public static void main(String[] args) {
+ TestPhysicsCharacter app = new TestPhysicsCharacter();
+ app.start();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Strafe Left",
+ new KeyTrigger(KeyInput.KEY_Q),
+ new KeyTrigger(KeyInput.KEY_Z));
+ inputManager.addMapping("Strafe Right",
+ new KeyTrigger(KeyInput.KEY_E),
+ new KeyTrigger(KeyInput.KEY_X));
+ inputManager.addMapping("Rotate Left",
+ new KeyTrigger(KeyInput.KEY_A),
+ new KeyTrigger(KeyInput.KEY_LEFT));
+ inputManager.addMapping("Rotate Right",
+ new KeyTrigger(KeyInput.KEY_D),
+ new KeyTrigger(KeyInput.KEY_RIGHT));
+ inputManager.addMapping("Walk Forward",
+ new KeyTrigger(KeyInput.KEY_W),
+ new KeyTrigger(KeyInput.KEY_UP));
+ inputManager.addMapping("Walk Backward",
+ new KeyTrigger(KeyInput.KEY_S),
+ new KeyTrigger(KeyInput.KEY_DOWN));
+ inputManager.addMapping("Jump",
+ new KeyTrigger(KeyInput.KEY_SPACE),
+ new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addMapping("Shoot",
+ new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(this, "Strafe Left", "Strafe Right");
+ inputManager.addListener(this, "Rotate Left", "Rotate Right");
+ inputManager.addListener(this, "Walk Forward", "Walk Backward");
+ inputManager.addListener(this, "Jump", "Shoot");
+ }
+ @Override
+ public void simpleInitApp() {
+ // activate physics
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+
+ // init a physical test scene
+ PhysicsTestHelper.createPhysicsTestWorldSoccer(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ setupKeys();
+
+ // Add a physics character to the world
+ physicsCharacter = new CharacterControl(new CapsuleCollisionShape(0.5f, 1.8f), .1f);
+ physicsCharacter.setPhysicsLocation(new Vector3f(0, 1, 0));
+ characterNode = new Node("character node");
+ Spatial model = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
+ model.scale(0.25f);
+ characterNode.addControl(physicsCharacter);
+ getPhysicsSpace().add(physicsCharacter);
+ rootNode.attachChild(characterNode);
+ characterNode.attachChild(model);
+
+ // set forward camera node that follows the character
+ camNode = new CameraNode("CamNode", cam);
+ camNode.setControlDir(ControlDirection.SpatialToCamera);
+ camNode.setLocalTranslation(new Vector3f(0, 1, -5));
+ camNode.lookAt(model.getLocalTranslation(), Vector3f.UNIT_Y);
+ characterNode.attachChild(camNode);
+
+ //disable the default 1st-person flyCam (don't forget this!!)
+ flyCam.setEnabled(false);
+
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ Vector3f camDir = cam.getDirection().mult(0.2f);
+ Vector3f camLeft = cam.getLeft().mult(0.2f);
+ camDir.y = 0;
+ camLeft.y = 0;
+ viewDirection.set(camDir);
+ walkDirection.set(0, 0, 0);
+ if (leftStrafe) {
+ walkDirection.addLocal(camLeft);
+ } else
+ if (rightStrafe) {
+ walkDirection.addLocal(camLeft.negate());
+ }
+ if (leftRotate) {
+ viewDirection.addLocal(camLeft.mult(0.02f));
+ } else
+ if (rightRotate) {
+ viewDirection.addLocal(camLeft.mult(0.02f).negate());
+ }
+ if (forward) {
+ walkDirection.addLocal(camDir);
+ } else
+ if (backward) {
+ walkDirection.addLocal(camDir.negate());
+ }
+ physicsCharacter.setWalkDirection(walkDirection);
+ physicsCharacter.setViewDirection(viewDirection);
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Strafe Left")) {
+ if (value) {
+ leftStrafe = true;
+ } else {
+ leftStrafe = false;
+ }
+ } else if (binding.equals("Strafe Right")) {
+ if (value) {
+ rightStrafe = true;
+ } else {
+ rightStrafe = false;
+ }
+ } else if (binding.equals("Rotate Left")) {
+ if (value) {
+ leftRotate = true;
+ } else {
+ leftRotate = false;
+ }
+ } else if (binding.equals("Rotate Right")) {
+ if (value) {
+ rightRotate = true;
+ } else {
+ rightRotate = false;
+ }
+ } else if (binding.equals("Walk Forward")) {
+ if (value) {
+ forward = true;
+ } else {
+ forward = false;
+ }
+ } else if (binding.equals("Walk Backward")) {
+ if (value) {
+ backward = true;
+ } else {
+ backward = false;
+ }
+ } else if (binding.equals("Jump")) {
+ physicsCharacter.jump();
+ }
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ @Override
+ public void simpleRender(RenderManager rm) {
+ //TODO: add render code
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestPhysicsHingeJoint.java b/engine/src/test/jme3test/bullet/TestPhysicsHingeJoint.java
new file mode 100644
index 0000000..95b4b30
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestPhysicsHingeJoint.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.joints.HingeJoint;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+
+public class TestPhysicsHingeJoint extends SimpleApplication implements AnalogListener {
+ private BulletAppState bulletAppState;
+ private HingeJoint joint;
+
+ public static void main(String[] args) {
+ TestPhysicsHingeJoint app = new TestPhysicsHingeJoint();
+ app.start();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Swing", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this, "Left", "Right", "Swing");
+ }
+
+ public void onAnalog(String binding, float value, float tpf) {
+ if(binding.equals("Left")){
+ joint.enableMotor(true, 1, .1f);
+ }
+ else if(binding.equals("Right")){
+ joint.enableMotor(true, -1, .1f);
+ }
+ else if(binding.equals("Swing")){
+ joint.enableMotor(false, 0, 0);
+ }
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ setupKeys();
+ setupJoint();
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ public void setupJoint() {
+ Node holderNode=PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f( .1f, .1f, .1f)),0);
+ holderNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f,0,0f));
+ rootNode.attachChild(holderNode);
+ getPhysicsSpace().add(holderNode);
+
+ Node hammerNode=PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f( .3f, .3f, .3f)),1);
+ hammerNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f,-1,0f));
+ rootNode.attachChild(hammerNode);
+ getPhysicsSpace().add(hammerNode);
+
+ joint=new HingeJoint(holderNode.getControl(RigidBodyControl.class), hammerNode.getControl(RigidBodyControl.class), Vector3f.ZERO, new Vector3f(0f,-1,0f), Vector3f.UNIT_Z, Vector3f.UNIT_Z);
+ getPhysicsSpace().add(joint);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+
+ }
+
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/bullet/TestPhysicsReadWrite.java b/engine/src/test/jme3test/bullet/TestPhysicsReadWrite.java
new file mode 100644
index 0000000..b31d79a
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestPhysicsReadWrite.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.*;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.joints.HingeJoint;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This is a basic Test of jbullet-jme functions
+ *
+ * @author normenhansen
+ */
+public class TestPhysicsReadWrite extends SimpleApplication{
+ private BulletAppState bulletAppState;
+ private Node physicsRootNode;
+ public static void main(String[] args){
+ TestPhysicsReadWrite app = new TestPhysicsReadWrite();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ physicsRootNode=new Node("PhysicsRootNode");
+ rootNode.attachChild(physicsRootNode);
+
+ // Add a physics sphere to the world
+ Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
+ rootNode.attachChild(physicsSphere);
+ getPhysicsSpace().add(physicsSphere);
+
+ // Add a physics sphere to the world using the collision shape from sphere one
+ Node physicsSphere2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, physicsSphere.getControl(RigidBodyControl.class).getCollisionShape(), 1);
+ physicsSphere2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(4, 8, 0));
+ rootNode.attachChild(physicsSphere2);
+ getPhysicsSpace().add(physicsSphere2);
+
+ // Add a physics box to the world
+ Node physicsBox = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
+ physicsBox.getControl(RigidBodyControl.class).setFriction(0.1f);
+ physicsBox.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(.6f, 4, .5f));
+ rootNode.attachChild(physicsBox);
+ getPhysicsSpace().add(physicsBox);
+
+ // Add a physics cylinder to the world
+ Node physicsCylinder = PhysicsTestHelper.createPhysicsTestNode(assetManager, new CylinderCollisionShape(new Vector3f(1f, 1f, 1.5f)), 1);
+ physicsCylinder.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2, 2, 0));
+ rootNode.attachChild(physicsCylinder);
+ getPhysicsSpace().add(physicsCylinder);
+
+ // an obstacle mesh, does not move (mass=0)
+ Node node2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new MeshCollisionShape(new Sphere(16, 16, 1.2f)), 0);
+ node2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2.5f, -4, 0f));
+ rootNode.attachChild(node2);
+ getPhysicsSpace().add(node2);
+
+ // the floor mesh, does not move (mass=0)
+ Node node3 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new PlaneCollisionShape(new Plane(new Vector3f(0, 1, 0), 0)), 0);
+ node3.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -6, 0f));
+ rootNode.attachChild(node3);
+ getPhysicsSpace().add(node3);
+
+ // Join the physics objects with a Point2Point joint
+ HingeJoint joint=new HingeJoint(physicsSphere.getControl(RigidBodyControl.class), physicsBox.getControl(RigidBodyControl.class), new Vector3f(-2,0,0), new Vector3f(2,0,0), Vector3f.UNIT_Z,Vector3f.UNIT_Z);
+ getPhysicsSpace().add(joint);
+
+ //save and load the physicsRootNode
+ try {
+ //remove all physics objects from physics space
+ getPhysicsSpace().removeAll(physicsRootNode);
+ physicsRootNode.removeFromParent();
+ //export to byte array
+ ByteArrayOutputStream bout=new ByteArrayOutputStream();
+ BinaryExporter.getInstance().save(physicsRootNode, bout);
+ //import from byte array
+ ByteArrayInputStream bin=new ByteArrayInputStream(bout.toByteArray());
+ BinaryImporter imp=BinaryImporter.getInstance();
+ imp.setAssetManager(assetManager);
+ Node newPhysicsRootNode=(Node)imp.load(bin);
+ //add all physics objects to physics space
+ getPhysicsSpace().addAll(newPhysicsRootNode);
+ rootNode.attachChild(newPhysicsRootNode);
+ } catch (IOException ex) {
+ Logger.getLogger(TestPhysicsReadWrite.class.getName()).log(Level.SEVERE, null, ex);
+ }
+
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ //TODO: add update code
+ }
+
+ @Override
+ public void simpleRender(RenderManager rm) {
+ //TODO: add render code
+ }
+
+}
diff --git a/engine/src/test/jme3test/bullet/TestQ3.java b/engine/src/test/jme3test/bullet/TestQ3.java
new file mode 100644
index 0000000..1d18c4b
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestQ3.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.objects.PhysicsCharacter;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.MaterialList;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.plugins.ogre.OgreMeshKey;
+import java.io.File;
+
+public class TestQ3 extends SimpleApplication implements ActionListener {
+
+ private BulletAppState bulletAppState;
+ private Node gameLevel;
+ private PhysicsCharacter player;
+ private Vector3f walkDirection = new Vector3f();
+ private static boolean useHttp = false;
+ private boolean left=false,right=false,up=false,down=false;
+
+ public static void main(String[] args) {
+ File file = new File("quake3level.zip");
+ if (!file.exists()) {
+ useHttp = true;
+ }
+ TestQ3 app = new TestQ3();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ flyCam.setMoveSpeed(100);
+ setupKeys();
+
+ this.cam.setFrustumFar(2000);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White.clone().multLocal(2));
+ dl.setDirection(new Vector3f(-1, -1, -1).normalize());
+ rootNode.addLight(dl);
+
+ AmbientLight am = new AmbientLight();
+ am.setColor(ColorRGBA.White.mult(2));
+ rootNode.addLight(am);
+
+ // load the level from zip or http zip
+ if (useHttp) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/quake3level.zip", HttpZipLocator.class.getName());
+ } else {
+ assetManager.registerLocator("quake3level.zip", ZipLocator.class.getName());
+ }
+
+ // create the geometry and attach it
+ MaterialList matList = (MaterialList) assetManager.loadAsset("Scene.material");
+ OgreMeshKey key = new OgreMeshKey("main.meshxml", matList);
+ gameLevel = (Node) assetManager.loadAsset(key);
+ gameLevel.setLocalScale(0.1f);
+
+ // add a physics control, it will generate a MeshCollisionShape based on the gameLevel
+ gameLevel.addControl(new RigidBodyControl(0));
+
+ player = new PhysicsCharacter(new SphereCollisionShape(5), .01f);
+ player.setJumpSpeed(20);
+ player.setFallSpeed(30);
+ player.setGravity(30);
+
+ player.setPhysicsLocation(new Vector3f(60, 10, -60));
+
+ rootNode.attachChild(gameLevel);
+
+ getPhysicsSpace().addAll(gameLevel);
+ getPhysicsSpace().add(player);
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
+ walkDirection.set(0,0,0);
+ if(left)
+ walkDirection.addLocal(camLeft);
+ if(right)
+ walkDirection.addLocal(camLeft.negate());
+ if(up)
+ walkDirection.addLocal(camDir);
+ if(down)
+ walkDirection.addLocal(camDir.negate());
+ player.setWalkDirection(walkDirection);
+ cam.setLocation(player.getPhysicsLocation());
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this,"Lefts");
+ inputManager.addListener(this,"Rights");
+ inputManager.addListener(this,"Ups");
+ inputManager.addListener(this,"Downs");
+ inputManager.addListener(this,"Space");
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+
+ if (binding.equals("Lefts")) {
+ if(value)
+ left=true;
+ else
+ left=false;
+ } else if (binding.equals("Rights")) {
+ if(value)
+ right=true;
+ else
+ right=false;
+ } else if (binding.equals("Ups")) {
+ if(value)
+ up=true;
+ else
+ up=false;
+ } else if (binding.equals("Downs")) {
+ if(value)
+ down=true;
+ else
+ down=false;
+ } else if (binding.equals("Space")) {
+ player.jump();
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestRagDoll.java b/engine/src/test/jme3test/bullet/TestRagDoll.java
new file mode 100644
index 0000000..9287f10
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestRagDoll.java
@@ -0,0 +1,124 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.joints.ConeJoint;
+import com.jme3.bullet.joints.PhysicsJoint;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TestRagDoll extends SimpleApplication implements ActionListener {
+
+ private BulletAppState bulletAppState = new BulletAppState();
+ private Node ragDoll = new Node();
+ private Node shoulders;
+ private Vector3f upforce = new Vector3f(0, 200, 0);
+ private boolean applyForce = false;
+
+ public static void main(String[] args) {
+ TestRagDoll app = new TestRagDoll();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ inputManager.addMapping("Pull ragdoll up", new MouseButtonTrigger(0));
+ inputManager.addListener(this, "Pull ragdoll up");
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ createRagDoll();
+ }
+
+ private void createRagDoll() {
+ shoulders = createLimb(0.2f, 1.0f, new Vector3f(0.00f, 1.5f, 0), true);
+ Node uArmL = createLimb(0.2f, 0.5f, new Vector3f(-0.75f, 0.8f, 0), false);
+ Node uArmR = createLimb(0.2f, 0.5f, new Vector3f(0.75f, 0.8f, 0), false);
+ Node lArmL = createLimb(0.2f, 0.5f, new Vector3f(-0.75f, -0.2f, 0), false);
+ Node lArmR = createLimb(0.2f, 0.5f, new Vector3f(0.75f, -0.2f, 0), false);
+ Node body = createLimb(0.2f, 1.0f, new Vector3f(0.00f, 0.5f, 0), false);
+ Node hips = createLimb(0.2f, 0.5f, new Vector3f(0.00f, -0.5f, 0), true);
+ Node uLegL = createLimb(0.2f, 0.5f, new Vector3f(-0.25f, -1.2f, 0), false);
+ Node uLegR = createLimb(0.2f, 0.5f, new Vector3f(0.25f, -1.2f, 0), false);
+ Node lLegL = createLimb(0.2f, 0.5f, new Vector3f(-0.25f, -2.2f, 0), false);
+ Node lLegR = createLimb(0.2f, 0.5f, new Vector3f(0.25f, -2.2f, 0), false);
+
+ join(body, shoulders, new Vector3f(0f, 1.4f, 0));
+ join(body, hips, new Vector3f(0f, -0.5f, 0));
+
+ join(uArmL, shoulders, new Vector3f(-0.75f, 1.4f, 0));
+ join(uArmR, shoulders, new Vector3f(0.75f, 1.4f, 0));
+ join(uArmL, lArmL, new Vector3f(-0.75f, .4f, 0));
+ join(uArmR, lArmR, new Vector3f(0.75f, .4f, 0));
+
+ join(uLegL, hips, new Vector3f(-.25f, -0.5f, 0));
+ join(uLegR, hips, new Vector3f(.25f, -0.5f, 0));
+ join(uLegL, lLegL, new Vector3f(-.25f, -1.7f, 0));
+ join(uLegR, lLegR, new Vector3f(.25f, -1.7f, 0));
+
+ ragDoll.attachChild(shoulders);
+ ragDoll.attachChild(body);
+ ragDoll.attachChild(hips);
+ ragDoll.attachChild(uArmL);
+ ragDoll.attachChild(uArmR);
+ ragDoll.attachChild(lArmL);
+ ragDoll.attachChild(lArmR);
+ ragDoll.attachChild(uLegL);
+ ragDoll.attachChild(uLegR);
+ ragDoll.attachChild(lLegL);
+ ragDoll.attachChild(lLegR);
+
+ rootNode.attachChild(ragDoll);
+ bulletAppState.getPhysicsSpace().addAll(ragDoll);
+ }
+
+ private Node createLimb(float width, float height, Vector3f location, boolean rotate) {
+ int axis = rotate ? PhysicsSpace.AXIS_X : PhysicsSpace.AXIS_Y;
+ CapsuleCollisionShape shape = new CapsuleCollisionShape(width, height, axis);
+ Node node = new Node("Limb");
+ RigidBodyControl rigidBodyControl = new RigidBodyControl(shape, 1);
+ node.setLocalTranslation(location);
+ node.addControl(rigidBodyControl);
+ return node;
+ }
+
+ private PhysicsJoint join(Node A, Node B, Vector3f connectionPoint) {
+ Vector3f pivotA = A.worldToLocal(connectionPoint, new Vector3f());
+ Vector3f pivotB = B.worldToLocal(connectionPoint, new Vector3f());
+ ConeJoint joint = new ConeJoint(A.getControl(RigidBodyControl.class), B.getControl(RigidBodyControl.class), pivotA, pivotB);
+ joint.setLimit(1f, 1f, 0);
+ return joint;
+ }
+
+ public void onAction(String string, boolean bln, float tpf) {
+ if ("Pull ragdoll up".equals(string)) {
+ if (bln) {
+ shoulders.getControl(RigidBodyControl.class).activate();
+ applyForce = true;
+ } else {
+ applyForce = false;
+ }
+ }
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ if (applyForce) {
+ shoulders.getControl(RigidBodyControl.class).applyForce(upforce, Vector3f.ZERO);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestRagdollCharacter.java b/engine/src/test/jme3test/bullet/TestRagdollCharacter.java
new file mode 100644
index 0000000..bdf5133
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestRagdollCharacter.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.AnimEventListener;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.control.KinematicRagdollControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.texture.Texture;
+
+/**
+ * @author normenhansen
+ */
+public class TestRagdollCharacter extends SimpleApplication implements AnimEventListener, ActionListener {
+
+ BulletAppState bulletAppState;
+ Node model;
+ KinematicRagdollControl ragdoll;
+ boolean leftStrafe = false, rightStrafe = false, forward = false, backward = false,
+ leftRotate = false, rightRotate = false;
+ AnimControl animControl;
+ AnimChannel animChannel;
+
+ public static void main(String[] args) {
+ TestRagdollCharacter app = new TestRagdollCharacter();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ setupKeys();
+
+ bulletAppState = new BulletAppState();
+ bulletAppState.setEnabled(true);
+ stateManager.attach(bulletAppState);
+
+
+// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ initWall(2,1,1);
+ setupLight();
+
+ cam.setLocation(new Vector3f(-8,0,-4));
+ cam.lookAt(new Vector3f(4,0,-7), Vector3f.UNIT_Y);
+
+ model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
+ model.lookAt(new Vector3f(0,0,-1), Vector3f.UNIT_Y);
+ model.setLocalTranslation(4, 0, -7f);
+
+ ragdoll = new KinematicRagdollControl(0.5f);
+ model.addControl(ragdoll);
+
+ getPhysicsSpace().add(ragdoll);
+ speed = 1.3f;
+
+ rootNode.attachChild(model);
+
+
+ AnimControl control = model.getControl(AnimControl.class);
+ animChannel = control.createChannel();
+ animChannel.setAnim("IdleTop");
+ control.addListener(this);
+
+ }
+
+ private void setupLight() {
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Rotate Left",
+ new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rotate Right",
+ new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Walk Forward",
+ new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("Walk Backward",
+ new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Slice",
+ new KeyTrigger(KeyInput.KEY_SPACE),
+ new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "Strafe Left", "Strafe Right");
+ inputManager.addListener(this, "Rotate Left", "Rotate Right");
+ inputManager.addListener(this, "Walk Forward", "Walk Backward");
+ inputManager.addListener(this, "Slice");
+ }
+
+ public void initWall(float bLength, float bWidth, float bHeight) {
+ Box brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
+ brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
+ Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ mat2.setTexture("ColorMap", tex);
+
+ float startpt = bLength / 4;
+ float height = -5;
+ for (int j = 0; j < 15; j++) {
+ for (int i = 0; i < 4; i++) {
+ Vector3f ori = new Vector3f(i * bLength * 2 + startpt, bHeight + height, -10);
+ Geometry reBoxg = new Geometry("brick", brick);
+ reBoxg.setMaterial(mat2);
+ reBoxg.setLocalTranslation(ori);
+ //for geometry with sphere mesh the physics system automatically uses a sphere collision shape
+ reBoxg.addControl(new RigidBodyControl(1.5f));
+ reBoxg.setShadowMode(ShadowMode.CastAndReceive);
+ reBoxg.getControl(RigidBodyControl.class).setFriction(0.6f);
+ this.rootNode.attachChild(reBoxg);
+ this.getPhysicsSpace().add(reBoxg);
+ }
+ startpt = -startpt;
+ height += 2 * bHeight;
+ }
+ }
+
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+
+ if (channel.getAnimationName().equals("SliceHorizontal")) {
+ channel.setLoopMode(LoopMode.DontLoop);
+ channel.setAnim("IdleTop", 5);
+ channel.setLoopMode(LoopMode.Loop);
+ }
+
+ }
+
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Rotate Left")) {
+ if (value) {
+ leftRotate = true;
+ } else {
+ leftRotate = false;
+ }
+ } else if (binding.equals("Rotate Right")) {
+ if (value) {
+ rightRotate = true;
+ } else {
+ rightRotate = false;
+ }
+ } else if (binding.equals("Walk Forward")) {
+ if (value) {
+ forward = true;
+ } else {
+ forward = false;
+ }
+ } else if (binding.equals("Walk Backward")) {
+ if (value) {
+ backward = true;
+ } else {
+ backward = false;
+ }
+ } else if (binding.equals("Slice")) {
+ if (value) {
+ animChannel.setAnim("SliceHorizontal");
+ animChannel.setSpeed(0.3f);
+ }
+ }
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ if(forward){
+ model.move(model.getLocalRotation().multLocal(new Vector3f(0,0,1)).multLocal(tpf));
+ }else if(backward){
+ model.move(model.getLocalRotation().multLocal(new Vector3f(0,0,1)).multLocal(-tpf));
+ }else if(leftRotate){
+ model.rotate(0, tpf, 0);
+ }else if(rightRotate){
+ model.rotate(0, -tpf, 0);
+ }
+ fpsText.setText(cam.getLocation() + "/" + cam.getRotation());
+ }
+
+}
diff --git a/engine/src/test/jme3test/bullet/TestSimplePhysics.java b/engine/src/test/jme3test/bullet/TestSimplePhysics.java
new file mode 100644
index 0000000..21706af
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestSimplePhysics.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.*;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+
+/**
+ * This is a basic Test of jbullet-jme functions
+ *
+ * @author normenhansen
+ */
+public class TestSimplePhysics extends SimpleApplication {
+
+ private BulletAppState bulletAppState;
+
+ public static void main(String[] args) {
+ TestSimplePhysics app = new TestSimplePhysics();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+
+ // Add a physics sphere to the world
+ Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
+ physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
+ rootNode.attachChild(physicsSphere);
+ getPhysicsSpace().add(physicsSphere);
+
+ // Add a physics sphere to the world using the collision shape from sphere one
+ Node physicsSphere2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, physicsSphere.getControl(RigidBodyControl.class).getCollisionShape(), 1);
+ physicsSphere2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(4, 8, 0));
+ rootNode.attachChild(physicsSphere2);
+ getPhysicsSpace().add(physicsSphere2);
+
+ // Add a physics box to the world
+ Node physicsBox = PhysicsTestHelper.createPhysicsTestNode(assetManager, new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
+ physicsBox.getControl(RigidBodyControl.class).setFriction(0.1f);
+ physicsBox.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(.6f, 4, .5f));
+ rootNode.attachChild(physicsBox);
+ getPhysicsSpace().add(physicsBox);
+
+ // Add a physics cylinder to the world
+ Node physicsCylinder = PhysicsTestHelper.createPhysicsTestNode(assetManager, new CylinderCollisionShape(new Vector3f(1f, 1f, 1.5f)), 1);
+ physicsCylinder.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2, 2, 0));
+ rootNode.attachChild(physicsCylinder);
+ getPhysicsSpace().add(physicsCylinder);
+
+ // an obstacle mesh, does not move (mass=0)
+ Node node2 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new MeshCollisionShape(new Sphere(16, 16, 1.2f)), 0);
+ node2.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(2.5f, -4, 0f));
+ rootNode.attachChild(node2);
+ getPhysicsSpace().add(node2);
+
+ // the floor mesh, does not move (mass=0)
+ Node node3 = PhysicsTestHelper.createPhysicsTestNode(assetManager, new PlaneCollisionShape(new Plane(new Vector3f(0, 1, 0), 0)), 0);
+ node3.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f, -6, 0f));
+ rootNode.attachChild(node3);
+ getPhysicsSpace().add(node3);
+
+ // Join the physics objects with a Point2Point joint
+// PhysicsPoint2PointJoint joint=new PhysicsPoint2PointJoint(physicsSphere, physicsBox, new Vector3f(-2,0,0), new Vector3f(2,0,0));
+// PhysicsHingeJoint joint=new PhysicsHingeJoint(physicsSphere, physicsBox, new Vector3f(-2,0,0), new Vector3f(2,0,0), Vector3f.UNIT_Z,Vector3f.UNIT_Z);
+// getPhysicsSpace().add(joint);
+
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+}
diff --git a/engine/src/test/jme3test/bullet/TestWalkingChar.java b/engine/src/test/jme3test/bullet/TestWalkingChar.java
new file mode 100644
index 0000000..e9746fb
--- /dev/null
+++ b/engine/src/test/jme3test/bullet/TestWalkingChar.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.bullet;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.AnimEventListener;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionListener;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.shapes.EmitterSphereShape;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A walking animated character followed by a 3rd person camera on a terrain with LOD.
+ * @author normenhansen
+ */
+public class TestWalkingChar extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener {
+
+ private BulletAppState bulletAppState;
+ //character
+ CharacterControl character;
+ Node model;
+ //temp vectors
+ Vector3f walkDirection = new Vector3f();
+ //terrain
+ TerrainQuad terrain;
+ RigidBodyControl terrainPhysicsNode;
+ //Materials
+ Material matRock;
+ Material matBullet;
+ //animation
+ AnimChannel animationChannel;
+ AnimChannel shootingChannel;
+ AnimControl animationControl;
+ float airTime = 0;
+ //camera
+ boolean left = false, right = false, up = false, down = false;
+ ChaseCamera chaseCam;
+ //bullet
+ Sphere bullet;
+ SphereCollisionShape bulletCollisionShape;
+ //explosion
+ ParticleEmitter effect;
+ //brick wall
+ Box brick;
+ float bLength = 0.8f;
+ float bWidth = 0.4f;
+ float bHeight = 0.4f;
+ FilterPostProcessor fpp;
+
+ public static void main(String[] args) {
+ TestWalkingChar app = new TestWalkingChar();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+ stateManager.attach(bulletAppState);
+ setupKeys();
+ prepareBullet();
+ prepareEffect();
+ createLight();
+ createSky();
+ createTerrain();
+ createWall();
+ createCharacter();
+ setupChaseCamera();
+ setupAnimationController();
+ setupFilter();
+ }
+
+ private void setupFilter() {
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
+ fpp.addFilter(bloom);
+ viewPort.addProcessor(fpp);
+ }
+
+ private PhysicsSpace getPhysicsSpace() {
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(this, "wireframe");
+ inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
+ inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
+ inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
+ inputManager.addMapping("CharSpace", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this, "CharLeft");
+ inputManager.addListener(this, "CharRight");
+ inputManager.addListener(this, "CharUp");
+ inputManager.addListener(this, "CharDown");
+ inputManager.addListener(this, "CharSpace");
+ inputManager.addListener(this, "CharShoot");
+ }
+
+ private void createWall() {
+ float xOff = -144;
+ float zOff = -40;
+ float startpt = bLength / 4 - xOff;
+ float height = 6.1f;
+ brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
+ brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
+ for (int j = 0; j < 15; j++) {
+ for (int i = 0; i < 4; i++) {
+ Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight + height, zOff);
+ addBrick(vt);
+ }
+ startpt = -startpt;
+ height += 1.01f * bHeight;
+ }
+ }
+
+ private void addBrick(Vector3f ori) {
+ Geometry reBoxg = new Geometry("brick", brick);
+ reBoxg.setMaterial(matBullet);
+ reBoxg.setLocalTranslation(ori);
+ reBoxg.addControl(new RigidBodyControl(1.5f));
+ reBoxg.setShadowMode(ShadowMode.CastAndReceive);
+ this.rootNode.attachChild(reBoxg);
+ this.getPhysicsSpace().add(reBoxg);
+ }
+
+ private void prepareBullet() {
+ bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ bulletCollisionShape = new SphereCollisionShape(0.4f);
+ matBullet = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ matBullet.setColor("Color", ColorRGBA.Green);
+ matBullet.setColor("m_GlowColor", ColorRGBA.Green);
+ getPhysicsSpace().addCollisionListener(this);
+ }
+
+ private void prepareEffect() {
+ int COUNT_FACTOR = 1;
+ float COUNT_FACTOR_F = 1f;
+ effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
+ effect.setSelectRandomImage(true);
+ effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (float) (1f / COUNT_FACTOR_F)));
+ effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
+ effect.setStartSize(1.3f);
+ effect.setEndSize(2f);
+ effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
+ effect.setParticlesPerSec(0);
+ effect.setGravity(0, -5, 0);
+ effect.setLowLife(.4f);
+ effect.setHighLife(.5f);
+ effect.setInitialVelocity(new Vector3f(0, 7, 0));
+ effect.setVelocityVariation(1f);
+ effect.setImagesX(2);
+ effect.setImagesY(2);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+ effect.setMaterial(mat);
+// effect.setLocalScale(100);
+ rootNode.attachChild(effect);
+ }
+
+ private void createLight() {
+ Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(direction);
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ private void createSky() {
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+ }
+
+ private void createTerrain() {
+ matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matRock.setBoolean("useTriPlanarMapping", false);
+ matRock.setBoolean("WardIso", true);
+ matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap", grass);
+ matRock.setFloat("DiffuseMap_0_scale", 64);
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_1", dirt);
+ matRock.setFloat("DiffuseMap_1_scale", 16);
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_2", rock);
+ matRock.setFloat("DiffuseMap_2_scale", 128);
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matRock.setTexture("NormalMap", normalMap0);
+ matRock.setTexture("NormalMap_1", normalMap2);
+ matRock.setTexture("NormalMap_2", normalMap2);
+
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
+ heightmap.load();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ List<Camera> cameras = new ArrayList<Camera>();
+ cameras.add(getCamera());
+ TerrainLodControl control = new TerrainLodControl(terrain, cameras);
+ terrain.addControl(control);
+ terrain.setMaterial(matRock);
+ terrain.setLocalScale(new Vector3f(2, 2, 2));
+
+ terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);
+ terrain.addControl(terrainPhysicsNode);
+ rootNode.attachChild(terrain);
+ getPhysicsSpace().add(terrainPhysicsNode);
+ }
+
+ private void createCharacter() {
+ CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
+ character = new CharacterControl(capsule, 0.01f);
+ model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ //model.setLocalScale(0.5f);
+ model.addControl(character);
+ character.setPhysicsLocation(new Vector3f(-140, 15, -10));
+ rootNode.attachChild(model);
+ getPhysicsSpace().add(character);
+ }
+
+ private void setupChaseCamera() {
+ flyCam.setEnabled(false);
+ chaseCam = new ChaseCamera(cam, model, inputManager);
+ }
+
+ private void setupAnimationController() {
+ animationControl = model.getControl(AnimControl.class);
+ animationControl.addListener(this);
+ animationChannel = animationControl.createChannel();
+ shootingChannel = animationControl.createChannel();
+ shootingChannel.addBone(animationControl.getSkeleton().getBone("uparm.right"));
+ shootingChannel.addBone(animationControl.getSkeleton().getBone("arm.right"));
+ shootingChannel.addBone(animationControl.getSkeleton().getBone("hand.right"));
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);
+ Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
+ camDir.y = 0;
+ camLeft.y = 0;
+ walkDirection.set(0, 0, 0);
+ if (left) {
+ walkDirection.addLocal(camLeft);
+ }
+ if (right) {
+ walkDirection.addLocal(camLeft.negate());
+ }
+ if (up) {
+ walkDirection.addLocal(camDir);
+ }
+ if (down) {
+ walkDirection.addLocal(camDir.negate());
+ }
+ if (!character.onGround()) {
+ airTime = airTime + tpf;
+ } else {
+ airTime = 0;
+ }
+ if (walkDirection.length() == 0) {
+ if (!"stand".equals(animationChannel.getAnimationName())) {
+ animationChannel.setAnim("stand", 1f);
+ }
+ } else {
+ character.setViewDirection(walkDirection);
+ if (airTime > .3f) {
+ if (!"stand".equals(animationChannel.getAnimationName())) {
+ animationChannel.setAnim("stand");
+ }
+ } else if (!"Walk".equals(animationChannel.getAnimationName())) {
+ animationChannel.setAnim("Walk", 0.7f);
+ }
+ }
+ character.setWalkDirection(walkDirection);
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("CharLeft")) {
+ if (value) {
+ left = true;
+ } else {
+ left = false;
+ }
+ } else if (binding.equals("CharRight")) {
+ if (value) {
+ right = true;
+ } else {
+ right = false;
+ }
+ } else if (binding.equals("CharUp")) {
+ if (value) {
+ up = true;
+ } else {
+ up = false;
+ }
+ } else if (binding.equals("CharDown")) {
+ if (value) {
+ down = true;
+ } else {
+ down = false;
+ }
+ } else if (binding.equals("CharSpace")) {
+ character.jump();
+ } else if (binding.equals("CharShoot") && !value) {
+ bulletControl();
+ }
+ }
+
+ private void bulletControl() {
+ shootingChannel.setAnim("Dodge", 0.1f);
+ shootingChannel.setLoopMode(LoopMode.DontLoop);
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(matBullet);
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.setLocalTranslation(character.getPhysicsLocation().add(cam.getDirection().mult(5)));
+ RigidBodyControl bulletControl = new BombControl(bulletCollisionShape, 1);
+ bulletControl.setCcdMotionThreshold(0.1f);
+ bulletControl.setLinearVelocity(cam.getDirection().mult(80));
+ bulletg.addControl(bulletControl);
+ rootNode.attachChild(bulletg);
+ getPhysicsSpace().add(bulletControl);
+ }
+
+ public void collision(PhysicsCollisionEvent event) {
+ if (event.getObjectA() instanceof BombControl) {
+ final Spatial node = event.getNodeA();
+ effect.killAllParticles();
+ effect.setLocalTranslation(node.getLocalTranslation());
+ effect.emitAllParticles();
+ } else if (event.getObjectB() instanceof BombControl) {
+ final Spatial node = event.getNodeB();
+ effect.killAllParticles();
+ effect.setLocalTranslation(node.getLocalTranslation());
+ effect.emitAllParticles();
+ }
+ }
+
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+ if (channel == shootingChannel) {
+ channel.setAnim("stand");
+ }
+ }
+
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+ }
+}
diff --git a/engine/src/test/jme3test/collision/RayTrace.java b/engine/src/test/jme3test/collision/RayTrace.java
new file mode 100644
index 0000000..7e02ad1
--- /dev/null
+++ b/engine/src/test/jme3test/collision/RayTrace.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.collision;
+
+import com.jme3.collision.CollisionResults;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Spatial;
+import java.awt.FlowLayout;
+import java.awt.image.BufferedImage;
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+public class RayTrace {
+
+ private BufferedImage image;
+ private Camera cam;
+ private Spatial scene;
+ private CollisionResults results = new CollisionResults();
+ private JFrame frame;
+ private JLabel label;
+
+ public RayTrace(Spatial scene, Camera cam, int width, int height){
+ image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ this.scene = scene;
+ this.cam = cam;
+ }
+
+ public void show(){
+ frame = new JFrame("HDR View");
+ label = new JLabel(new ImageIcon(image));
+ frame.getContentPane().add(label);
+ frame.setLayout(new FlowLayout());
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ public void update(){
+ int w = image.getWidth();
+ int h = image.getHeight();
+
+ float wr = (float) cam.getWidth() / image.getWidth();
+ float hr = (float) cam.getHeight() / image.getHeight();
+
+ scene.updateGeometricState();
+
+ for (int y = 0; y < h; y++){
+ for (int x = 0; x < w; x++){
+ Vector2f v = new Vector2f(x * wr,y * hr);
+ Vector3f pos = cam.getWorldCoordinates(v, 0.0f);
+ Vector3f dir = cam.getWorldCoordinates(v, 0.3f);
+ dir.subtractLocal(pos).normalizeLocal();
+
+ Ray r = new Ray(pos, dir);
+
+ results.clear();
+ scene.collideWith(r, results);
+ if (results.size() > 0){
+ image.setRGB(x, h - y - 1, 0xFFFFFFFF);
+ }else{
+ image.setRGB(x, h - y - 1, 0xFF000000);
+ }
+ }
+ }
+
+ label.repaint();
+ }
+
+}
diff --git a/engine/src/test/jme3test/collision/TestMousePick.java b/engine/src/test/jme3test/collision/TestMousePick.java
new file mode 100644
index 0000000..835b2c6
--- /dev/null
+++ b/engine/src/test/jme3test/collision/TestMousePick.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.collision;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import com.jme3.scene.shape.Box;
+
+public class TestMousePick extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestMousePick app = new TestMousePick();
+ app.start();
+ }
+
+ Node shootables;
+ Geometry mark;
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setEnabled(false);
+ initMark(); // a red sphere to mark the hit
+
+ /** create four colored boxes and a floor to shoot at: */
+ shootables = new Node("Shootables");
+ rootNode.attachChild(shootables);
+ shootables.attachChild(makeCube("a Dragon", -2f, 0f, 1f));
+ shootables.attachChild(makeCube("a tin can", 1f, -2f, 0f));
+ shootables.attachChild(makeCube("the Sheriff", 0f, 1f, -2f));
+ shootables.attachChild(makeCube("the Deputy", 1f, 0f, -4f));
+ shootables.attachChild(makeFloor());
+ shootables.attachChild(makeCharacter());
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ Vector3f origin = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
+ Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);
+ direction.subtractLocal(origin).normalizeLocal();
+
+ Ray ray = new Ray(origin, direction);
+ CollisionResults results = new CollisionResults();
+ shootables.collideWith(ray, results);
+// System.out.println("----- Collisions? " + results.size() + "-----");
+// for (int i = 0; i < results.size(); i++) {
+// // For each hit, we know distance, impact point, name of geometry.
+// float dist = results.getCollision(i).getDistance();
+// Vector3f pt = results.getCollision(i).getWorldContactPoint();
+// String hit = results.getCollision(i).getGeometry().getName();
+// System.out.println("* Collision #" + i);
+// System.out.println(" You shot " + hit + " at " + pt + ", " + dist + " wu away.");
+// }
+ if (results.size() > 0) {
+ CollisionResult closest = results.getClosestCollision();
+ mark.setLocalTranslation(closest.getContactPoint());
+
+ Quaternion q = new Quaternion();
+ q.lookAt(closest.getContactNormal(), Vector3f.UNIT_Y);
+ mark.setLocalRotation(q);
+
+ rootNode.attachChild(mark);
+ } else {
+ rootNode.detachChild(mark);
+ }
+ }
+
+ /** A cube object for target practice */
+ protected Geometry makeCube(String name, float x, float y, float z) {
+ Box box = new Box(new Vector3f(x, y, z), 1, 1, 1);
+ Geometry cube = new Geometry(name, box);
+ Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.randomColor());
+ cube.setMaterial(mat1);
+ return cube;
+ }
+
+ /** A floor to show that the "shot" can go through several objects. */
+ protected Geometry makeFloor() {
+ Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15);
+ Geometry floor = new Geometry("the Floor", box);
+ Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.Gray);
+ floor.setMaterial(mat1);
+ return floor;
+ }
+
+ /** A red ball that marks the last spot that was "hit" by the "shot". */
+ protected void initMark() {
+ Arrow arrow = new Arrow(Vector3f.UNIT_Z.mult(2f));
+ arrow.setLineWidth(3);
+
+ //Sphere sphere = new Sphere(30, 30, 0.2f);
+ mark = new Geometry("BOOM!", arrow);
+ //mark = new Geometry("BOOM!", sphere);
+ Material mark_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mark_mat.setColor("Color", ColorRGBA.Red);
+ mark.setMaterial(mark_mat);
+ }
+
+ protected Spatial makeCharacter() {
+ // load a character from jme3test-test-data
+ Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ golem.scale(0.5f);
+ golem.setLocalTranslation(-1.0f, -1.5f, -0.6f);
+
+ // We must add a light to make the model visible
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());
+ golem.addLight(sun);
+ return golem;
+ }
+}
diff --git a/engine/src/test/jme3test/collision/TestRayCasting.java b/engine/src/test/jme3test/collision/TestRayCasting.java
new file mode 100644
index 0000000..b2ef0de
--- /dev/null
+++ b/engine/src/test/jme3test/collision/TestRayCasting.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.collision;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+
+public class TestRayCasting extends SimpleApplication {
+
+ private RayTrace tracer;
+ private Spatial teapot;
+
+ public static void main(String[] args){
+ TestRayCasting app = new TestRayCasting();
+ app.setPauseOnLostFocus(false);
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+// flyCam.setEnabled(false);
+
+ // load material
+ Material mat = (Material) assetManager.loadMaterial("Interface/Logo/Logo.j3m");
+
+ Mesh q = new Mesh();
+ q.setBuffer(Type.Position, 3, new float[]
+ {
+ 1, 0, 0,
+ 0, 1.5f, 0,
+ -1, 0, 0
+ }
+ );
+ q.setBuffer(Type.Index, 3, new int[]{ 0, 1, 2 });
+ q.setBound(new BoundingSphere());
+ q.updateBound();
+// Geometry teapot = new Geometry("MyGeom", q);
+
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.mesh.xml");
+// teapot.scale(2f, 2f, 2f);
+// teapot.move(2f, 2f, -.5f);
+ teapot.rotate(FastMath.HALF_PI, FastMath.HALF_PI, FastMath.HALF_PI);
+ teapot.setMaterial(mat);
+ rootNode.attachChild(teapot);
+
+// cam.setLocation(cam.getLocation().add(0,1,0));
+// cam.lookAt(teapot.getWorldBound().getCenter(), Vector3f.UNIT_Y);
+
+ tracer = new RayTrace(rootNode, cam, 160, 128);
+ tracer.show();
+ tracer.update();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ teapot.rotate(0,tpf,0);
+ tracer.update();
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/collision/TestTriangleCollision.java b/engine/src/test/jme3test/collision/TestTriangleCollision.java
new file mode 100644
index 0000000..29ed81f
--- /dev/null
+++ b/engine/src/test/jme3test/collision/TestTriangleCollision.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.collision;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.CollisionResults;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+
+public class TestTriangleCollision extends SimpleApplication {
+
+ Geometry geom1;
+
+ Spatial golem;
+
+ public static void main(String[] args) {
+ TestTriangleCollision app = new TestTriangleCollision();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // Create two boxes
+ Mesh mesh1 = new Box(0.5f, 0.5f, 0.5f);
+ geom1 = new Geometry("Box", mesh1);
+ geom1.move(2, 2, -.5f);
+ Material m1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ m1.setColor("Color", ColorRGBA.Blue);
+ geom1.setMaterial(m1);
+ rootNode.attachChild(geom1);
+
+ // load a character from jme3test-test-data
+ golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ golem.scale(0.5f);
+ golem.setLocalTranslation(-1.0f, -1.5f, -0.6f);
+
+ // We must add a light to make the model visible
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());
+ golem.addLight(sun);
+ rootNode.attachChild(golem);
+
+ // Create input
+ inputManager.addMapping("MoveRight", new KeyTrigger(KeyInput.KEY_L));
+ inputManager.addMapping("MoveLeft", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("MoveUp", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("MoveDown", new KeyTrigger(KeyInput.KEY_K));
+
+ inputManager.addListener(analogListener, new String[]{
+ "MoveRight", "MoveLeft", "MoveUp", "MoveDown"
+ });
+ }
+ private AnalogListener analogListener = new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals("MoveRight")) {
+ geom1.move(2 * tpf, 0, 0);
+ }
+
+ if (name.equals("MoveLeft")) {
+ geom1.move(-2 * tpf, 0, 0);
+ }
+
+ if (name.equals("MoveUp")) {
+ geom1.move(0, 2 * tpf, 0);
+ }
+
+ if (name.equals("MoveDown")) {
+ geom1.move(0, -2 * tpf, 0);
+ }
+ }
+ };
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ CollisionResults results = new CollisionResults();
+ BoundingVolume bv = geom1.getWorldBound();
+ golem.collideWith(bv, results);
+
+ if (results.size() > 0) {
+ geom1.getMaterial().setColor("Color", ColorRGBA.Red);
+ }else{
+ geom1.getMaterial().setColor("Color", ColorRGBA.Blue);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/conversion/TestMipMapGen.java b/engine/src/test/jme3test/conversion/TestMipMapGen.java
new file mode 100644
index 0000000..13f67f1
--- /dev/null
+++ b/engine/src/test/jme3test/conversion/TestMipMapGen.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.conversion;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.material.Material;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import jme3tools.converters.MipMapGenerator;
+
+public class TestMipMapGen extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestMipMapGen app = new TestMipMapGen();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ BitmapText txt = guiFont.createLabel("Left: HW Mips");
+ txt.setLocalTranslation(0, settings.getHeight() - txt.getLineHeight() * 4, 0);
+ guiNode.attachChild(txt);
+
+ txt = guiFont.createLabel("Right: AWT Mips");
+ txt.setLocalTranslation(0, settings.getHeight() - txt.getLineHeight() * 3, 0);
+ guiNode.attachChild(txt);
+
+ // create a simple plane/quad
+ Quad quadMesh = new Quad(1, 1);
+ quadMesh.updateGeometry(1, 1, false);
+ quadMesh.updateBound();
+
+ Geometry quad1 = new Geometry("Textured Quad", quadMesh);
+ Geometry quad2 = new Geometry("Textured Quad 2", quadMesh);
+
+ Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.png");
+ tex.setMinFilter(Texture.MinFilter.Trilinear);
+
+ Texture texCustomMip = tex.clone();
+ Image imageCustomMip = texCustomMip.getImage().clone();
+ MipMapGenerator.generateMipMaps(imageCustomMip);
+ texCustomMip.setImage(imageCustomMip);
+
+ Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setTexture("ColorMap", tex);
+
+ Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.setTexture("ColorMap", texCustomMip);
+
+ quad1.setMaterial(mat1);
+// quad1.setLocalTranslation(1, 0, 0);
+
+ quad2.setMaterial(mat2);
+ quad2.setLocalTranslation(1, 0, 0);
+
+ rootNode.attachChild(quad1);
+ rootNode.attachChild(quad2);
+ }
+
+}
diff --git a/engine/src/test/jme3test/conversion/TestTriangleStrip.java b/engine/src/test/jme3test/conversion/TestTriangleStrip.java
new file mode 100644
index 0000000..2191e11
--- /dev/null
+++ b/engine/src/test/jme3test/conversion/TestTriangleStrip.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.conversion;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import jme3tools.converters.model.ModelConverter;
+
+public class TestTriangleStrip extends SimpleApplication {
+
+
+ public static void main(String[] args){
+ TestTriangleStrip app = new TestTriangleStrip();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Geometry teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+ Mesh teaMesh = teaGeom.getMesh();
+ ModelConverter.generateStrips(teaMesh, true, false, 24, 0);
+
+ // show normals as material
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+
+ for (int y = -10; y < 10; y++){
+ for (int x = -10; x < 10; x++){
+ Geometry teaClone = new Geometry("teapot", teaMesh);
+ teaClone.setMaterial(mat);
+
+ teaClone.setLocalTranslation(x * .5f, 0, y * .5f);
+ teaClone.setLocalScale(.5f);
+
+ rootNode.attachChild(teaClone);
+ }
+ }
+
+ cam.setLocation(new Vector3f(8.378951f, 5.4324f, 8.795956f));
+ cam.setRotation(new Quaternion(-0.083419204f, 0.90370524f, -0.20599906f, -0.36595422f));
+ }
+
+}
diff --git a/engine/src/test/jme3test/effect/TestEverything.java b/engine/src/test/jme3test/effect/TestEverything.java
new file mode 100644
index 0000000..a3ada4d
--- /dev/null
+++ b/engine/src/test/jme3test/effect/TestEverything.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.effect;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.HDRRenderer;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.shape.Box;
+import com.jme3.shadow.BasicShadowRenderer;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestEverything extends SimpleApplication {
+
+ private BasicShadowRenderer bsr;
+ private HDRRenderer hdrRender;
+ private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal();
+
+ public static void main(String[] args){
+ TestEverything app = new TestEverything();
+ app.start();
+ }
+
+ public void setupHdr(){
+ if (renderer.getCaps().contains(Caps.GLSL100)){
+ hdrRender = new HDRRenderer(assetManager, renderer);
+ hdrRender.setMaxIterations(40);
+ hdrRender.setSamples(settings.getSamples());
+
+ hdrRender.setWhiteLevel(3);
+ hdrRender.setExposure(0.72f);
+ hdrRender.setThrottle(1);
+
+ // setPauseOnLostFocus(false);
+ // new HDRConfig(hdrRender).setVisible(true);
+
+ viewPort.addProcessor(hdrRender);
+ }
+ }
+
+ public void setupBasicShadow(){
+ if (renderer.getCaps().contains(Caps.GLSL100)){
+ bsr = new BasicShadowRenderer(assetManager, 1024);
+ bsr.setDirection(lightDir);
+ viewPort.addProcessor(bsr);
+ }
+ }
+
+ public void setupSkyBox(){
+ Texture envMap;
+ if (renderer.getCaps().contains(Caps.FloatTexture)){
+ envMap = assetManager.loadTexture("Textures/Sky/St Peters/StPeters.hdr");
+ }else{
+ envMap = assetManager.loadTexture("Textures/Sky/St Peters/StPeters.jpg");
+ }
+ rootNode.attachChild(SkyFactory.createSky(assetManager, envMap, new Vector3f(-1,-1,-1), true));
+ }
+
+ public void setupLighting(){
+ boolean hdr = false;
+ if (hdrRender != null){
+ hdr = hdrRender.isEnabled();
+ }
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(lightDir);
+ if (hdr){
+ dl.setColor(new ColorRGBA(3, 3, 3, 1));
+ }else{
+ dl.setColor(new ColorRGBA(.9f, .9f, .9f, 1));
+ }
+ rootNode.addLight(dl);
+
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, 0, -1).normalizeLocal());
+ if (hdr){
+ dl.setColor(new ColorRGBA(1, 1, 1, 1));
+ }else{
+ dl.setColor(new ColorRGBA(.4f, .4f, .4f, 1));
+ }
+ rootNode.addLight(dl);
+ }
+
+ public void setupFloor(){
+ Material mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m");
+ mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
+
+ Box floor = new Box(Vector3f.ZERO, 50, 1f, 50);
+ TangentBinormalGenerator.generate(floor);
+ floor.scaleTextureCoordinates(new Vector2f(5, 5));
+ Geometry floorGeom = new Geometry("Floor", floor);
+ floorGeom.setMaterial(mat);
+ floorGeom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(floorGeom);
+ }
+
+// public void setupTerrain(){
+// Material mat = manager.loadMaterial("Textures/Terrain/Rock/Rock.j3m");
+// mat.getTextureParam("DiffuseMap").getValue().setWrap(WrapMode.Repeat);
+// mat.getTextureParam("NormalMap").getValue().setWrap(WrapMode.Repeat);
+// try{
+// Geomap map = GeomapLoader.fromImage(TestEverything.class.getResource("/textures/heightmap.png"));
+// Mesh m = map.createMesh(new Vector3f(0.35f, 0.0005f, 0.35f), new Vector2f(10, 10), true);
+// Logger.getLogger(TangentBinormalGenerator.class.getName()).setLevel(Level.SEVERE);
+// TangentBinormalGenerator.generate(m);
+// Geometry t = new Geometry("Terrain", m);
+// t.setLocalTranslation(85, -15, 0);
+// t.setMaterial(mat);
+// t.updateModelBound();
+// t.setShadowMode(ShadowMode.Receive);
+// rootNode.attachChild(t);
+// }catch (IOException ex){
+// ex.printStackTrace();
+// }
+//
+// }
+
+ public void setupRobotGuy(){
+ Node model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ Material mat = assetManager.loadMaterial("Models/Oto/Oto.j3m");
+ model.getChild(0).setMaterial(mat);
+// model.setAnimation("Walk");
+ model.setLocalTranslation(30, 10.5f, 30);
+ model.setLocalScale(2);
+ model.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(model);
+ }
+
+ public void setupSignpost(){
+ Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
+ Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
+ signpost.setMaterial(mat);
+ signpost.rotate(0, FastMath.HALF_PI, 0);
+ signpost.setLocalTranslation(12, 3.5f, 30);
+ signpost.setLocalScale(4);
+ signpost.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(signpost);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(-32.295086f, 54.80136f, 79.59805f));
+ cam.setRotation(new Quaternion(0.074364014f, 0.92519957f, -0.24794696f, 0.27748522f));
+ cam.update();
+
+ cam.setFrustumFar(300);
+ flyCam.setMoveSpeed(30);
+
+ rootNode.setCullHint(CullHint.Never);
+
+ setupBasicShadow();
+ setupHdr();
+
+ setupLighting();
+ setupSkyBox();
+
+// setupTerrain();
+ setupFloor();
+// setupRobotGuy();
+ setupSignpost();
+
+
+ }
+
+}
diff --git a/engine/src/test/jme3test/effect/TestExplosionEffect.java b/engine/src/test/jme3test/effect/TestExplosionEffect.java
new file mode 100644
index 0000000..7ceae96
--- /dev/null
+++ b/engine/src/test/jme3test/effect/TestExplosionEffect.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.effect;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.shapes.EmitterSphereShape;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+
+public class TestExplosionEffect extends SimpleApplication {
+
+ private float time = 0;
+ private int state = 0;
+ private Node explosionEffect = new Node("explosionFX");
+ private ParticleEmitter flame, flash, spark, roundspark, smoketrail, debris,
+ shockwave;
+
+
+ private static final int COUNT_FACTOR = 1;
+ private static final float COUNT_FACTOR_F = 1f;
+
+ private static final boolean POINT_SPRITE = true;
+ private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle;
+
+ public static void main(String[] args){
+ TestExplosionEffect app = new TestExplosionEffect();
+ app.start();
+ }
+
+ private void createFlame(){
+ flame = new ParticleEmitter("Flame", EMITTER_TYPE, 32 * COUNT_FACTOR);
+ flame.setSelectRandomImage(true);
+ flame.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (float) (1f / COUNT_FACTOR_F)));
+ flame.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
+ flame.setStartSize(1.3f);
+ flame.setEndSize(2f);
+ flame.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
+ flame.setParticlesPerSec(0);
+ flame.setGravity(0, -5, 0);
+ flame.setLowLife(.4f);
+ flame.setHighLife(.5f);
+ flame.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 7, 0));
+ flame.getParticleInfluencer().setVelocityVariation(1f);
+ flame.setImagesX(2);
+ flame.setImagesY(2);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+ mat.setBoolean("PointSprite", POINT_SPRITE);
+ flame.setMaterial(mat);
+ explosionEffect.attachChild(flame);
+ }
+
+ private void createFlash(){
+ flash = new ParticleEmitter("Flash", EMITTER_TYPE, 24 * COUNT_FACTOR);
+ flash.setSelectRandomImage(true);
+ flash.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, (float) (1f / COUNT_FACTOR_F)));
+ flash.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
+ flash.setStartSize(.1f);
+ flash.setEndSize(3.0f);
+ flash.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
+ flash.setParticlesPerSec(0);
+ flash.setGravity(0, 0, 0);
+ flash.setLowLife(.2f);
+ flash.setHighLife(.2f);
+ flash.setInitialVelocity(new Vector3f(0, 5f, 0));
+ flash.setVelocityVariation(1);
+ flash.setImagesX(2);
+ flash.setImagesY(2);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flash.png"));
+ mat.setBoolean("PointSprite", POINT_SPRITE);
+ flash.setMaterial(mat);
+ explosionEffect.attachChild(flash);
+ }
+
+ private void createRoundSpark(){
+ roundspark = new ParticleEmitter("RoundSpark", EMITTER_TYPE, 20 * COUNT_FACTOR);
+ roundspark.setStartColor(new ColorRGBA(1f, 0.29f, 0.34f, (float) (1.0 / COUNT_FACTOR_F)));
+ roundspark.setEndColor(new ColorRGBA(0, 0, 0, (float) (0.5f / COUNT_FACTOR_F)));
+ roundspark.setStartSize(1.2f);
+ roundspark.setEndSize(1.8f);
+ roundspark.setShape(new EmitterSphereShape(Vector3f.ZERO, 2f));
+ roundspark.setParticlesPerSec(0);
+ roundspark.setGravity(0, -.5f, 0);
+ roundspark.setLowLife(1.8f);
+ roundspark.setHighLife(2f);
+ roundspark.setInitialVelocity(new Vector3f(0, 3, 0));
+ roundspark.setVelocityVariation(.5f);
+ roundspark.setImagesX(1);
+ roundspark.setImagesY(1);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/roundspark.png"));
+ mat.setBoolean("PointSprite", POINT_SPRITE);
+ roundspark.setMaterial(mat);
+ explosionEffect.attachChild(roundspark);
+ }
+
+ private void createSpark(){
+ spark = new ParticleEmitter("Spark", Type.Triangle, 30 * COUNT_FACTOR);
+ spark.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, (float) (1.0f / COUNT_FACTOR_F)));
+ spark.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
+ spark.setStartSize(.5f);
+ spark.setEndSize(.5f);
+ spark.setFacingVelocity(true);
+ spark.setParticlesPerSec(0);
+ spark.setGravity(0, 5, 0);
+ spark.setLowLife(1.1f);
+ spark.setHighLife(1.5f);
+ spark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
+ spark.getParticleInfluencer().setVelocityVariation(1);
+ spark.setImagesX(1);
+ spark.setImagesY(1);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/spark.png"));
+ spark.setMaterial(mat);
+ explosionEffect.attachChild(spark);
+ }
+
+ private void createSmokeTrail(){
+ smoketrail = new ParticleEmitter("SmokeTrail", Type.Triangle, 22 * COUNT_FACTOR);
+ smoketrail.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, (float) (1.0f / COUNT_FACTOR_F)));
+ smoketrail.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
+ smoketrail.setStartSize(.2f);
+ smoketrail.setEndSize(1f);
+
+// smoketrail.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
+ smoketrail.setFacingVelocity(true);
+ smoketrail.setParticlesPerSec(0);
+ smoketrail.setGravity(0, 1, 0);
+ smoketrail.setLowLife(.4f);
+ smoketrail.setHighLife(.5f);
+ smoketrail.setInitialVelocity(new Vector3f(0, 12, 0));
+ smoketrail.setVelocityVariation(1);
+ smoketrail.setImagesX(1);
+ smoketrail.setImagesY(3);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/smoketrail.png"));
+ smoketrail.setMaterial(mat);
+ explosionEffect.attachChild(smoketrail);
+ }
+
+ private void createDebris(){
+ debris = new ParticleEmitter("Debris", Type.Triangle, 15 * COUNT_FACTOR);
+ debris.setSelectRandomImage(true);
+ debris.setRandomAngle(true);
+ debris.setRotateSpeed(FastMath.TWO_PI * 4);
+ debris.setStartColor(new ColorRGBA(1f, 0.59f, 0.28f, (float) (1.0f / COUNT_FACTOR_F)));
+ debris.setEndColor(new ColorRGBA(.5f, 0.5f, 0.5f, 0f));
+ debris.setStartSize(.2f);
+ debris.setEndSize(.2f);
+
+// debris.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
+ debris.setParticlesPerSec(0);
+ debris.setGravity(0, 12f, 0);
+ debris.setLowLife(1.4f);
+ debris.setHighLife(1.5f);
+ debris.setInitialVelocity(new Vector3f(0, 15, 0));
+ debris.setVelocityVariation(.60f);
+ debris.setImagesX(3);
+ debris.setImagesY(3);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/Debris.png"));
+ debris.setMaterial(mat);
+ explosionEffect.attachChild(debris);
+ }
+
+ private void createShockwave(){
+ shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR);
+// shockwave.setRandomAngle(true);
+ shockwave.setFaceNormal(Vector3f.UNIT_Y);
+ shockwave.setStartColor(new ColorRGBA(.48f, 0.17f, 0.01f, (float) (.8f / COUNT_FACTOR_F)));
+ shockwave.setEndColor(new ColorRGBA(.48f, 0.17f, 0.01f, 0f));
+
+ shockwave.setStartSize(0f);
+ shockwave.setEndSize(7f);
+
+ shockwave.setParticlesPerSec(0);
+ shockwave.setGravity(0, 0, 0);
+ shockwave.setLowLife(0.5f);
+ shockwave.setHighLife(0.5f);
+ shockwave.setInitialVelocity(new Vector3f(0, 0, 0));
+ shockwave.setVelocityVariation(0f);
+ shockwave.setImagesX(1);
+ shockwave.setImagesY(1);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/shockwave.png"));
+ shockwave.setMaterial(mat);
+ explosionEffect.attachChild(shockwave);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ createFlame();
+ createFlash();
+ createSpark();
+ createRoundSpark();
+ createSmokeTrail();
+ createDebris();
+ createShockwave();
+ explosionEffect.setLocalScale(0.5f);
+ renderManager.preloadScene(explosionEffect);
+
+ cam.setLocation(new Vector3f(0, 3.5135868f, 10));
+ cam.setRotation(new Quaternion(1.5714673E-4f, 0.98696727f, -0.16091813f, 9.6381607E-4f));
+
+ rootNode.attachChild(explosionEffect);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ time += tpf / speed;
+ if (time > 1f && state == 0){
+ flash.emitAllParticles();
+ spark.emitAllParticles();
+ smoketrail.emitAllParticles();
+ debris.emitAllParticles();
+ shockwave.emitAllParticles();
+ state++;
+ }
+ if (time > 1f + .05f / speed && state == 1){
+ flame.emitAllParticles();
+ roundspark.emitAllParticles();
+ state++;
+ }
+
+ // rewind the effect
+ if (time > 5 / speed && state == 2){
+ state = 0;
+ time = 0;
+
+ flash.killAllParticles();
+ spark.killAllParticles();
+ smoketrail.killAllParticles();
+ debris.killAllParticles();
+ flame.killAllParticles();
+ roundspark.killAllParticles();
+ shockwave.killAllParticles();
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/effect/TestMovingParticle.java b/engine/src/test/jme3test/effect/TestMovingParticle.java
new file mode 100644
index 0000000..af4b675
--- /dev/null
+++ b/engine/src/test/jme3test/effect/TestMovingParticle.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.effect;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+
+/**
+ * Particle that moves in a circle.
+ *
+ * @author Kirill Vainer
+ */
+public class TestMovingParticle extends SimpleApplication {
+
+ private ParticleEmitter emit;
+ private float angle = 0;
+
+ public static void main(String[] args) {
+ TestMovingParticle app = new TestMovingParticle();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ emit = new ParticleEmitter("Emitter", Type.Triangle, 300);
+ emit.setGravity(0, 0, 0);
+ emit.setVelocityVariation(1);
+ emit.setLowLife(1);
+ emit.setHighLife(1);
+ emit.setInitialVelocity(new Vector3f(0, .5f, 0));
+ emit.setImagesX(15);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png"));
+ emit.setMaterial(mat);
+
+ rootNode.attachChild(emit);
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if ("setNum".equals(name) && isPressed) {
+ emit.setNumParticles(1000);
+ }
+ }
+ }, "setNum");
+
+ inputManager.addMapping("setNum", new KeyTrigger(KeyInput.KEY_SPACE));
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+ float x = FastMath.cos(angle) * 2;
+ float y = FastMath.sin(angle) * 2;
+ emit.setLocalTranslation(x, 0, y);
+ }
+}
diff --git a/engine/src/test/jme3test/effect/TestParticleExportingCloning.java b/engine/src/test/jme3test/effect/TestParticleExportingCloning.java
new file mode 100644
index 0000000..df6390a
--- /dev/null
+++ b/engine/src/test/jme3test/effect/TestParticleExportingCloning.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.effect;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.shapes.EmitterSphereShape;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class TestParticleExportingCloning extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestParticleExportingCloning app = new TestParticleExportingCloning();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ ParticleEmitter emit = new ParticleEmitter("Emitter", Type.Triangle, 200);
+ emit.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
+ emit.setGravity(0, 0, 0);
+ emit.setLowLife(5);
+ emit.setHighLife(10);
+ emit.setInitialVelocity(new Vector3f(0, 0, 0));
+ emit.setImagesX(15);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png"));
+ emit.setMaterial(mat);
+
+ ParticleEmitter emit2 = emit.clone();
+ emit2.move(3, 0, 0);
+
+ rootNode.attachChild(emit);
+ rootNode.attachChild(emit2);
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ BinaryExporter.getInstance().save(emit, out);
+
+ BinaryImporter imp = new BinaryImporter();
+ imp.setAssetManager(assetManager);
+ ParticleEmitter emit3 = (ParticleEmitter) imp.load(out.toByteArray());
+
+ emit3.move(-3, 0, 0);
+ rootNode.attachChild(emit3);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ // Camera cam2 = cam.clone();
+ // cam.setViewPortTop(0.5f);
+ // cam2.setViewPortBottom(0.5f);
+ // ViewPort vp = renderManager.createMainView("SecondView", cam2);
+ // viewPort.setClearEnabled(false);
+ // vp.attachScene(rootNode);
+ }
+
+}
diff --git a/engine/src/test/jme3test/effect/TestPointSprite.java b/engine/src/test/jme3test/effect/TestPointSprite.java
new file mode 100644
index 0000000..77064d2
--- /dev/null
+++ b/engine/src/test/jme3test/effect/TestPointSprite.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.effect;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh.Type;
+import com.jme3.effect.shapes.EmitterBoxShape;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+
+public class TestPointSprite extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestPointSprite app = new TestPointSprite();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ final ParticleEmitter emit = new ParticleEmitter("Emitter", Type.Point, 10000);
+ emit.setShape(new EmitterBoxShape(new Vector3f(-1.8f, -1.8f, -1.8f),
+ new Vector3f(1.8f, 1.8f, 1.8f)));
+ emit.setGravity(0, 0, 0);
+ emit.setLowLife(60);
+ emit.setHighLife(60);
+ emit.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
+ emit.setImagesX(15);
+ emit.setStartSize(0.05f);
+ emit.setEndSize(0.05f);
+ emit.setStartColor(ColorRGBA.White);
+ emit.setEndColor(ColorRGBA.White);
+ emit.setSelectRandomImage(true);
+ emit.emitAllParticles();
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat.setBoolean("PointSprite", true);
+ mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png"));
+ emit.setMaterial(mat);
+
+ rootNode.attachChild(emit);
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if ("setNum".equals(name) && isPressed) {
+ emit.setNumParticles(5000);
+ emit.emitAllParticles();
+ }
+ }
+ }, "setNum");
+
+ inputManager.addMapping("setNum", new KeyTrigger(KeyInput.KEY_SPACE));
+
+ }
+
+}
diff --git a/engine/src/test/jme3test/export/TestAssetLinkNode.java b/engine/src/test/jme3test/export/TestAssetLinkNode.java
new file mode 100644
index 0000000..b15f379
--- /dev/null
+++ b/engine/src/test/jme3test/export/TestAssetLinkNode.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.export;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.ModelKey;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.AssetLinkNode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class TestAssetLinkNode extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestAssetLinkNode app = new TestAssetLinkNode();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ AssetLinkNode loaderNode=new AssetLinkNode();
+ loaderNode.addLinkedChild(new ModelKey("Models/MonkeyHead/MonkeyHead.mesh.xml"));
+ //load/attach the children (happens automatically on load)
+// loaderNode.attachLinkedChildren(assetManager);
+// rootNode.attachChild(loaderNode);
+
+ //save and load the loaderNode
+ try {
+ //export to byte array
+ ByteArrayOutputStream bout=new ByteArrayOutputStream();
+ BinaryExporter.getInstance().save(loaderNode, bout);
+ //import from byte array, automatically loads the monkeyhead from file
+ ByteArrayInputStream bin=new ByteArrayInputStream(bout.toByteArray());
+ BinaryImporter imp=BinaryImporter.getInstance();
+ imp.setAssetManager(assetManager);
+ Node newLoaderNode=(Node)imp.load(bin);
+ //attach to rootNode
+ rootNode.attachChild(newLoaderNode);
+ } catch (IOException ex) {
+ Logger.getLogger(TestAssetLinkNode.class.getName()).log(Level.SEVERE, null, ex);
+ }
+
+
+ rootNode.attachChild(loaderNode);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial( (Material) assetManager.loadAsset(new AssetKey("Common/Materials/RedColor.j3m")));
+ rootNode.attachChild(lightMdl);
+
+ // flourescent main light
+ pl = new PointLight();
+ pl.setColor(new ColorRGBA(0.88f, 0.92f, 0.95f, 1.0f));
+ rootNode.addLight(pl);
+
+ // sunset light
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f,-0.7f,1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.44f, 0.30f, 0.20f, 1.0f));
+ rootNode.addLight(dl);
+
+ // skylight
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.6f,-1,-0.6f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.10f, 0.22f, 0.44f, 1.0f));
+ rootNode.addLight(dl);
+
+ // white ambient light
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, -0.5f,-0.1f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.50f, 0.40f, 0.50f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 6f, 3f, FastMath.sin(angle) * 6f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/export/TestOgreConvert.java b/engine/src/test/jme3test/export/TestOgreConvert.java
new file mode 100644
index 0000000..0b00a73
--- /dev/null
+++ b/engine/src/test/jme3test/export/TestOgreConvert.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.export;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.app.SimpleApplication;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class TestOgreConvert extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestOgreConvert app = new TestOgreConvert();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Spatial ogreModel = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White);
+ dl.setDirection(new Vector3f(0,-1,-1).normalizeLocal());
+ rootNode.addLight(dl);
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ BinaryExporter exp = new BinaryExporter();
+ exp.save(ogreModel, baos);
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ BinaryImporter imp = new BinaryImporter();
+ imp.setAssetManager(assetManager);
+ Node ogreModelReloaded = (Node) imp.load(bais, null, null);
+
+ AnimControl control = ogreModelReloaded.getControl(AnimControl.class);
+ AnimChannel chan = control.createChannel();
+ chan.setAnim("Walk");
+
+ rootNode.attachChild(ogreModelReloaded);
+ } catch (IOException ex){
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/games/CubeField.java b/engine/src/test/jme3test/games/CubeField.java
new file mode 100644
index 0000000..ec215b0
--- /dev/null
+++ b/engine/src/test/jme3test/games/CubeField.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.games;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Dome;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Kyle "bonechilla" Williams
+ */
+public class CubeField extends SimpleApplication implements AnalogListener {
+
+ public static void main(String[] args) {
+ CubeField app = new CubeField();
+ app.start();
+ }
+
+ private BitmapFont defaultFont;
+
+ private boolean START;
+ private int difficulty, Score, colorInt, highCap, lowCap,diffHelp;
+ private Node player;
+ private Geometry fcube;
+ private ArrayList<Geometry> cubeField;
+ private ArrayList<ColorRGBA> obstacleColors;
+ private float speed, coreTime,coreTime2;
+ private float camAngle = 0;
+ private BitmapText fpsScoreText, pressStart;
+
+ private boolean solidBox = true;
+ private Material playerMaterial;
+ private Material floorMaterial;
+
+ private float fpsRate = 1000f / 1f;
+
+ /**
+ * Initializes game
+ */
+ @Override
+ public void simpleInitApp() {
+ Logger.getLogger("com.jme3").setLevel(Level.WARNING);
+
+ flyCam.setEnabled(false);
+ setDisplayStatView(false);
+
+ Keys();
+
+ defaultFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ pressStart = new BitmapText(defaultFont, false);
+ fpsScoreText = new BitmapText(defaultFont, false);
+
+ loadText(fpsScoreText, "Current Score: 0", defaultFont, 0, 2, 0);
+ loadText(pressStart, "PRESS ENTER", defaultFont, 0, 5, 0);
+
+ player = createPlayer();
+ rootNode.attachChild(player);
+ cubeField = new ArrayList<Geometry>();
+ obstacleColors = new ArrayList<ColorRGBA>();
+
+ gameReset();
+ }
+ /**
+ * Used to reset cubeField
+ */
+ private void gameReset(){
+ Score = 0;
+ lowCap = 10;
+ colorInt = 0;
+ highCap = 40;
+ difficulty = highCap;
+
+ for (Geometry cube : cubeField){
+ cube.removeFromParent();
+ }
+ cubeField.clear();
+
+ if (fcube != null){
+ fcube.removeFromParent();
+ }
+ fcube = createFirstCube();
+
+ obstacleColors.clear();
+ obstacleColors.add(ColorRGBA.Orange);
+ obstacleColors.add(ColorRGBA.Red);
+ obstacleColors.add(ColorRGBA.Yellow);
+ renderer.setBackgroundColor(ColorRGBA.White);
+ speed = lowCap / 400f;
+ coreTime = 20.0f;
+ coreTime2 = 10.0f;
+ diffHelp=lowCap;
+ player.setLocalTranslation(0,0,0);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ camTakeOver(tpf);
+ if (START){
+ gameLogic(tpf);
+ }
+ colorLogic();
+ }
+ /**
+ * Forcefully takes over Camera adding functionality and placing it behind the character
+ * @param tpf Tickes Per Frame
+ */
+ private void camTakeOver(float tpf) {
+ cam.setLocation(player.getLocalTranslation().add(-8, 2, 0));
+ cam.lookAt(player.getLocalTranslation(), Vector3f.UNIT_Y);
+
+ Quaternion rot = new Quaternion();
+ rot.fromAngleNormalAxis(camAngle, Vector3f.UNIT_Z);
+ cam.setRotation(cam.getRotation().mult(rot));
+ camAngle *= FastMath.pow(.99f, fpsRate * tpf);
+ }
+
+ @Override
+ public void requestClose(boolean esc) {
+ if (!esc){
+ System.out.println("The game was quit.");
+ }else{
+ System.out.println("Player has Collided. Final Score is " + Score);
+ }
+ context.destroy(false);
+ }
+ /**
+ * Randomly Places a cube on the map between 30 and 90 paces away from player
+ */
+ private void randomizeCube() {
+ Geometry cube = fcube.clone();
+ int playerX = (int) player.getLocalTranslation().getX();
+ int playerZ = (int) player.getLocalTranslation().getZ();
+// float x = FastMath.nextRandomInt(playerX + difficulty + 10, playerX + difficulty + 150);
+ float x = FastMath.nextRandomInt(playerX + difficulty + 30, playerX + difficulty + 90);
+ float z = FastMath.nextRandomInt(playerZ - difficulty - 50, playerZ + difficulty + 50);
+ cube.getLocalTranslation().set(x, 0, z);
+
+// playerX+difficulty+30,playerX+difficulty+90
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ if (!solidBox){
+ mat.getAdditionalRenderState().setWireframe(true);
+ }
+ mat.setColor("Color", obstacleColors.get(FastMath.nextRandomInt(0, obstacleColors.size() - 1)));
+ cube.setMaterial(mat);
+
+ rootNode.attachChild(cube);
+ cubeField.add(cube);
+ }
+
+ private Geometry createFirstCube() {
+ Vector3f loc = player.getLocalTranslation();
+ loc.addLocal(4, 0, 0);
+ Box b = new Box(loc, 1, 1, 1);
+
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+ geom.setMaterial(mat);
+
+ return geom;
+ }
+
+ private Node createPlayer() {
+ Dome b = new Dome(Vector3f.ZERO, 10, 100, 1);
+ Geometry playerMesh = new Geometry("Box", b);
+
+ playerMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ playerMaterial.setColor("Color", ColorRGBA.Red);
+ playerMesh.setMaterial(playerMaterial);
+ playerMesh.setName("player");
+
+ Box floor = new Box(Vector3f.ZERO.add(playerMesh.getLocalTranslation().getX(),
+ playerMesh.getLocalTranslation().getY() - 1, 0), 100, 0, 100);
+ Geometry floorMesh = new Geometry("Box", floor);
+
+ floorMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ floorMaterial.setColor("Color", ColorRGBA.LightGray);
+ floorMesh.setMaterial(floorMaterial);
+ floorMesh.setName("floor");
+
+ Node playerNode = new Node();
+ playerNode.attachChild(playerMesh);
+ playerNode.attachChild(floorMesh);
+
+ return playerNode;
+ }
+
+ /**
+ * If Game is Lost display Score and Reset the Game
+ */
+ private void gameLost(){
+ START = false;
+ loadText(pressStart, "You lost! Press enter to try again.", defaultFont, 0, 5, 0);
+ gameReset();
+ }
+
+ /**
+ * Core Game Logic
+ */
+ private void gameLogic(float tpf){
+ //Subtract difficulty level in accordance to speed every 10 seconds
+ if(timer.getTimeInSeconds()>=coreTime2){
+ coreTime2=timer.getTimeInSeconds()+10;
+ if(difficulty<=lowCap){
+ difficulty=lowCap;
+ }
+ else if(difficulty>lowCap){
+ difficulty-=5;
+ diffHelp+=1;
+ }
+ }
+
+ if(speed<.1f){
+ speed+=.000001f*tpf*fpsRate;
+ }
+
+ player.move(speed * tpf * fpsRate, 0, 0);
+ if (cubeField.size() > difficulty){
+ cubeField.remove(0);
+ }else if (cubeField.size() != difficulty){
+ randomizeCube();
+ }
+
+ if (cubeField.isEmpty()){
+ requestClose(false);
+ }else{
+ for (int i = 0; i < cubeField.size(); i++){
+
+ //better way to check collision
+ Geometry playerModel = (Geometry) player.getChild(0);
+ Geometry cubeModel = cubeField.get(i);
+ cubeModel.updateGeometricState();
+
+ BoundingVolume pVol = playerModel.getWorldBound();
+ BoundingVolume vVol = cubeModel.getWorldBound();
+
+ if (pVol.intersects(vVol)){
+ gameLost();
+ return;
+ }
+ //Remove cube if 10 world units behind player
+ if (cubeField.get(i).getLocalTranslation().getX() + 10 < player.getLocalTranslation().getX()){
+ cubeField.get(i).removeFromParent();
+ cubeField.remove(cubeField.get(i));
+ }
+
+ }
+ }
+
+ Score += fpsRate * tpf;
+ fpsScoreText.setText("Current Score: "+Score);
+ }
+ /**
+ * Sets up the keyboard bindings
+ */
+ private void Keys() {
+ inputManager.addMapping("START", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT));
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT));
+ inputManager.addListener(this, "START", "Left", "Right");
+ }
+
+ public void onAnalog(String binding, float value, float tpf) {
+ if (binding.equals("START") && !START){
+ START = true;
+ guiNode.detachChild(pressStart);
+ System.out.println("START");
+ }else if (START == true && binding.equals("Left")){
+ player.move(0, 0, -(speed / 2f) * value * fpsRate);
+ camAngle -= value*tpf;
+ }else if (START == true && binding.equals("Right")){
+ player.move(0, 0, (speed / 2f) * value * fpsRate);
+ camAngle += value*tpf;
+ }
+ }
+
+ /**
+ * Determines the colors of the player, floor, obstacle and background
+ */
+ private void colorLogic() {
+ if (timer.getTimeInSeconds() >= coreTime){
+
+ colorInt++;
+ coreTime = timer.getTimeInSeconds() + 20;
+
+
+ switch (colorInt){
+ case 1:
+ obstacleColors.clear();
+ solidBox = false;
+ obstacleColors.add(ColorRGBA.Green);
+ renderer.setBackgroundColor(ColorRGBA.Black);
+ playerMaterial.setColor("Color", ColorRGBA.White);
+ floorMaterial.setColor("Color", ColorRGBA.Black);
+ break;
+ case 2:
+ obstacleColors.set(0, ColorRGBA.Black);
+ solidBox = true;
+ renderer.setBackgroundColor(ColorRGBA.White);
+ playerMaterial.setColor("Color", ColorRGBA.Gray);
+ floorMaterial.setColor("Color", ColorRGBA.LightGray);
+ break;
+ case 3:
+ obstacleColors.set(0, ColorRGBA.Pink);
+ break;
+ case 4:
+ obstacleColors.set(0, ColorRGBA.Cyan);
+ obstacleColors.add(ColorRGBA.Magenta);
+ renderer.setBackgroundColor(ColorRGBA.Gray);
+ floorMaterial.setColor("Color", ColorRGBA.Gray);
+ playerMaterial.setColor("Color", ColorRGBA.White);
+ break;
+ case 5:
+ obstacleColors.remove(0);
+ renderer.setBackgroundColor(ColorRGBA.Pink);
+ solidBox = false;
+ playerMaterial.setColor("Color", ColorRGBA.White);
+ break;
+ case 6:
+ obstacleColors.set(0, ColorRGBA.White);
+ solidBox = true;
+ renderer.setBackgroundColor(ColorRGBA.Black);
+ playerMaterial.setColor("Color", ColorRGBA.Gray);
+ floorMaterial.setColor("Color", ColorRGBA.LightGray);
+ break;
+ case 7:
+ obstacleColors.set(0, ColorRGBA.Green);
+ renderer.setBackgroundColor(ColorRGBA.Gray);
+ playerMaterial.setColor("Color", ColorRGBA.Black);
+ floorMaterial.setColor("Color", ColorRGBA.Orange);
+ break;
+ case 8:
+ obstacleColors.set(0, ColorRGBA.Red);
+ floorMaterial.setColor("Color", ColorRGBA.Pink);
+ break;
+ case 9:
+ obstacleColors.set(0, ColorRGBA.Orange);
+ obstacleColors.add(ColorRGBA.Red);
+ obstacleColors.add(ColorRGBA.Yellow);
+ renderer.setBackgroundColor(ColorRGBA.White);
+ playerMaterial.setColor("Color", ColorRGBA.Red);
+ floorMaterial.setColor("Color", ColorRGBA.Gray);
+ colorInt=0;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ /**
+ * Sets up a BitmapText to be displayed
+ * @param txt the Bitmap Text
+ * @param text the
+ * @param font the font of the text
+ * @param x
+ * @param y
+ * @param z
+ */
+ private void loadText(BitmapText txt, String text, BitmapFont font, float x, float y, float z) {
+ txt.setSize(font.getCharSet().getRenderedSize());
+ txt.setLocalTranslation(txt.getLineWidth() * x, txt.getLineHeight() * y, z);
+ txt.setText(text);
+ guiNode.attachChild(txt);
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/gui/TestBitmapFont.java b/engine/src/test/jme3test/gui/TestBitmapFont.java
new file mode 100644
index 0000000..088046e
--- /dev/null
+++ b/engine/src/test/jme3test/gui/TestBitmapFont.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.gui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.font.LineWrapMode;
+import com.jme3.font.Rectangle;
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.event.*;
+
+public class TestBitmapFont extends SimpleApplication {
+
+ private String txtB =
+ "ABCDEFGHIKLMNOPQRSTUVWXYZ1234567 890`~!@#$%^&*()-=_+[]\\;',./{}|:<>?";
+
+ private BitmapText txt;
+ private BitmapText txt2;
+ private BitmapText txt3;
+
+ public static void main(String[] args){
+ TestBitmapFont app = new TestBitmapFont();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ inputManager.addMapping("WordWrap", new KeyTrigger(KeyInput.KEY_TAB));
+ inputManager.addListener(keyListener, "WordWrap");
+ inputManager.addRawInputListener(textListener);
+
+ BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ txt = new BitmapText(fnt, false);
+ txt.setBox(new Rectangle(0, 0, settings.getWidth(), settings.getHeight()));
+ txt.setSize(fnt.getPreferredSize() * 2f);
+ txt.setText(txtB);
+ txt.setLocalTranslation(0, txt.getHeight(), 0);
+ guiNode.attachChild(txt);
+
+ txt2 = new BitmapText(fnt, false);
+ txt2.setSize(fnt.getPreferredSize() * 1.2f);
+ txt2.setText("Text without restriction. \nText without restriction. Text without restriction. Text without restriction");
+ txt2.setLocalTranslation(0, txt2.getHeight(), 0);
+ guiNode.attachChild(txt2);
+
+ txt3 = new BitmapText(fnt, false);
+ txt3.setBox(new Rectangle(0, 0, settings.getWidth(), 0));
+ txt3.setText("Press Tab to toggle word-wrap. type text and enter to input text");
+ txt3.setLocalTranslation(0, settings.getHeight()/2, 0);
+ guiNode.attachChild(txt3);
+ }
+
+ private ActionListener keyListener = new ActionListener() {
+ @Override
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("WordWrap") && !isPressed) {
+ txt.setLineWrapMode( txt.getLineWrapMode() == LineWrapMode.Word ?
+ LineWrapMode.NoWrap : LineWrapMode.Word );
+ }
+ }
+ };
+
+ private RawInputListener textListener = new RawInputListener() {
+ private StringBuilder str = new StringBuilder();
+
+ @Override
+ public void onMouseMotionEvent(MouseMotionEvent evt) { }
+
+ @Override
+ public void onMouseButtonEvent(MouseButtonEvent evt) { }
+
+ @Override
+ public void onKeyEvent(KeyInputEvent evt) {
+ if (evt.isReleased())
+ return;
+ if (evt.getKeyChar() == '\n' || evt.getKeyChar() == '\r') {
+ txt3.setText(str.toString());
+ str.setLength(0);
+ } else {
+ str.append(evt.getKeyChar());
+ }
+ }
+
+ @Override
+ public void onJoyButtonEvent(JoyButtonEvent evt) { }
+
+ @Override
+ public void onJoyAxisEvent(JoyAxisEvent evt) { }
+
+ @Override
+ public void endInput() { }
+
+ @Override
+ public void beginInput() { }
+
+ @Override
+ public void onTouchEvent(TouchEvent evt) { }
+ };
+
+}
diff --git a/engine/src/test/jme3test/gui/TestBitmapText3D.java b/engine/src/test/jme3test/gui/TestBitmapText3D.java
new file mode 100644
index 0000000..e8c44c0
--- /dev/null
+++ b/engine/src/test/jme3test/gui/TestBitmapText3D.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.gui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.font.Rectangle;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+
+public class TestBitmapText3D extends SimpleApplication {
+
+ private String txtB =
+ "ABCDEFGHIKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()-=_+[]\\;',./{}|:<>?";
+
+ public static void main(String[] args){
+ TestBitmapText3D app = new TestBitmapText3D();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Quad q = new Quad(6, 3);
+ Geometry g = new Geometry("quad", q);
+ g.setLocalTranslation(0, -3, -0.0001f);
+ g.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(g);
+
+ BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText txt = new BitmapText(fnt, false);
+ txt.setBox(new Rectangle(0, 0, 6, 3));
+ txt.setQueueBucket(Bucket.Transparent);
+ txt.setSize( 0.5f );
+ txt.setText(txtB);
+ rootNode.attachChild(txt);
+ }
+
+}
diff --git a/engine/src/test/jme3test/gui/TestOrtho.java b/engine/src/test/jme3test/gui/TestOrtho.java
new file mode 100644
index 0000000..83a1517
--- /dev/null
+++ b/engine/src/test/jme3test/gui/TestOrtho.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.gui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.ui.Picture;
+
+public class TestOrtho extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestOrtho app = new TestOrtho();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Picture p = new Picture("Picture");
+ p.move(0, 0, -1); // make it appear behind stats view
+ p.setPosition(0, 0);
+ p.setWidth(settings.getWidth());
+ p.setHeight(settings.getHeight());
+ p.setImage(assetManager, "Interface/Logo/Monkey.png", false);
+
+ // attach geometry to orthoNode
+ guiNode.attachChild(p);
+ }
+
+}
diff --git a/engine/src/test/jme3test/gui/TestSoftwareMouse.java b/engine/src/test/jme3test/gui/TestSoftwareMouse.java
new file mode 100644
index 0000000..32c6801
--- /dev/null
+++ b/engine/src/test/jme3test/gui/TestSoftwareMouse.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.gui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.*;
+import com.jme3.math.FastMath;
+import com.jme3.system.AppSettings;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+
+public class TestSoftwareMouse extends SimpleApplication {
+
+ private Picture cursor;
+
+ private RawInputListener inputListener = new RawInputListener() {
+
+ private float x = 0, y = 0;
+
+ public void beginInput() {
+ }
+ public void endInput() {
+ }
+ public void onJoyAxisEvent(JoyAxisEvent evt) {
+ }
+ public void onJoyButtonEvent(JoyButtonEvent evt) {
+ }
+ public void onMouseMotionEvent(MouseMotionEvent evt) {
+ x += evt.getDX();
+ y += evt.getDY();
+
+ // Prevent mouse from leaving screen
+ AppSettings settings = TestSoftwareMouse.this.settings;
+ x = FastMath.clamp(x, 0, settings.getWidth());
+ y = FastMath.clamp(y, 0, settings.getHeight());
+
+ // adjust for hotspot
+ cursor.setPosition(x, y - 64);
+ }
+ public void onMouseButtonEvent(MouseButtonEvent evt) {
+ }
+ public void onKeyEvent(KeyInputEvent evt) {
+ }
+ public void onTouchEvent(TouchEvent evt) {
+ }
+ };
+
+ public static void main(String[] args){
+ TestSoftwareMouse app = new TestSoftwareMouse();
+
+// AppSettings settings = new AppSettings(true);
+// settings.setFrameRate(60);
+// app.setSettings(settings);
+
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setEnabled(false);
+// inputManager.setCursorVisible(false);
+
+ Texture tex = assetManager.loadTexture("Interface/Logo/Cursor.png");
+
+ cursor = new Picture("cursor");
+ cursor.setTexture(assetManager, (Texture2D) tex, true);
+ cursor.setWidth(64);
+ cursor.setHeight(64);
+ guiNode.attachChild(cursor);
+
+ inputManager.addRawInputListener(inputListener);
+
+// Image img = tex.getImage();
+// ByteBuffer data = img.getData(0);
+// IntBuffer image = BufferUtils.createIntBuffer(64 * 64);
+// for (int y = 0; y < 64; y++){
+// for (int x = 0; x < 64; x++){
+// int rgba = data.getInt();
+// image.put(rgba);
+// }
+// }
+// image.clear();
+//
+// try {
+// Cursor cur = new Cursor(64, 64, 2, 62, 1, image, null);
+// Mouse.setNativeCursor(cur);
+// } catch (LWJGLException ex) {
+// Logger.getLogger(TestSoftwareMouse.class.getName()).log(Level.SEVERE, null, ex);
+// }
+ }
+}
diff --git a/engine/src/test/jme3test/gui/TestZOrder.java b/engine/src/test/jme3test/gui/TestZOrder.java
new file mode 100644
index 0000000..bc4bd8c
--- /dev/null
+++ b/engine/src/test/jme3test/gui/TestZOrder.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.gui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.ui.Picture;
+
+public class TestZOrder extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestZOrder app = new TestZOrder();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Picture p = new Picture("Picture1");
+ p.move(0,0,-1);
+ p.setPosition(100, 100);
+ p.setWidth(100);
+ p.setHeight(100);
+ p.setImage(assetManager, "Interface/Logo/Monkey.png", false);
+ guiNode.attachChild(p);
+
+ Picture p2 = new Picture("Picture2");
+ p2.move(0,0,1.001f);
+ p2.setPosition(150, 150);
+ p2.setWidth(100);
+ p2.setHeight(100);
+ p2.setImage(assetManager, "Interface/Logo/Monkey.png", false);
+ guiNode.attachChild(p2);
+ }
+
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloAnimation.java b/engine/src/test/jme3test/helloworld/HelloAnimation.java
new file mode 100644
index 0000000..101796b
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloAnimation.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.AnimEventListener;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+
+/** Sample 7 - how to load an OgreXML model and play an animation,
+ * using channels, a controller, and an AnimEventListener. */
+public class HelloAnimation extends SimpleApplication
+ implements AnimEventListener {
+
+ Node player;
+ private AnimChannel channel;
+ private AnimControl control;
+
+ public static void main(String[] args) {
+ HelloAnimation app = new HelloAnimation();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ viewPort.setBackgroundColor(ColorRGBA.LightGray);
+ initKeys();
+
+ /** Add a light source so we can see the model */
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
+ rootNode.addLight(dl);
+
+ /** Load a model that contains animation */
+ player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ player.setLocalScale(0.5f);
+ rootNode.attachChild(player);
+
+ /** Create a controller and channels. */
+ control = player.getControl(AnimControl.class);
+ control.addListener(this);
+ channel = control.createChannel();
+ channel.setAnim("stand");
+ }
+
+ /** Use this listener to trigger something after an animation is done. */
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+ if (animName.equals("Walk")) {
+ /** After "walk", reset to "stand". */
+ channel.setAnim("stand", 0.50f);
+ channel.setLoopMode(LoopMode.DontLoop);
+ channel.setSpeed(1f);
+ }
+ }
+
+ /** Use this listener to trigger something between two animations. */
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+ // unused
+ }
+
+ /** Custom Keybindings: Mapping a named action to a key input. */
+ private void initKeys() {
+ inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(actionListener, "Walk");
+ }
+
+ /** Definining the named action that can be triggered by key inputs. */
+ private ActionListener actionListener = new ActionListener() {
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("Walk") && !keyPressed) {
+ if (!channel.getAnimationName().equals("Walk")) {
+ /** Play the "walk" animation! */
+ channel.setAnim("Walk", 0.50f);
+ channel.setLoopMode(LoopMode.Loop);
+ }
+ }
+ }
+ };
+
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloAssets.java b/engine/src/test/jme3test/helloworld/HelloAssets.java
new file mode 100644
index 0000000..7635abb
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloAssets.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+
+/** Sample 3 - how to load an OBJ model, and OgreXML model,
+ * a material/texture, or text. */
+public class HelloAssets extends SimpleApplication {
+
+ public static void main(String[] args) {
+ HelloAssets app = new HelloAssets();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ /** Load a teapot model (OBJ file from test-data) */
+ Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ Material mat_default = new Material( assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+ teapot.setMaterial(mat_default);
+ rootNode.attachChild(teapot);
+
+ /** Create a wall (Box with material and texture from test-data) */
+ Box box = new Box(Vector3f.ZERO, 2.5f,2.5f,1.0f);
+ Spatial wall = new Geometry("Box", box );
+ Material mat_brick = new Material( assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat_brick.setTexture("ColorMap", assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"));
+ wall.setMaterial(mat_brick);
+ wall.setLocalTranslation(2.0f,-2.5f,0.0f);
+ rootNode.attachChild(wall);
+
+ /** Display a line of text (default font from test-data) */
+ guiNode.detachAllChildren();
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText helloText = new BitmapText(guiFont, false);
+ helloText.setSize(guiFont.getCharSet().getRenderedSize());
+ helloText.setText("Hello World");
+ helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
+ guiNode.attachChild(helloText);
+
+ /** Load a Ninja model (OgreXML + material + texture from test_data) */
+ Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+ ninja.scale(0.05f, 0.05f, 0.05f);
+ ninja.rotate(0.0f, -3.0f, 0.0f);
+ ninja.setLocalTranslation(0.0f, -5.0f, -2.0f);
+ rootNode.attachChild(ninja);
+ /** You must add a light to make the model visible */
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());
+ rootNode.addLight(sun);
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloAudio.java b/engine/src/test/jme3test/helloworld/HelloAudio.java
new file mode 100644
index 0000000..b614396
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloAudio.java
@@ -0,0 +1,84 @@
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+/** Sample 11 - playing 3D audio. */
+public class HelloAudio extends SimpleApplication {
+
+ private AudioNode audio_gun;
+ private AudioNode audio_nature;
+ private Geometry player;
+
+ public static void main(String[] args) {
+ HelloAudio app = new HelloAudio();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(40);
+
+ /** just a blue box floating in space */
+ Box box1 = new Box(Vector3f.ZERO, 1, 1, 1);
+ player = new Geometry("Player", box1);
+ Material mat1 = new Material(assetManager,
+ "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.Blue);
+ player.setMaterial(mat1);
+ rootNode.attachChild(player);
+
+ /** custom init methods, see below */
+ initKeys();
+ initAudio();
+ }
+
+ /** We create two audio nodes. */
+ private void initAudio() {
+ /* gun shot sound is to be triggered by a mouse click. */
+ audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
+ audio_gun.setLooping(false);
+ audio_gun.setVolume(2);
+ rootNode.attachChild(audio_gun);
+
+ /* nature sound - keeps playing in a loop. */
+ audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true);
+ audio_nature.setLooping(true); // activate continuous playing
+ audio_nature.setPositional(true);
+ audio_nature.setLocalTranslation(Vector3f.ZERO.clone());
+ audio_nature.setVolume(3);
+ rootNode.attachChild(audio_nature);
+ audio_nature.play(); // play continuously!
+ }
+
+ /** Declaring "Shoot" action, mapping it to a trigger (mouse click). */
+ private void initKeys() {
+ inputManager.addMapping("Shoot", new MouseButtonTrigger(0));
+ inputManager.addListener(actionListener, "Shoot");
+ }
+
+ /** Defining the "Shoot" action: Play a gun sound. */
+ private ActionListener actionListener = new ActionListener() {
+ @Override
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("Shoot") && !keyPressed) {
+ audio_gun.playInstance(); // play each instance once!
+ }
+ }
+ };
+
+ /** Move the listener with the a camera - for 3D audio. */
+ @Override
+ public void simpleUpdate(float tpf) {
+ listener.setLocation(cam.getLocation());
+ listener.setRotation(cam.getRotation());
+ }
+
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloCollision.java b/engine/src/test/jme3test/helloworld/HelloCollision.java
new file mode 100644
index 0000000..35d91ed
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloCollision.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+/**
+ * Example 9 - How to make walls and floors solid.
+ * This collision code uses Physics and a custom Action Listener.
+ * @author normen, with edits by Zathras
+ */
+public class HelloCollision extends SimpleApplication
+ implements ActionListener {
+
+ private Spatial sceneModel;
+ private BulletAppState bulletAppState;
+ private RigidBodyControl landscape;
+ private CharacterControl player;
+ private Vector3f walkDirection = new Vector3f();
+ private boolean left = false, right = false, up = false, down = false;
+
+ public static void main(String[] args) {
+ HelloCollision app = new HelloCollision();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ /** Set up Physics */
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+
+ // We re-use the flyby camera for rotation, while positioning is handled by physics
+ viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+ flyCam.setMoveSpeed(100);
+ setUpKeys();
+ setUpLight();
+
+ // We load the scene from the zip file and adjust its size.
+ assetManager.registerLocator("town.zip", ZipLocator.class.getName());
+ sceneModel = assetManager.loadModel("main.scene");
+ sceneModel.setLocalScale(2f);
+
+ // We set up collision detection for the scene by creating a
+ // compound collision shape and a static RigidBodyControl with mass zero.
+ CollisionShape sceneShape =
+ CollisionShapeFactory.createMeshShape((Node) sceneModel);
+ landscape = new RigidBodyControl(sceneShape, 0);
+ sceneModel.addControl(landscape);
+
+ // We set up collision detection for the player by creating
+ // a capsule collision shape and a CharacterControl.
+ // The CharacterControl offers extra settings for
+ // size, stepheight, jumping, falling, and gravity.
+ // We also put the player in its starting position.
+ CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
+ player = new CharacterControl(capsuleShape, 0.05f);
+ player.setJumpSpeed(20);
+ player.setFallSpeed(30);
+ player.setGravity(30);
+ player.setPhysicsLocation(new Vector3f(0, 10, 0));
+
+ // We attach the scene and the player to the rootnode and the physics space,
+ // to make them appear in the game world.
+ rootNode.attachChild(sceneModel);
+ bulletAppState.getPhysicsSpace().add(landscape);
+ bulletAppState.getPhysicsSpace().add(player);
+ }
+
+ private void setUpLight() {
+ // We add light so we see the scene
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White.mult(1.3f));
+ rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White);
+ dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
+ rootNode.addLight(dl);
+ }
+
+ /** We over-write some navigational key mappings here, so we can
+ * add physics-controlled walking and jumping: */
+ private void setUpKeys() {
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
+ inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
+ inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this, "Left");
+ inputManager.addListener(this, "Right");
+ inputManager.addListener(this, "Up");
+ inputManager.addListener(this, "Down");
+ inputManager.addListener(this, "Jump");
+ }
+
+ /** These are our custom actions triggered by key presses.
+ * We do not walk yet, we just keep track of the direction the user pressed. */
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Left")) {
+ if (value) { left = true; } else { left = false; }
+ } else if (binding.equals("Right")) {
+ if (value) { right = true; } else { right = false; }
+ } else if (binding.equals("Up")) {
+ if (value) { up = true; } else { up = false; }
+ } else if (binding.equals("Down")) {
+ if (value) { down = true; } else { down = false; }
+ } else if (binding.equals("Jump")) {
+ player.jump();
+ }
+ }
+
+ /**
+ * This is the main event loop--walking happens here.
+ * We check in which direction the player is walking by interpreting
+ * the camera direction forward (camDir) and to the side (camLeft).
+ * The setWalkDirection() command is what lets a physics-controlled player walk.
+ * We also make sure here that the camera moves with player.
+ */
+ @Override
+ public void simpleUpdate(float tpf) {
+ Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
+ walkDirection.set(0, 0, 0);
+ if (left) { walkDirection.addLocal(camLeft); }
+ if (right) { walkDirection.addLocal(camLeft.negate()); }
+ if (up) { walkDirection.addLocal(camDir); }
+ if (down) { walkDirection.addLocal(camDir.negate()); }
+ player.setWalkDirection(walkDirection);
+ cam.setLocation(player.getPhysicsLocation());
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloEffects.java b/engine/src/test/jme3test/helloworld/HelloEffects.java
new file mode 100644
index 0000000..70e2e78
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloEffects.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+
+/** Sample 11 - how to create fire, water, and explosion effects. */
+public class HelloEffects extends SimpleApplication {
+
+ public static void main(String[] args) {
+ HelloEffects app = new HelloEffects();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ ParticleEmitter fire =
+ new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
+ Material mat_red = new Material(assetManager,
+ "Common/MatDefs/Misc/Particle.j3md");
+ mat_red.setTexture("Texture", assetManager.loadTexture(
+ "Effects/Explosion/flame.png"));
+ fire.setMaterial(mat_red);
+ fire.setImagesX(2);
+ fire.setImagesY(2); // 2x2 texture animation
+ fire.setEndColor( new ColorRGBA(1f, 0f, 0f, 1f)); // red
+ fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+ fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
+ fire.setStartSize(1.5f);
+ fire.setEndSize(0.1f);
+ fire.setGravity(0, 0, 0);
+ fire.setLowLife(1f);
+ fire.setHighLife(3f);
+ fire.getParticleInfluencer().setVelocityVariation(0.3f);
+ rootNode.attachChild(fire);
+
+ ParticleEmitter debris =
+ new ParticleEmitter("Debris", ParticleMesh.Type.Triangle, 10);
+ Material debris_mat = new Material(assetManager,
+ "Common/MatDefs/Misc/Particle.j3md");
+ debris_mat.setTexture("Texture", assetManager.loadTexture(
+ "Effects/Explosion/Debris.png"));
+ debris.setMaterial(debris_mat);
+ debris.setImagesX(3);
+ debris.setImagesY(3); // 3x3 texture animation
+ debris.setSelectRandomImage(true);
+ debris.setRotateSpeed(4);
+ debris.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 4, 0));
+ debris.setStartColor(ColorRGBA.White);
+ debris.setGravity(0, 6, 0);
+ debris.getParticleInfluencer().setVelocityVariation(.60f);
+ rootNode.attachChild(debris);
+ debris.emitAllParticles();
+
+// ParticleEmitter water =
+// new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 20);
+// Material mat_blue = new Material(assetManager,
+// "Common/MatDefs/Misc/Particle.j3md");
+// mat_blue.setTexture("Texture", assetManager.loadTexture(
+// "Effects/Explosion/flame.png"));
+// water.setMaterial(mat_blue);
+// water.setImagesX(2);
+// water.setImagesY(2); // 2x2 texture animation
+// water.setStartColor( ColorRGBA.Blue);
+// water.setEndColor( ColorRGBA.Cyan);
+// water.getParticleInfluencer().setInitialVelocity(new Vector3f(0, -4, 0));
+// water.setStartSize(1f);
+// water.setEndSize(1.5f);
+// water.setGravity(0,1,0);
+// water.setLowLife(1f);
+// water.setHighLife(1f);
+// water.getParticleInfluencer().setVelocityVariation(0.1f);
+// water.setLocalTranslation(0, 6, 0);
+// rootNode.attachChild(water);
+
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloInput.java b/engine/src/test/jme3test/helloworld/HelloInput.java
new file mode 100644
index 0000000..c749c96
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloInput.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+/** Sample 5 - how to map keys and mousebuttons to actions */
+public class HelloInput extends SimpleApplication {
+
+ public static void main(String[] args) {
+ HelloInput app = new HelloInput();
+ app.start();
+ }
+ protected Geometry player;
+ Boolean isRunning=true;
+
+ @Override
+ public void simpleInitApp() {
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ player = new Geometry("Player", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+ player.setMaterial(mat);
+ rootNode.attachChild(player);
+ initKeys(); // load my custom keybinding
+ }
+
+ /** Custom Keybinding: Map named actions to inputs. */
+ private void initKeys() {
+ /** You can map one or several inputs to one named mapping. */
+ inputManager.addMapping("Pause", new KeyTrigger(keyInput.KEY_P));
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE), // spacebar!
+ new MouseButtonTrigger(MouseInput.BUTTON_LEFT) ); // left click!
+ /** Add the named mappings to the action listeners. */
+ inputManager.addListener(actionListener, new String[]{"Pause"});
+ inputManager.addListener(analogListener, new String[]{"Left", "Right", "Rotate"});
+ }
+
+ /** Use this listener for KeyDown/KeyUp events */
+ private ActionListener actionListener = new ActionListener() {
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("Pause") && !keyPressed) {
+ isRunning = !isRunning;
+ }
+ }
+ };
+
+ /** Use this listener for continuous events */
+ private AnalogListener analogListener = new AnalogListener() {
+ public void onAnalog(String name, float value, float tpf) {
+ if (isRunning) {
+ if (name.equals("Rotate")) {
+ player.rotate(0, value, 0);
+ }
+ if (name.equals("Right")) {
+ player.move((new Vector3f(value, 0,0)) );
+ }
+ if (name.equals("Left")) {
+ player.move(new Vector3f(-value, 0,0));
+ }
+ } else {
+ System.out.println("Press P to unpause.");
+ }
+ }
+ };
+
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloJME3.java b/engine/src/test/jme3test/helloworld/HelloJME3.java
new file mode 100644
index 0000000..3be80f2
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloJME3.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+/** Sample 1 - how to get started with the most simple JME 3 application.
+ * Display a blue 3D cube and view from all sides by
+ * moving the mouse and pressing the WASD keys. */
+public class HelloJME3 extends SimpleApplication {
+
+ public static void main(String[] args){
+ HelloJME3 app = new HelloJME3();
+ app.start(); // start the game
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1); // create cube shape at the origin
+ Geometry geom = new Geometry("Box", b); // create cube geometry from the shape
+ Material mat = new Material(assetManager,
+ "Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material
+ mat.setColor("Color", ColorRGBA.Blue); // set color of material to blue
+ geom.setMaterial(mat); // set the cube's material
+ rootNode.attachChild(geom); // make the cube appear in the scene
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/helloworld/HelloLoop.java b/engine/src/test/jme3test/helloworld/HelloLoop.java
new file mode 100644
index 0000000..0aa73dd
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloLoop.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+/** Sample 4 - how to trigger repeating actions from the main event loop.
+ * In this example, you use the loop to make the player character
+ * rotate continuously. */
+public class HelloLoop extends SimpleApplication {
+
+ public static void main(String[] args){
+ HelloLoop app = new HelloLoop();
+ app.start();
+ }
+
+ protected Geometry player;
+
+ @Override
+ public void simpleInitApp() {
+ /** this blue box is our player character */
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ player = new Geometry("blue cube", b);
+ Material mat = new Material(assetManager,
+ "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+ player.setMaterial(mat);
+ rootNode.attachChild(player);
+ }
+
+ /* Use the main event loop to trigger repeating actions. */
+ @Override
+ public void simpleUpdate(float tpf) {
+ // make the player rotate:
+ player.rotate(0, 2*tpf, 0);
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/helloworld/HelloMaterial.java b/engine/src/test/jme3test/helloworld/HelloMaterial.java
new file mode 100644
index 0000000..e930c91
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloMaterial.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Texture;
+import com.jme3.util.TangentBinormalGenerator;
+
+/** Sample 6 - how to give an object's surface a material and texture.
+ * How to make objects transparent, or let colors "leak" through partially
+ * transparent textures. How to make bumpy and shiny surfaces. */
+public class HelloMaterial extends SimpleApplication {
+
+ public static void main(String[] args) {
+ HelloMaterial app = new HelloMaterial();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ /** A simple textured cube -- in good MIP map quality. */
+ Box boxshape1 = new Box(new Vector3f(-3f,1.1f,0f), 1f,1f,1f);
+ Geometry cube = new Geometry("My Textured Box", boxshape1);
+ Material mat_stl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
+ mat_stl.setTexture("ColorMap", tex_ml);
+ cube.setMaterial(mat_stl);
+ rootNode.attachChild(cube);
+
+ /** A translucent/transparent texture, similar to a window frame. */
+ Box boxshape3 = new Box(new Vector3f(0f,0f,0f), 1f,1f,0.01f);
+ Geometry window_frame = new Geometry("window frame", boxshape3);
+ Material mat_tt = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat_tt.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
+ mat_tt.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); // activate transparency
+ window_frame.setQueueBucket(Bucket.Transparent);
+ window_frame.setMaterial(mat_tt);
+ rootNode.attachChild(window_frame);
+
+ /** A cube with its base color "leaking" through a partially transparent texture */
+ Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);
+ Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4);
+ Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md");
+ mat_tl.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
+ mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple
+ cube_leak.setMaterial(mat_tl);
+ rootNode.attachChild(cube_leak);
+ // cube_leak.setMaterial((Material) assetManager.loadAsset( "Materials/LeakThrough.j3m"));
+
+ /** A bumpy rock with a shiny light effect. To make bumpy objects you must create a NormalMap. */
+ Sphere rock = new Sphere(32,32, 2f);
+ Geometry shiny_rock = new Geometry("Shiny rock", rock);
+ rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
+ TangentBinormalGenerator.generate(rock); // for lighting effect
+ Material mat_lit = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat_lit.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
+ mat_lit.setTexture("NormalMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
+ mat_lit.setBoolean("UseMaterialColors",true);
+ mat_lit.setColor("Specular",ColorRGBA.White);
+ mat_lit.setColor("Diffuse",ColorRGBA.White);
+ mat_lit.setFloat("Shininess", 5f); // [0,128]
+ shiny_rock.setMaterial(mat_lit);
+ shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit
+ shiny_rock.rotate(1.6f, 0, 0); // Rotate it a bit
+ rootNode.attachChild(shiny_rock);
+ /** Must add a light to make the lit object visible! */
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());
+ sun.setColor(ColorRGBA.White);
+ rootNode.addLight(sun);
+
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloNode.java b/engine/src/test/jme3test/helloworld/HelloNode.java
new file mode 100644
index 0000000..ccda211
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloNode.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+
+/** Sample 2 - How to use nodes as handles to manipulate objects in the scene.
+ * You can rotate, translate, and scale objects by manipulating their parent nodes.
+ * The Root Node is special: Only what is attached to the Root Node appears in the scene. */
+public class HelloNode extends SimpleApplication {
+
+ public static void main(String[] args){
+ HelloNode app = new HelloNode();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ /** create a blue box at coordinates (1,-1,1) */
+ Box box1 = new Box( new Vector3f(1,-1,1), 1,1,1);
+ Geometry blue = new Geometry("Box", box1);
+ Material mat1 = new Material(assetManager,
+ "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.Blue);
+ blue.setMaterial(mat1);
+
+ /** create a red box straight above the blue one at (1,3,1) */
+ Box box2 = new Box( new Vector3f(1,3,1), 1,1,1);
+ Geometry red = new Geometry("Box", box2);
+ Material mat2 = new Material(assetManager,
+ "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.setColor("Color", ColorRGBA.Red);
+ red.setMaterial(mat2);
+
+ /** Create a pivot node at (0,0,0) and attach it to the root node */
+ Node pivot = new Node("pivot");
+ rootNode.attachChild(pivot); // put this node in the scene
+
+ /** Attach the two boxes to the *pivot* node. (And transitively to the root node.) */
+ pivot.attachChild(blue);
+ pivot.attachChild(red);
+ /** Rotate the pivot node: Note that both boxes have rotated! */
+ pivot.rotate(.4f,.4f,0f);
+ }
+}
+
diff --git a/engine/src/test/jme3test/helloworld/HelloPhysics.java b/engine/src/test/jme3test/helloworld/HelloPhysics.java
new file mode 100644
index 0000000..7a30cb8
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloPhysics.java
@@ -0,0 +1,228 @@
+/**
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+/**
+ * Example 12 - how to give objects physical properties so they bounce and fall.
+ * @author base code by double1984, updated by zathras
+ */
+public class HelloPhysics extends SimpleApplication {
+
+ public static void main(String args[]) {
+ HelloPhysics app = new HelloPhysics();
+ app.start();
+ }
+
+ /** Prepare the Physics Application State (jBullet) */
+ private BulletAppState bulletAppState;
+
+ /** Prepare Materials */
+ Material wall_mat;
+ Material stone_mat;
+ Material floor_mat;
+
+ /** Prepare geometries and physical nodes for bricks and cannon balls. */
+ private RigidBodyControl brick_phy;
+ private static final Box box;
+ private RigidBodyControl ball_phy;
+ private static final Sphere sphere;
+ private RigidBodyControl floor_phy;
+ private static final Box floor;
+
+ /** dimensions used for bricks and wall */
+ private static final float brickLength = 0.48f;
+ private static final float brickWidth = 0.24f;
+ private static final float brickHeight = 0.12f;
+
+ static {
+ /** Initialize the cannon ball geometry */
+ sphere = new Sphere(32, 32, 0.4f, true, false);
+ sphere.setTextureMode(TextureMode.Projected);
+ /** Initialize the brick geometry */
+ box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);
+ box.scaleTextureCoordinates(new Vector2f(1f, .5f));
+ /** Initialize the floor geometry */
+ floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
+ floor.scaleTextureCoordinates(new Vector2f(3, 6));
+ }
+
+ @Override
+ public void simpleInitApp() {
+ /** Set up Physics Game */
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ /** Configure cam to look at scene */
+ cam.setLocation(new Vector3f(0, 4f, 6f));
+ cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);
+ /** Initialize the scene, materials, inputs, and physics space */
+ initInputs();
+ initMaterials();
+ initWall();
+ initFloor();
+ initCrossHairs();
+ }
+
+ /** Add InputManager action: Left click triggers shooting. */
+ private void initInputs() {
+ inputManager.addMapping("shoot",
+ new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(actionListener, "shoot");
+ }
+
+ /**
+ * Every time the shoot action is triggered, a new cannon ball is produced.
+ * The ball is set up to fly from the camera position in the camera direction.
+ */
+ private ActionListener actionListener = new ActionListener() {
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("shoot") && !keyPressed) {
+ makeCannonBall();
+ }
+ }
+ };
+
+ /** Initialize the materials used in this scene. */
+ public void initMaterials() {
+ wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ wall_mat.setTexture("ColorMap", tex);
+
+ stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = assetManager.loadTexture(key2);
+ stone_mat.setTexture("ColorMap", tex2);
+
+ floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
+ key3.setGenerateMips(true);
+ Texture tex3 = assetManager.loadTexture(key3);
+ tex3.setWrap(WrapMode.Repeat);
+ floor_mat.setTexture("ColorMap", tex3);
+ }
+
+ /** Make a solid floor and add it to the scene. */
+ public void initFloor() {
+ Geometry floor_geo = new Geometry("Floor", floor);
+ floor_geo.setMaterial(floor_mat);
+ floor_geo.setLocalTranslation(0, -0.1f, 0);
+ this.rootNode.attachChild(floor_geo);
+ /* Make the floor physical with mass 0.0f! */
+ floor_phy = new RigidBodyControl(0.0f);
+ floor_geo.addControl(floor_phy);
+ bulletAppState.getPhysicsSpace().add(floor_phy);
+ }
+
+ /** This loop builds a wall out of individual bricks. */
+ public void initWall() {
+ float startpt = brickLength / 4;
+ float height = 0;
+ for (int j = 0; j < 15; j++) {
+ for (int i = 0; i < 6; i++) {
+ Vector3f vt =
+ new Vector3f(i * brickLength * 2 + startpt, brickHeight + height, 0);
+ makeBrick(vt);
+ }
+ startpt = -startpt;
+ height += 2 * brickHeight;
+ }
+ }
+
+ /** This method creates one individual physical brick. */
+ public void makeBrick(Vector3f loc) {
+ /** Create a brick geometry and attach to scene graph. */
+ Geometry brick_geo = new Geometry("brick", box);
+ brick_geo.setMaterial(wall_mat);
+ rootNode.attachChild(brick_geo);
+ /** Position the brick geometry */
+ brick_geo.setLocalTranslation(loc);
+ /** Make brick physical with a mass > 0.0f. */
+ brick_phy = new RigidBodyControl(2f);
+ /** Add physical brick to physics space. */
+ brick_geo.addControl(brick_phy);
+ bulletAppState.getPhysicsSpace().add(brick_phy);
+ }
+
+ /** This method creates one individual physical cannon ball.
+ * By defaul, the ball is accelerated and flies
+ * from the camera position in the camera direction.*/
+ public void makeCannonBall() {
+ /** Create a cannon ball geometry and attach to scene graph. */
+ Geometry ball_geo = new Geometry("cannon ball", sphere);
+ ball_geo.setMaterial(stone_mat);
+ rootNode.attachChild(ball_geo);
+ /** Position the cannon ball */
+ ball_geo.setLocalTranslation(cam.getLocation());
+ /** Make the ball physcial with a mass > 0.0f */
+ ball_phy = new RigidBodyControl(1f);
+ /** Add physical ball to physics space. */
+ ball_geo.addControl(ball_phy);
+ bulletAppState.getPhysicsSpace().add(ball_phy);
+ /** Accelerate the physcial ball to shoot it. */
+ ball_phy.setLinearVelocity(cam.getDirection().mult(25));
+ }
+
+ /** A plus sign used as crosshairs to help the player with aiming.*/
+ protected void initCrossHairs() {
+ guiNode.detachAllChildren();
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // fake crosshairs :)
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloPicking.java b/engine/src/test/jme3test/helloworld/HelloPicking.java
new file mode 100644
index 0000000..5881cc0
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloPicking.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+
+/** Sample 8 - how to let the user pick (select) objects in the scene
+ * using the mouse or key presses. Can be used for shooting, opening doors, etc. */
+public class HelloPicking extends SimpleApplication {
+
+ public static void main(String[] args) {
+ HelloPicking app = new HelloPicking();
+ app.start();
+ }
+ Node shootables;
+ Geometry mark;
+
+ @Override
+ public void simpleInitApp() {
+ initCrossHairs(); // a "+" in the middle of the screen to help aiming
+ initKeys(); // load custom key mappings
+ initMark(); // a red sphere to mark the hit
+
+ /** create four colored boxes and a floor to shoot at: */
+ shootables = new Node("Shootables");
+ rootNode.attachChild(shootables);
+ shootables.attachChild(makeCube("a Dragon", -2f, 0f, 1f));
+ shootables.attachChild(makeCube("a tin can", 1f, -2f, 0f));
+ shootables.attachChild(makeCube("the Sheriff", 0f, 1f, -2f));
+ shootables.attachChild(makeCube("the Deputy", 1f, 0f, -4f));
+ shootables.attachChild(makeFloor());
+ shootables.attachChild(makeCharacter());
+ }
+
+ /** Declaring the "Shoot" action and mapping to its triggers. */
+ private void initKeys() {
+ inputManager.addMapping("Shoot",
+ new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar
+ new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // trigger 2: left-button click
+ inputManager.addListener(actionListener, "Shoot");
+ }
+ /** Defining the "Shoot" action: Determine what was hit and how to respond. */
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("Shoot") && !keyPressed) {
+ // 1. Reset results list.
+ CollisionResults results = new CollisionResults();
+ // 2. Aim the ray from cam loc to cam direction.
+ Ray ray = new Ray(cam.getLocation(), cam.getDirection());
+ // 3. Collect intersections between Ray and Shootables in results list.
+ shootables.collideWith(ray, results);
+ // 4. Print the results
+ System.out.println("----- Collisions? " + results.size() + "-----");
+ for (int i = 0; i < results.size(); i++) {
+ // For each hit, we know distance, impact point, name of geometry.
+ float dist = results.getCollision(i).getDistance();
+ Vector3f pt = results.getCollision(i).getContactPoint();
+ String hit = results.getCollision(i).getGeometry().getName();
+ System.out.println("* Collision #" + i);
+ System.out.println(" You shot " + hit + " at " + pt + ", " + dist + " wu away.");
+ }
+ // 5. Use the results (we mark the hit object)
+ if (results.size() > 0) {
+ // The closest collision point is what was truly hit:
+ CollisionResult closest = results.getClosestCollision();
+ // Let's interact - we mark the hit with a red dot.
+ mark.setLocalTranslation(closest.getContactPoint());
+ rootNode.attachChild(mark);
+ } else {
+ // No hits? Then remove the red mark.
+ rootNode.detachChild(mark);
+ }
+ }
+ }
+ };
+
+ /** A cube object for target practice */
+ protected Geometry makeCube(String name, float x, float y, float z) {
+ Box box = new Box(new Vector3f(x, y, z), 1, 1, 1);
+ Geometry cube = new Geometry(name, box);
+ Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.randomColor());
+ cube.setMaterial(mat1);
+ return cube;
+ }
+
+ /** A floor to show that the "shot" can go through several objects. */
+ protected Geometry makeFloor() {
+ Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15);
+ Geometry floor = new Geometry("the Floor", box);
+ Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat1.setColor("Color", ColorRGBA.Gray);
+ floor.setMaterial(mat1);
+ return floor;
+ }
+
+ /** A red ball that marks the last spot that was "hit" by the "shot". */
+ protected void initMark() {
+ Sphere sphere = new Sphere(30, 30, 0.2f);
+ mark = new Geometry("BOOM!", sphere);
+ Material mark_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mark_mat.setColor("Color", ColorRGBA.Red);
+ mark.setMaterial(mark_mat);
+ }
+
+ /** A centred plus sign to help the player aim. */
+ protected void initCrossHairs() {
+ guiNode.detachAllChildren();
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+
+ protected Spatial makeCharacter() {
+ // load a character from jme3test-test-data
+ Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ golem.scale(0.5f);
+ golem.setLocalTranslation(-1.0f, -1.5f, -0.6f);
+
+ // We must add a light to make the model visible
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
+ golem.addLight(sun);
+ return golem;
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloTerrain.java b/engine/src/test/jme3test/helloworld/HelloTerrain.java
new file mode 100644
index 0000000..6986b6c
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloTerrain.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+public class HelloTerrain extends SimpleApplication {
+
+ private TerrainQuad terrain;
+ Material mat_terrain;
+
+ public static void main(String[] args) {
+ HelloTerrain app = new HelloTerrain();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(50);
+
+ /** 1. Create terrain material and load four textures into it. */
+ mat_terrain = new Material(assetManager,
+ "Common/MatDefs/Terrain/Terrain.j3md");
+
+ /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
+ mat_terrain.setTexture("Alpha", assetManager.loadTexture(
+ "Textures/Terrain/splat/alphamap.png"));
+
+ /** 1.2) Add GRASS texture into the red layer (Tex1). */
+ Texture grass = assetManager.loadTexture(
+ "Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ mat_terrain.setTexture("Tex1", grass);
+ mat_terrain.setFloat("Tex1Scale", 64f);
+
+ /** 1.3) Add DIRT texture into the green layer (Tex2) */
+ Texture dirt = assetManager.loadTexture(
+ "Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ mat_terrain.setTexture("Tex2", dirt);
+ mat_terrain.setFloat("Tex2Scale", 32f);
+
+ /** 1.4) Add ROAD texture into the blue layer (Tex3) */
+ Texture rock = assetManager.loadTexture(
+ "Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ mat_terrain.setTexture("Tex3", rock);
+ mat_terrain.setFloat("Tex3Scale", 128f);
+
+ /** 2. Create the height map */
+ AbstractHeightMap heightmap = null;
+ Texture heightMapImage = assetManager.loadTexture(
+ "Textures/Terrain/splat/mountains512.png");
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
+ heightmap.load();
+
+ /** 3. We have prepared material and heightmap.
+ * Now we create the actual terrain:
+ * 3.1) Create a TerrainQuad and name it "my terrain".
+ * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
+ * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
+ * 3.4) As LOD step scale we supply Vector3f(1,1,1).
+ * 3.5) We supply the prepared heightmap itself.
+ */
+ int patchSize = 65;
+ terrain = new TerrainQuad("my terrain", patchSize, 513, heightmap.getHeightMap());
+
+ /** 4. We give the terrain its material, position & scale it, and attach it. */
+ terrain.setMaterial(mat_terrain);
+ terrain.setLocalTranslation(0, -100, 0);
+ terrain.setLocalScale(2f, 1f, 2f);
+ rootNode.attachChild(terrain);
+
+ /** 5. The LOD (level of detail) depends on were the camera is: */
+ TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(patchSize, 2.7f) ); // patch size, and a multiplier
+ terrain.addControl(control);
+ }
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloTerrainCollision.java b/engine/src/test/jme3test/helloworld/HelloTerrainCollision.java
new file mode 100644
index 0000000..c1a96a8
--- /dev/null
+++ b/engine/src/test/jme3test/helloworld/HelloTerrainCollision.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.helloworld;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Node;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This demo shows a terrain with collision detection,
+ * that you can walk around in with a first-person perspective.
+ * This code combines HelloCollision and HelloTerrain.
+ */
+public class HelloTerrainCollision extends SimpleApplication
+ implements ActionListener {
+
+ private BulletAppState bulletAppState;
+ private RigidBodyControl landscape;
+ private CharacterControl player;
+ private Vector3f walkDirection = new Vector3f();
+ private boolean left = false, right = false, up = false, down = false;
+ private TerrainQuad terrain;
+ private Material mat_terrain;
+
+ public static void main(String[] args) {
+ HelloTerrainCollision app = new HelloTerrainCollision();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ /** Set up Physics */
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+
+ flyCam.setMoveSpeed(100);
+ setUpKeys();
+
+ /** 1. Create terrain material and load four textures into it. */
+ mat_terrain = new Material(assetManager,
+ "Common/MatDefs/Terrain/Terrain.j3md");
+
+ /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
+ mat_terrain.setTexture("Alpha", assetManager.loadTexture(
+ "Textures/Terrain/splat/alphamap.png"));
+
+ /** 1.2) Add GRASS texture into the red layer (Tex1). */
+ Texture grass = assetManager.loadTexture(
+ "Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ mat_terrain.setTexture("Tex1", grass);
+ mat_terrain.setFloat("Tex1Scale", 64f);
+
+ /** 1.3) Add DIRT texture into the green layer (Tex2) */
+ Texture dirt = assetManager.loadTexture(
+ "Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ mat_terrain.setTexture("Tex2", dirt);
+ mat_terrain.setFloat("Tex2Scale", 32f);
+
+ /** 1.4) Add ROAD texture into the blue layer (Tex3) */
+ Texture rock = assetManager.loadTexture(
+ "Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ mat_terrain.setTexture("Tex3", rock);
+ mat_terrain.setFloat("Tex3Scale", 128f);
+
+ /** 2. Create the height map */
+ AbstractHeightMap heightmap = null;
+ Texture heightMapImage = assetManager.loadTexture(
+ "Textures/Terrain/splat/mountains512.png");
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
+ heightmap.load();
+
+ /** 3. We have prepared material and heightmap.
+ * Now we create the actual terrain:
+ * 3.1) Create a TerrainQuad and name it "my terrain".
+ * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
+ * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
+ * 3.4) As LOD step scale we supply Vector3f(1,1,1).
+ * 3.5) We supply the prepared heightmap itself.
+ */
+ terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
+
+ /** 4. We give the terrain its material, position & scale it, and attach it. */
+ terrain.setMaterial(mat_terrain);
+ terrain.setLocalTranslation(0, -100, 0);
+ terrain.setLocalScale(2f, 1f, 2f);
+ rootNode.attachChild(terrain);
+
+ /** 5. The LOD (level of detail) depends on were the camera is: */
+ List<Camera> cameras = new ArrayList<Camera>();
+ cameras.add(getCamera());
+ TerrainLodControl control = new TerrainLodControl(terrain, cameras);
+ terrain.addControl(control);
+
+ /** 6. Add physics: */
+ // We set up collision detection for the scene by creating a
+ // compound collision shape and a static RigidBodyControl with mass zero.*/
+ CollisionShape terrainShape =
+ CollisionShapeFactory.createMeshShape((Node) terrain);
+ landscape = new RigidBodyControl(terrainShape, 0);
+ terrain.addControl(landscape);
+
+ // We set up collision detection for the player by creating
+ // a capsule collision shape and a CharacterControl.
+ // The CharacterControl offers extra settings for
+ // size, stepheight, jumping, falling, and gravity.
+ // We also put the player in its starting position.
+ CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
+ player = new CharacterControl(capsuleShape, 0.05f);
+ player.setJumpSpeed(20);
+ player.setFallSpeed(30);
+ player.setGravity(30);
+ player.setPhysicsLocation(new Vector3f(0, 10, 0));
+
+ // We attach the scene and the player to the rootnode and the physics space,
+ // to make them appear in the game world.
+ bulletAppState.getPhysicsSpace().add(terrain);
+ bulletAppState.getPhysicsSpace().add(player);
+
+ }
+ /** We over-write some navigational key mappings here, so we can
+ * add physics-controlled walking and jumping: */
+ private void setUpKeys() {
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
+ inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
+ inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this, "Left");
+ inputManager.addListener(this, "Right");
+ inputManager.addListener(this, "Up");
+ inputManager.addListener(this, "Down");
+ inputManager.addListener(this, "Jump");
+ }
+
+ /** These are our custom actions triggered by key presses.
+ * We do not walk yet, we just keep track of the direction the user pressed. */
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Left")) {
+ if (value) { left = true; } else { left = false; }
+ } else if (binding.equals("Right")) {
+ if (value) { right = true; } else { right = false; }
+ } else if (binding.equals("Up")) {
+ if (value) { up = true; } else { up = false; }
+ } else if (binding.equals("Down")) {
+ if (value) { down = true; } else { down = false; }
+ } else if (binding.equals("Jump")) {
+ player.jump();
+ }
+ }
+
+ /**
+ * This is the main event loop--walking happens here.
+ * We check in which direction the player is walking by interpreting
+ * the camera direction forward (camDir) and to the side (camLeft).
+ * The setWalkDirection() command is what lets a physics-controlled player walk.
+ * We also make sure here that the camera moves with player.
+ */
+ @Override
+ public void simpleUpdate(float tpf) {
+ Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
+ walkDirection.set(0, 0, 0);
+ if (left) { walkDirection.addLocal(camLeft); }
+ if (right) { walkDirection.addLocal(camLeft.negate()); }
+ if (up) { walkDirection.addLocal(camDir); }
+ if (down) { walkDirection.addLocal(camDir.negate()); }
+ player.setWalkDirection(walkDirection);
+ cam.setLocation(player.getPhysicsLocation());
+ }
+}
+
diff --git a/engine/src/test/jme3test/input/TestCameraNode.java b/engine/src/test/jme3test/input/TestCameraNode.java
new file mode 100644
index 0000000..111260a
--- /dev/null
+++ b/engine/src/test/jme3test/input/TestCameraNode.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * <p/>
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.input;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.*;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.CameraNode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.CameraControl.ControlDirection;
+import com.jme3.scene.shape.Quad;
+import com.jme3.system.AppSettings;
+
+/**
+ * A 3rd-person camera node follows a target (teapot). Follow the teapot with
+ * WASD keys, rotate by dragging the mouse.
+ */
+public class TestCameraNode extends SimpleApplication implements AnalogListener, ActionListener {
+
+ private Geometry teaGeom;
+ private Node teaNode;
+ CameraNode camNode;
+ boolean rotate = false;
+ Vector3f direction = new Vector3f();
+
+ public static void main(String[] args) {
+ TestCameraNode app = new TestCameraNode();
+ AppSettings s = new AppSettings(true);
+ s.setFrameRate(100);
+ app.setSettings(s);
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ // load a teapot model
+ teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+ teaGeom.setMaterial(mat);
+ //create a node to attach the geometry and the camera node
+ teaNode = new Node("teaNode");
+ teaNode.attachChild(teaGeom);
+ rootNode.attachChild(teaNode);
+ // create a floor
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Geometry ground = new Geometry("ground", new Quad(50, 50));
+ ground.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
+ ground.setLocalTranslation(-25, -1, 25);
+ ground.setMaterial(mat);
+ rootNode.attachChild(ground);
+
+ //creating the camera Node
+ camNode = new CameraNode("CamNode", cam);
+ //Setting the direction to Spatial to camera, this means the camera will copy the movements of the Node
+ camNode.setControlDir(ControlDirection.SpatialToCamera);
+ //attaching the camNode to the teaNode
+ teaNode.attachChild(camNode);
+ //setting the local translation of the cam node to move it away from the teanNode a bit
+ camNode.setLocalTranslation(new Vector3f(-10, 0, 0));
+ //setting the camNode to look at the teaNode
+ camNode.lookAt(teaNode.getLocalTranslation(), Vector3f.UNIT_Y);
+
+ //disable the default 1st-person flyCam (don't forget this!!)
+ flyCam.setEnabled(false);
+
+ registerInput();
+ }
+
+ public void registerInput() {
+ inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP), new KeyTrigger(keyInput.KEY_W));
+ inputManager.addMapping("moveBackward", new KeyTrigger(keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
+ inputManager.addMapping("moveRight", new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(keyInput.KEY_D));
+ inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT), new KeyTrigger(keyInput.KEY_A));
+ inputManager.addMapping("toggleRotate", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addMapping("rotateRight", new MouseAxisTrigger(MouseInput.AXIS_X, true));
+ inputManager.addMapping("rotateLeft", new MouseAxisTrigger(MouseInput.AXIS_X, false));
+ inputManager.addListener(this, "moveForward", "moveBackward", "moveRight", "moveLeft");
+ inputManager.addListener(this, "rotateRight", "rotateLeft", "toggleRotate");
+ }
+
+ public void onAnalog(String name, float value, float tpf) {
+ //computing the normalized direction of the cam to move the teaNode
+ direction.set(cam.getDirection()).normalizeLocal();
+ if (name.equals("moveForward")) {
+ direction.multLocal(5 * tpf);
+ teaNode.move(direction);
+ }
+ if (name.equals("moveBackward")) {
+ direction.multLocal(-5 * tpf);
+ teaNode.move(direction);
+ }
+ if (name.equals("moveRight")) {
+ direction.crossLocal(Vector3f.UNIT_Y).multLocal(5 * tpf);
+ teaNode.move(direction);
+ }
+ if (name.equals("moveLeft")) {
+ direction.crossLocal(Vector3f.UNIT_Y).multLocal(-5 * tpf);
+ teaNode.move(direction);
+ }
+ if (name.equals("rotateRight") && rotate) {
+ teaNode.rotate(0, 5 * tpf, 0);
+ }
+ if (name.equals("rotateLeft") && rotate) {
+ teaNode.rotate(0, -5 * tpf, 0);
+ }
+
+ }
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ //toggling rotation on or off
+ if (name.equals("toggleRotate") && keyPressed) {
+ rotate = true;
+ inputManager.setCursorVisible(false);
+ }
+ if (name.equals("toggleRotate") && !keyPressed) {
+ rotate = false;
+ inputManager.setCursorVisible(true);
+ }
+
+ }
+}
diff --git a/engine/src/test/jme3test/input/TestChaseCamera.java b/engine/src/test/jme3test/input/TestChaseCamera.java
new file mode 100644
index 0000000..04b528f
--- /dev/null
+++ b/engine/src/test/jme3test/input/TestChaseCamera.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.input;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+
+/** A 3rd-person chase camera orbits a target (teapot).
+ * Follow the teapot with WASD keys, rotate by dragging the mouse. */
+public class TestChaseCamera extends SimpleApplication implements AnalogListener, ActionListener {
+
+ private Geometry teaGeom;
+ private ChaseCamera chaseCam;
+
+ public static void main(String[] args) {
+ TestChaseCamera app = new TestChaseCamera();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ // Load a teapot model
+ teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+ Material mat_tea = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+ teaGeom.setMaterial(mat_tea);
+ rootNode.attachChild(teaGeom);
+
+ // Load a floor model
+ Material mat_ground = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat_ground.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Geometry ground = new Geometry("ground", new Quad(50, 50));
+ ground.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
+ ground.setLocalTranslation(-25, -1, 25);
+ ground.setMaterial(mat_ground);
+ rootNode.attachChild(ground);
+
+ // Disable the default first-person cam!
+ flyCam.setEnabled(false);
+
+ // Enable a chase cam
+ chaseCam = new ChaseCamera(cam, teaGeom, inputManager);
+
+ //Uncomment this to invert the camera's vertical rotation Axis
+ //chaseCam.setInvertVerticalAxis(true);
+
+ //Uncomment this to invert the camera's horizontal rotation Axis
+ //chaseCam.setInvertHorizontalAxis(true);
+
+ //Comment this to disable smooth camera motion
+ chaseCam.setSmoothMotion(true);
+
+ //Uncomment this to disable trailing of the camera
+ //WARNING, trailing only works with smooth motion enabled. It is true by default.
+ //chaseCam.setTrailingEnabled(false);
+
+ //Uncomment this to look 3 world units above the target
+ //chaseCam.setLookAtOffset(Vector3f.UNIT_Y.mult(3));
+
+ //Uncomment this to enable rotation when the middle mouse button is pressed (like Blender)
+ //WARNING : setting this trigger disable the rotation on right and left mouse button click
+ //chaseCam.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE));
+
+ //Uncomment this to set mutiple triggers to enable rotation of the cam
+ //Here spade bar and middle mouse button
+ //chaseCam.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE),new KeyTrigger(KeyInput.KEY_SPACE));
+
+ //registering inputs for target's movement
+ registerInput();
+
+ }
+
+ public void registerInput() {
+ inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP), new KeyTrigger(keyInput.KEY_W));
+ inputManager.addMapping("moveBackward", new KeyTrigger(keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
+ inputManager.addMapping("moveRight", new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(keyInput.KEY_D));
+ inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT), new KeyTrigger(keyInput.KEY_A));
+ inputManager.addMapping("displayPosition", new KeyTrigger(keyInput.KEY_P));
+ inputManager.addListener(this, "moveForward", "moveBackward", "moveRight", "moveLeft");
+ inputManager.addListener(this, "displayPosition");
+ }
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals("moveForward")) {
+ teaGeom.move(0, 0, -5 * tpf);
+ }
+ if (name.equals("moveBackward")) {
+ teaGeom.move(0, 0, 5 * tpf);
+ }
+ if (name.equals("moveRight")) {
+ teaGeom.move(5 * tpf, 0, 0);
+ }
+ if (name.equals("moveLeft")) {
+ teaGeom.move(-5 * tpf, 0, 0);
+
+ }
+
+ }
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("displayPosition") && keyPressed) {
+ teaGeom.move(10, 10, 10);
+
+ }
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ super.simpleUpdate(tpf);
+
+ // teaGeom.move(new Vector3f(0.001f, 0, 0));
+ // pivot.rotate(0, 0.00001f, 0);
+ // rootNode.updateGeometricState();
+ }
+// public void update() {
+// super.update();
+//// render the viewports
+// float tpf = timer.getTimePerFrame();
+// state.getRootNode().rotate(0, 0.000001f, 0);
+// stateManager.update(tpf);
+// stateManager.render(renderManager);
+// renderManager.render(tpf);
+// }
+}
diff --git a/engine/src/test/jme3test/input/TestControls.java b/engine/src/test/jme3test/input/TestControls.java
new file mode 100644
index 0000000..6967b6b
--- /dev/null
+++ b/engine/src/test/jme3test/input/TestControls.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.input;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseAxisTrigger;
+
+public class TestControls extends SimpleApplication {
+
+ private ActionListener actionListener = new ActionListener(){
+ public void onAction(String name, boolean pressed, float tpf){
+ System.out.println(name + " = " + pressed);
+ }
+ };
+ public AnalogListener analogListener = new AnalogListener() {
+ public void onAnalog(String name, float value, float tpf) {
+ System.out.println(name + " = " + value);
+ }
+ };
+
+ public static void main(String[] args){
+ TestControls app = new TestControls();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // Test multiple inputs per mapping
+ inputManager.addMapping("My Action",
+ new KeyTrigger(KeyInput.KEY_SPACE),
+ new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
+
+ // Test multiple listeners per mapping
+ inputManager.addListener(actionListener, "My Action");
+ inputManager.addListener(analogListener, "My Action");
+ }
+
+}
diff --git a/engine/src/test/jme3test/input/TestJoystick.java b/engine/src/test/jme3test/input/TestJoystick.java
new file mode 100644
index 0000000..2ea924e
--- /dev/null
+++ b/engine/src/test/jme3test/input/TestJoystick.java
@@ -0,0 +1,51 @@
+package jme3test.input;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.JoyInput;
+import com.jme3.input.Joystick;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.JoyAxisTrigger;
+import com.jme3.system.AppSettings;
+
+public class TestJoystick extends SimpleApplication implements AnalogListener {
+
+ public static void main(String[] args){
+ TestJoystick app = new TestJoystick();
+ AppSettings settings = new AppSettings(true);
+ settings.setUseJoysticks(true);
+ app.setSettings(settings);
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Joystick[] joysticks = inputManager.getJoysticks();
+ if (joysticks == null)
+ throw new IllegalStateException("Cannot find any joysticks!");
+
+ for (Joystick joy : joysticks){
+ System.out.println(joy.toString());
+ }
+
+ inputManager.addMapping("DPAD Left", new JoyAxisTrigger(0, JoyInput.AXIS_POV_X, true));
+ inputManager.addMapping("DPAD Right", new JoyAxisTrigger(0, JoyInput.AXIS_POV_X, false));
+ inputManager.addMapping("DPAD Down", new JoyAxisTrigger(0, JoyInput.AXIS_POV_Y, true));
+ inputManager.addMapping("DPAD Up", new JoyAxisTrigger(0, JoyInput.AXIS_POV_Y, false));
+ inputManager.addListener(this, "DPAD Left", "DPAD Right", "DPAD Down", "DPAD Up");
+
+ inputManager.addMapping("Joy Left", new JoyAxisTrigger(0, 0, true));
+ inputManager.addMapping("Joy Right", new JoyAxisTrigger(0, 0, false));
+ inputManager.addMapping("Joy Down", new JoyAxisTrigger(0, 1, true));
+ inputManager.addMapping("Joy Up", new JoyAxisTrigger(0, 1, false));
+ inputManager.addListener(this, "Joy Left", "Joy Right", "Joy Down", "Joy Up");
+ }
+
+ public void onAnalog(String name, float isPressed, float tpf) {
+ System.out.println(name + " = " + isPressed);
+ }
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ System.out.println(name + " = " + isPressed);
+ }
+
+}
diff --git a/engine/src/test/jme3test/input/combomoves/ComboMove.java b/engine/src/test/jme3test/input/combomoves/ComboMove.java
new file mode 100644
index 0000000..f0763f7
--- /dev/null
+++ b/engine/src/test/jme3test/input/combomoves/ComboMove.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.input.combomoves;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ComboMove {
+
+ public static class ComboMoveState {
+
+ private String[] pressedMappings;
+ private String[] unpressedMappings;
+ private float timeElapsed;
+
+ public ComboMoveState(String[] pressedMappings, String[] unpressedMappings, float timeElapsed) {
+ this.pressedMappings = pressedMappings;
+ this.unpressedMappings = unpressedMappings;
+ this.timeElapsed = timeElapsed;
+ }
+
+ public String[] getUnpressedMappings() {
+ return unpressedMappings;
+ }
+
+ public String[] getPressedMappings() {
+ return pressedMappings;
+ }
+
+ public float getTimeElapsed() {
+ return timeElapsed;
+ }
+
+ }
+
+ private String moveName;
+ private List<ComboMoveState> states = new ArrayList<ComboMoveState>();
+ private boolean useFinalState = true;
+ private float priority = 1;
+ private float castTime = 0.8f;
+
+ private transient String[] pressed, unpressed;
+ private transient float timeElapsed;
+
+ public ComboMove(String moveName){
+ this.moveName = moveName;
+ }
+
+ public float getPriority() {
+ return priority;
+ }
+
+ public void setPriority(float priority) {
+ this.priority = priority;
+ }
+
+ public float getCastTime() {
+ return castTime;
+ }
+
+ public void setCastTime(float castTime) {
+ this.castTime = castTime;
+ }
+
+ public boolean useFinalState() {
+ return useFinalState;
+ }
+
+ public void setUseFinalState(boolean useFinalState) {
+ this.useFinalState = useFinalState;
+ }
+
+ public ComboMove press(String ... pressedMappings){
+ this.pressed = pressedMappings;
+ return this;
+ }
+
+ public ComboMove notPress(String ... unpressedMappings){
+ this.unpressed = unpressedMappings;
+ return this;
+ }
+
+ public ComboMove timeElapsed(float time){
+ this.timeElapsed = time;
+ return this;
+ }
+
+ public void done(){
+ if (pressed == null)
+ pressed = new String[0];
+
+ if (unpressed == null)
+ unpressed = new String[0];
+
+ states.add(new ComboMoveState(pressed, unpressed, timeElapsed));
+ pressed = null;
+ unpressed = null;
+ timeElapsed = -1;
+ }
+
+ public ComboMoveState getState(int num){
+ return states.get(num);
+ }
+
+ public int getNumStates(){
+ return states.size();
+ }
+
+ public String getMoveName() {
+ return moveName;
+ }
+
+}
diff --git a/engine/src/test/jme3test/input/combomoves/ComboMoveExecution.java b/engine/src/test/jme3test/input/combomoves/ComboMoveExecution.java
new file mode 100644
index 0000000..3a623bb
--- /dev/null
+++ b/engine/src/test/jme3test/input/combomoves/ComboMoveExecution.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.input.combomoves;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import jme3test.input.combomoves.ComboMove.ComboMoveState;
+
+public class ComboMoveExecution {
+
+ private static final float TIME_LIMIT = 0.3f;
+
+ private ComboMove moveDef;
+ private int state;
+ private float moveTime;
+ private boolean finalState = false;
+
+ private String debugString = ""; // for debug only
+
+ public ComboMoveExecution(ComboMove move){
+ moveDef = move;
+ }
+
+ private boolean isStateSatisfied(HashSet<String> pressedMappings, float time,
+ ComboMoveState state){
+
+ if (state.getTimeElapsed() != -1f){
+ // check if an appropriate amount of time has passed
+ // if the state requires it
+ if (moveTime + state.getTimeElapsed() >= time){
+ return false;
+ }
+ }
+ for (String mapping : state.getPressedMappings()){
+ if (!pressedMappings.contains(mapping))
+ return false;
+ }
+ for (String mapping : state.getUnpressedMappings()){
+ if (pressedMappings.contains(mapping))
+ return false;
+ }
+ return true;
+ }
+
+ public String getDebugString(){
+ return debugString;
+ }
+
+ public void updateExpiration(float time){
+ if (!finalState && moveTime > 0 && moveTime + TIME_LIMIT < time){
+ state = 0;
+ moveTime = 0;
+ finalState = false;
+
+ // reset debug string.
+ debugString = "";
+ }
+ }
+
+ /**
+ * Check if move needs to be executed.
+ * @param pressedMappings Which mappings are currently pressed
+ * @param time Current time since start of app
+ * @return True if move needs to be executed.
+ */
+ public boolean updateState(HashSet<String> pressedMappings, float time){
+ ComboMoveState currentState = moveDef.getState(state);
+ if (isStateSatisfied(pressedMappings, time, currentState)){
+ state ++;
+ moveTime = time;
+
+ if (state >= moveDef.getNumStates()){
+ finalState = false;
+ state = 0;
+
+ moveTime = time+0.5f; // this is for the reset of the debug string only.
+ debugString += ", -CASTING " + moveDef.getMoveName().toUpperCase() + "-";
+ return true;
+ }
+
+ // the following for debug only.
+ if (currentState.getPressedMappings().length > 0){
+ if (!debugString.equals(""))
+ debugString += ", ";
+
+ debugString += Arrays.toString(currentState.getPressedMappings()).replace(", ", "+");
+ }
+
+ if (moveDef.useFinalState() && state == moveDef.getNumStates() - 1){
+ finalState = true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/engine/src/test/jme3test/input/combomoves/TestComboMoves.java b/engine/src/test/jme3test/input/combomoves/TestComboMoves.java
new file mode 100644
index 0000000..cd922f4
--- /dev/null
+++ b/engine/src/test/jme3test/input/combomoves/TestComboMoves.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.input.combomoves;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Spatial.CullHint;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class TestComboMoves extends SimpleApplication implements ActionListener {
+
+ private HashSet<String> pressedMappings = new HashSet<String>();
+
+ private ComboMove fireball;
+ private ComboMoveExecution fireballExec;
+ private BitmapText fireballText;
+
+ private ComboMove shuriken;
+ private ComboMoveExecution shurikenExec;
+ private BitmapText shurikenText;
+
+ private ComboMove jab;
+ private ComboMoveExecution jabExec;
+ private BitmapText jabText;
+
+ private ComboMove punch;
+ private ComboMoveExecution punchExec;
+ private BitmapText punchText;
+
+ private ComboMove currentMove = null;
+ private float currentMoveCastTime = 0;
+ private float time = 0;
+
+ public static void main(String[] args){
+ TestComboMoves app = new TestComboMoves();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ setDisplayFps(false);
+ setDisplayStatView(false);
+
+ // Create debug text
+ BitmapText helpText = new BitmapText(guiFont);
+ helpText.setLocalTranslation(0, settings.getHeight(), 0);
+ helpText.setText("Moves:\n" +
+ "Fireball: Down, Down+Right, Right\n"+
+ "Shuriken: Left, Down, Attack1(Z)\n"+
+ "Jab: Attack1(Z)\n"+
+ "Punch: Attack1(Z), Attack1(Z)\n");
+ guiNode.attachChild(helpText);
+
+ fireballText = new BitmapText(guiFont);
+ fireballText.setColor(ColorRGBA.Orange);
+ fireballText.setLocalTranslation(0, fireballText.getLineHeight(), 0);
+ guiNode.attachChild(fireballText);
+
+ shurikenText = new BitmapText(guiFont);
+ shurikenText.setColor(ColorRGBA.Cyan);
+ shurikenText.setLocalTranslation(0, shurikenText.getLineHeight()*2f, 0);
+ guiNode.attachChild(shurikenText);
+
+ jabText = new BitmapText(guiFont);
+ jabText.setColor(ColorRGBA.Red);
+ jabText.setLocalTranslation(0, jabText.getLineHeight()*3f, 0);
+ guiNode.attachChild(jabText);
+
+ punchText = new BitmapText(guiFont);
+ punchText.setColor(ColorRGBA.Green);
+ punchText.setLocalTranslation(0, punchText.getLineHeight()*4f, 0);
+ guiNode.attachChild(punchText);
+
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT));
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT));
+ inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_UP));
+ inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_DOWN));
+ inputManager.addMapping("Attack1", new KeyTrigger(KeyInput.KEY_Z));
+ inputManager.addListener(this, "Left", "Right", "Up", "Down", "Attack1");
+
+ fireball = new ComboMove("Fireball");
+ fireball.press("Down").notPress("Right").done();
+ fireball.press("Right", "Down").done();
+ fireball.press("Right").notPress("Down").done();
+ fireball.notPress("Right", "Down").done();
+ fireball.setUseFinalState(false); // no waiting on final state
+
+ shuriken = new ComboMove("Shuriken");
+ shuriken.press("Left").notPress("Down", "Attack1").done();
+ shuriken.press("Down").notPress("Attack1").timeElapsed(0.11f).done();
+ shuriken.press("Attack1").notPress("Left").timeElapsed(0.11f).done();
+ shuriken.notPress("Left", "Down", "Attack1").done();
+
+ jab = new ComboMove("Jab");
+ jab.setPriority(0.5f); // make jab less important than other moves
+ jab.press("Attack1").done();
+
+ punch = new ComboMove("Punch");
+ punch.press("Attack1").done();
+ punch.notPress("Attack1").done();
+ punch.press("Attack1").done();
+
+ fireballExec = new ComboMoveExecution(fireball);
+ shurikenExec = new ComboMoveExecution(shuriken);
+ jabExec = new ComboMoveExecution(jab);
+ punchExec = new ComboMoveExecution(punch);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ time += tpf;
+
+ // check every frame if any executions are expired
+ shurikenExec.updateExpiration(time);
+ shurikenText.setText("Shuriken Exec: " + shurikenExec.getDebugString());
+
+ fireballExec.updateExpiration(time);
+ fireballText.setText("Fireball Exec: " + fireballExec.getDebugString());
+
+ jabExec.updateExpiration(time);
+ jabText.setText("Jab Exec: " + jabExec.getDebugString());
+
+ punchExec.updateExpiration(time);
+ punchText.setText("Punch Exec: " + punchExec.getDebugString());
+
+ if (currentMove != null){
+ currentMoveCastTime -= tpf;
+ if (currentMoveCastTime <= 0){
+ System.out.println("DONE CASTING " + currentMove.getMoveName());
+ currentMoveCastTime = 0;
+ currentMove = null;
+ }
+ }
+ }
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (isPressed){
+ pressedMappings.add(name);
+ }else{
+ pressedMappings.remove(name);
+ }
+
+ // the pressed mappings was changed. update combo executions
+ List<ComboMove> invokedMoves = new ArrayList<ComboMove>();
+ if (shurikenExec.updateState(pressedMappings, time)){
+ invokedMoves.add(shuriken);
+ }
+
+ if (fireballExec.updateState(pressedMappings, time)){
+ invokedMoves.add(fireball);
+ }
+
+ if (jabExec.updateState(pressedMappings, time)){
+ invokedMoves.add(jab);
+ }
+
+ if (punchExec.updateState(pressedMappings, time)){
+ invokedMoves.add(punch);
+ }
+
+ if (invokedMoves.size() > 0){
+ // choose move with highest priority
+ float priority = 0;
+ ComboMove toExec = null;
+ for (ComboMove move : invokedMoves){
+ if (move.getPriority() > priority){
+ priority = move.getPriority();
+ toExec = move;
+ }
+ }
+ if (currentMove != null && currentMove.getPriority() > toExec.getPriority()){
+ return;
+ }
+
+ currentMove = toExec;
+ currentMoveCastTime = currentMove.getCastTime();
+ //System.out.println("CASTING " + currentMove.getMoveName());
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestEnvironmentMapping.java b/engine/src/test/jme3test/light/TestEnvironmentMapping.java
new file mode 100644
index 0000000..67b03e0
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestEnvironmentMapping.java
@@ -0,0 +1,61 @@
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.input.ChaseCamera;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.Texture;
+import com.jme3.util.SkyFactory;
+
+/**
+ * test
+ * @author nehon
+ */
+public class TestEnvironmentMapping extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestEnvironmentMapping app = new TestEnvironmentMapping();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ final Node buggy = (Node) assetManager.loadModel("Models/Buggy/Buggy.j3o");
+
+ TextureKey key = new TextureKey("Textures/Sky/Bright/BrightSky.dds", true);
+ key.setGenerateMips(true);
+ key.setAsCube(true);
+ final Texture tex = assetManager.loadTexture(key);
+
+ for (Spatial geom : buggy.getChildren()) {
+ if (geom instanceof Geometry) {
+ Material m = ((Geometry) geom).getMaterial();
+ m.setTexture("EnvMap", tex);
+ m.setVector3("FresnelParams", new Vector3f(0.05f, 0.18f, 0.11f));
+ }
+ }
+
+ flyCam.setEnabled(false);
+
+ ChaseCamera chaseCam = new ChaseCamera(cam, inputManager);
+ chaseCam.setLookAtOffset(new Vector3f(0,0.5f,-1.0f));
+ buggy.addControl(chaseCam);
+ rootNode.attachChild(buggy);
+ rootNode.attachChild(SkyFactory.createSky(assetManager, tex, false));
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ BloomFilter bf = new BloomFilter(BloomFilter.GlowMode.Objects);
+ bf.setBloomIntensity(2.3f);
+ bf.setExposurePower(0.6f);
+
+ fpp.addFilter(bf);
+
+ viewPort.addProcessor(fpp);
+ }
+}
diff --git a/engine/src/test/jme3test/light/TestLightNode.java b/engine/src/test/jme3test/light/TestLightNode.java
new file mode 100644
index 0000000..955f24f
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestLightNode.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.LightNode;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Torus;
+
+public class TestLightNode extends SimpleApplication {
+
+ float angle;
+ Node movingNode;
+
+ public static void main(String[] args){
+ TestLightNode app = new TestLightNode();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Torus torus = new Torus(10, 6, 1, 3);
+// Torus torus = new Torus(50, 30, 1, 3);
+ Geometry g = new Geometry("Torus Geom", torus);
+ g.rotate(-FastMath.HALF_PI, 0, 0);
+ g.center();
+// g.move(0, 1, 0);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 32f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Black);
+ mat.setColor("Diffuse", ColorRGBA.White);
+ mat.setColor("Specular", ColorRGBA.White);
+// mat.setBoolean("VertexLighting", true);
+// mat.setBoolean("LowQuality", true);
+ g.setMaterial(mat);
+
+ rootNode.attachChild(g);
+
+ Geometry lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+
+ movingNode=new Node("lightParentNode");
+ movingNode.attachChild(lightMdl);
+ rootNode.attachChild(movingNode);
+
+ PointLight pl = new PointLight();
+ pl.setColor(ColorRGBA.Green);
+ pl.setRadius(4f);
+ rootNode.addLight(pl);
+
+ LightNode lightNode=new LightNode("pointLight", pl);
+ movingNode.attachChild(lightNode);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.Red);
+ dl.setDirection(new Vector3f(0, 1, 0));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+// cam.setLocation(new Vector3f(5.0347548f, 6.6481347f, 3.74853f));
+// cam.setRotation(new Quaternion(-0.19183293f, 0.80776674f, -0.37974006f, -0.40805697f));
+
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+
+ movingNode.setLocalTranslation(new Vector3f(FastMath.cos(angle) * 3f, 2, FastMath.sin(angle) * 3f));
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestLightRadius.java b/engine/src/test/jme3test/light/TestLightRadius.java
new file mode 100644
index 0000000..680639f
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestLightRadius.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Torus;
+
+public class TestLightRadius extends SimpleApplication {
+
+ float pos, vel=1;
+ PointLight pl;
+ Geometry lightMdl;
+
+ public static void main(String[] args){
+ TestLightRadius app = new TestLightRadius();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Torus torus = new Torus(10, 6, 1, 3);
+// Torus torus = new Torus(50, 30, 1, 3);
+ Geometry g = new Geometry("Torus Geom", torus);
+ g.rotate(-FastMath.HALF_PI, 0, 0);
+ g.center();
+// g.move(0, 1, 0);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 32f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Black);
+ mat.setColor("Diffuse", ColorRGBA.White);
+ mat.setColor("Specular", ColorRGBA.White);
+// mat.setBoolean("VertexLighting", true);
+// mat.setBoolean("LowQuality", true);
+ g.setMaterial(mat);
+
+ rootNode.attachChild(g);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.Green);
+ pl.setRadius(4f);
+ rootNode.addLight(pl);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.Red);
+ dl.setDirection(new Vector3f(0, 1, 0));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+// cam.setLocation(new Vector3f(5.0347548f, 6.6481347f, 3.74853f));
+// cam.setRotation(new Quaternion(-0.19183293f, 0.80776674f, -0.37974006f, -0.40805697f));
+
+ pos += tpf * vel * 5f;
+ if (pos > 15){
+ vel *= -1;
+ }else if (pos < -15){
+ vel *= -1;
+ }
+
+ pl.setPosition(new Vector3f(pos, 2, 0));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestManyLights.java b/engine/src/test/jme3test/light/TestManyLights.java
new file mode 100644
index 0000000..edd3af5
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestManyLights.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.scene.Node;
+
+public class TestManyLights extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestManyLights app = new TestManyLights();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(10);
+
+ Node scene = (Node) assetManager.loadModel("Scenes/ManyLights/Main.scene");
+ rootNode.attachChild(scene);
+// guiNode.setCullHint(CullHint.Always);
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/light/TestPssmShadow.java b/engine/src/test/jme3test/light/TestPssmShadow.java
new file mode 100644
index 0000000..67addf0
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestPssmShadow.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.shadow.PssmShadowRenderer;
+import com.jme3.shadow.PssmShadowRenderer.CompareMode;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+import java.util.Random;
+
+public class TestPssmShadow extends SimpleApplication implements ActionListener {
+
+ private Spatial teapot;
+ private boolean renderShadows = true;
+ private boolean hardwareShadows = false;
+ private PssmShadowRenderer pssmRenderer;
+
+ public static void main(String[] args){
+ TestPssmShadow app = new TestPssmShadow();
+ app.start();
+ }
+
+ public void loadScene(){
+ Material mat = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+ Material matSoil = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
+ matSoil.setColor("Color", ColorRGBA.Cyan);
+
+ teapot = new Geometry("sphere", new Sphere(30, 30, 2));
+// teapot = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f));
+// teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalTranslation(0,0,10);
+
+ teapot.setMaterial(mat);
+ teapot.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(teapot);
+
+ long seed = 1294719330150L; //System.currentTimeMillis();
+ Random random = new Random(seed);
+ System.out.println(seed);
+
+ for (int i = 0; i < 30; i++) {
+ Spatial t = teapot.clone(false);
+ rootNode.attachChild(t);
+ teapot.setLocalTranslation((float) random.nextFloat() * 3, (float) random.nextFloat() * 3, (i + 2));
+ }
+
+ Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700));
+ soil.setMaterial(matSoil);
+ soil.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(soil);
+
+ for (int i = 0; i < 30; i++) {
+ Spatial t = teapot.clone(false);
+ t.setLocalScale(10.0f);
+ rootNode.attachChild(t);
+ teapot.setLocalTranslation((float) random.nextFloat() * 300, (float) random.nextFloat() * 30, 30 * (i + 2));
+ }
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // put the camera in a bad position
+ cam.setLocation(new Vector3f(41.59757f, 34.38738f, 11.528807f));
+ cam.setRotation(new Quaternion(0.2905285f, 0.3816416f, -0.12772122f, 0.86811876f));
+ flyCam.setMoveSpeed(100);
+
+ loadScene();
+
+ pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
+ pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ pssmRenderer.setLambda(0.55f);
+ pssmRenderer.setShadowIntensity(0.6f);
+ pssmRenderer.setCompareMode(CompareMode.Software);
+ pssmRenderer.setFilterMode(FilterMode.Bilinear);
+ pssmRenderer.displayDebug();
+ viewPort.addProcessor(pssmRenderer);
+ initInputs();
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G));
+ inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp","ShadowDown","ThicknessUp","ThicknessDown");
+ }
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("toggle") && keyPressed) {
+ if (renderShadows) {
+ renderShadows = false;
+ viewPort.removeProcessor(pssmRenderer);
+ } else {
+ renderShadows = true;
+ viewPort.addProcessor(pssmRenderer);
+ }
+ } else if (name.equals("toggleHW") && keyPressed) {
+ hardwareShadows = !hardwareShadows;
+ pssmRenderer.setCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software);
+ System.out.println("HW Shadows: " + hardwareShadows);
+ }
+
+ if (name.equals("lambdaUp") && keyPressed) {
+ pssmRenderer.setLambda(pssmRenderer.getLambda() + 0.01f);
+ System.out.println("Lambda : " + pssmRenderer.getLambda());
+ } else if (name.equals("lambdaDown") && keyPressed) {
+ pssmRenderer.setLambda(pssmRenderer.getLambda() - 0.01f);
+ System.out.println("Lambda : " + pssmRenderer.getLambda());
+ }
+
+ if (name.equals("ShadowUp") && keyPressed) {
+ pssmRenderer.setShadowIntensity(pssmRenderer.getShadowIntensity() + 0.1f);
+ System.out.println("Shadow intensity : " + pssmRenderer.getShadowIntensity());
+ }
+ if (name.equals("ShadowDown") && keyPressed) {
+ pssmRenderer.setShadowIntensity(pssmRenderer.getShadowIntensity() - 0.1f);
+ System.out.println("Shadow intensity : " + pssmRenderer.getShadowIntensity());
+ }
+ if (name.equals("ThicknessUp") && keyPressed) {
+ pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() + 1);
+ System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness());
+ }
+ if (name.equals("ThicknessDown") && keyPressed) {
+ pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1);
+ System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness());
+ }
+ }
+
+
+}
diff --git a/engine/src/test/jme3test/light/TestShadow.java b/engine/src/test/jme3test/light/TestShadow.java
new file mode 100644
index 0000000..a8d168c
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestShadow.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.scene.shape.Box;
+import com.jme3.shadow.BasicShadowRenderer;
+import com.jme3.shadow.ShadowUtil;
+
+public class TestShadow extends SimpleApplication {
+
+ float angle;
+ Spatial lightMdl;
+ Spatial teapot;
+ Geometry frustumMdl;
+ WireFrustum frustum;
+
+ private BasicShadowRenderer bsr;
+ private Vector3f[] points;
+
+ {
+ points = new Vector3f[8];
+ for (int i = 0; i < points.length; i++) points[i] = new Vector3f();
+ }
+
+ public static void main(String[] args){
+ TestShadow app = new TestShadow();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // put the camera in a bad position
+ cam.setLocation(new Vector3f(0.7804813f, 1.7502685f, -2.1556435f));
+ cam.setRotation(new Quaternion(0.1961598f, -0.7213164f, 0.2266092f, 0.6243975f));
+ cam.setFrustumFar(10);
+
+ Material mat = assetManager.loadMaterial("Common/Materials/WhiteColor.j3m");
+ rootNode.setShadowMode(ShadowMode.Off);
+ Box floor = new Box(Vector3f.ZERO, 3, 0.1f, 3);
+ Geometry floorGeom = new Geometry("Floor", floor);
+ floorGeom.setMaterial(mat);
+ floorGeom.setLocalTranslation(0,-0.2f,0);
+ floorGeom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(floorGeom);
+
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalScale(2f);
+ teapot.setMaterial(mat);
+ teapot.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(teapot);
+// lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+// lightMdl.setMaterial(mat);
+// // disable shadowing for light representation
+// lightMdl.setShadowMode(ShadowMode.Off);
+// rootNode.attachChild(lightMdl);
+
+ bsr = new BasicShadowRenderer(assetManager, 512);
+ bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ viewPort.addProcessor(bsr);
+
+ frustum = new WireFrustum(bsr.getPoints());
+ frustumMdl = new Geometry("f", frustum);
+ frustumMdl.setCullHint(Spatial.CullHint.Never);
+ frustumMdl.setShadowMode(ShadowMode.Off);
+ frustumMdl.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
+ frustumMdl.getMaterial().getAdditionalRenderState().setWireframe(true);
+ frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red);
+ rootNode.attachChild(frustumMdl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ Camera shadowCam = bsr.getShadowCamera();
+ ShadowUtil.updateFrustumPoints2(shadowCam, points);
+
+ frustum.update(points);
+ // rotate teapot around Y axis
+ teapot.rotate(0, tpf * 0.25f, 0);
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestSimpleLighting.java b/engine/src/test/jme3test/light/TestSimpleLighting.java
new file mode 100644
index 0000000..5c94f81
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestSimpleLighting.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestSimpleLighting extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Geometry lightMdl;
+
+ public static void main(String[] args){
+ TestSimpleLighting app = new TestSimpleLighting();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Geometry teapot = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+ TangentBinormalGenerator.generate(teapot.getMesh(), true);
+
+ teapot.setLocalScale(2f);
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+// mat.selectTechnique("GBuf");
+ mat.setFloat("Shininess", 12);
+ mat.setBoolean("UseMaterialColors", true);
+
+// mat.setTexture("ColorRamp", assetManager.loadTexture("Textures/ColorRamp/cloudy.png"));
+//
+// mat.setBoolean("VTangent", true);
+// mat.setBoolean("Minnaert", true);
+// mat.setBoolean("WardIso", true);
+// mat.setBoolean("VertexLighting", true);
+// mat.setBoolean("LowQuality", true);
+// mat.setBoolean("HighQuality", true);
+
+ mat.setColor("Ambient", ColorRGBA.Black);
+ mat.setColor("Diffuse", ColorRGBA.Gray);
+ mat.setColor("Specular", ColorRGBA.Gray);
+
+ teapot.setMaterial(mat);
+ rootNode.attachChild(teapot);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ lightMdl.getMesh().setStatic();
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ pl.setRadius(4f);
+ rootNode.addLight(pl);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ dl.setColor(ColorRGBA.Green);
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+// cam.setLocation(new Vector3f(2.0632997f, 1.9493936f, 2.6885238f));
+// cam.setRotation(new Quaternion(-0.053555284f, 0.9407851f, -0.17754152f, -0.28378546f));
+
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 2f, 0.5f, FastMath.sin(angle) * 2f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestSpotLight.java b/engine/src/test/jme3test/light/TestSpotLight.java
new file mode 100644
index 0000000..91e6987
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestSpotLight.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestSpotLight extends SimpleApplication {
+
+ private Vector3f lightTarget = new Vector3f(12, 3.5f, 30);
+
+ public static void main(String[] args){
+ TestSpotLight app = new TestSpotLight();
+ app.start();
+ }
+
+ SpotLight spot;
+ Geometry lightMdl;
+ public void setupLighting(){
+ AmbientLight al=new AmbientLight();
+ al.setColor(ColorRGBA.White.mult(0.8f));
+ rootNode.addLight(al);
+
+ spot=new SpotLight();
+
+ spot.setSpotRange(1000);
+ spot.setSpotInnerAngle(5*FastMath.DEG_TO_RAD);
+ spot.setSpotOuterAngle(10*FastMath.DEG_TO_RAD);
+ spot.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f));
+ spot.setDirection(lightTarget.subtract(spot.getPosition()));
+ spot.setColor(ColorRGBA.White.mult(2));
+ rootNode.addLight(spot);
+
+
+// PointLight pl=new PointLight();
+// pl.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f));
+// pl.setRadius(1000);
+// pl.setColor(ColorRGBA.White.mult(2));
+// rootNode.addLight(pl);
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ lightMdl.setLocalTranslation(new Vector3f(77.70334f, 34.013165f, 27.1017f));
+ lightMdl.setLocalScale(5);
+ rootNode.attachChild(lightMdl);
+
+// DirectionalLight dl = new DirectionalLight();
+// dl.setDirection(lightTarget.subtract(new Vector3f(77.70334f, 34.013165f, 27.1017f)));
+// dl.setColor(ColorRGBA.White.mult(2));
+// rootNode.addLight(dl);
+
+
+ }
+
+ public void setupFloor(){
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+ // mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.setFloat("Shininess",3);
+ // mat.setBoolean("VertexLighting", true);
+
+
+ Box floor = new Box(Vector3f.ZERO, 50, 1f, 50);
+ TangentBinormalGenerator.generate(floor);
+ floor.scaleTextureCoordinates(new Vector2f(5, 5));
+ Geometry floorGeom = new Geometry("Floor", floor);
+ floorGeom.setMaterial(mat);
+ floorGeom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(floorGeom);
+ }
+
+
+
+ public void setupSignpost(){
+ Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
+ Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
+ // mat.setBoolean("VertexLighting", true);
+ signpost.setMaterial(mat);
+ signpost.rotate(0, FastMath.HALF_PI, 0);
+ signpost.setLocalTranslation(12, 3.5f, 30);
+ signpost.setLocalScale(4);
+ signpost.setShadowMode(ShadowMode.CastAndReceive);
+ TangentBinormalGenerator.generate(signpost);
+ rootNode.attachChild(signpost);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(27.492603f, 29.138166f, -13.232513f));
+ cam.setRotation(new Quaternion(0.25168246f, -0.10547892f, 0.02760565f, 0.96164864f));
+ flyCam.setMoveSpeed(30);
+
+ setupLighting();
+ setupFloor();
+ setupSignpost();
+
+
+ }
+
+ float angle;
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ super.simpleUpdate(tpf);
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+
+ spot.setPosition(new Vector3f(FastMath.cos(angle) * 30f, 34.013165f, FastMath.sin(angle) * 30f));
+ lightMdl.setLocalTranslation(spot.getPosition());
+ spot.setDirection(lightTarget.subtract(spot.getPosition()));
+ }
+
+
+
+}
diff --git a/engine/src/test/jme3test/light/TestSpotLightTerrain.java b/engine/src/test/jme3test/light/TestSpotLightTerrain.java
new file mode 100644
index 0000000..7e6139f
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestSpotLightTerrain.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.font.BitmapText;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import jme3tools.converters.ImageToAwt;
+
+/**
+ * Uses the terrain's lighting texture with normal maps and lights.
+ *
+ * @author bowens
+ */
+public class TestSpotLightTerrain extends SimpleApplication {
+
+ private TerrainQuad terrain;
+ Material matTerrain;
+ Material matWire;
+ boolean wireframe = false;
+ boolean triPlanar = false;
+ boolean wardiso = false;
+ boolean minnaert = false;
+ protected BitmapText hintText;
+ PointLight pl;
+ Geometry lightMdl;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+ SpotLight sl;
+
+ public static void main(String[] args) {
+ TestSpotLightTerrain app = new TestSpotLightTerrain();
+ app.start();
+ }
+
+
+ @Override
+ public void simpleInitApp() {
+ makeTerrain();
+ flyCam.setMoveSpeed(50);
+
+ sl = new SpotLight();
+ sl.setSpotRange(100);
+ sl.setSpotOuterAngle(20 * FastMath.DEG_TO_RAD);
+ sl.setSpotInnerAngle(15 * FastMath.DEG_TO_RAD);
+ sl.setDirection(new Vector3f(-0.39820394f, -0.73094344f, 0.55421597f));
+ sl.setPosition(new Vector3f(-64.61567f, -87.615425f, -202.41328f));
+ rootNode.addLight(sl);
+
+ AmbientLight ambLight = new AmbientLight();
+ ambLight.setColor(new ColorRGBA(0.8f, 0.8f, 0.8f, 0.2f));
+ rootNode.addLight(ambLight);
+
+ cam.setLocation(new Vector3f(-41.219646f, -84.8363f, -171.67267f));
+ cam.setRotation(new Quaternion(-0.04562731f, 0.89917684f, -0.09668826f, -0.4243236f));
+ sl.setDirection(cam.getDirection());
+ sl.setPosition(cam.getLocation());
+
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ super.simpleUpdate(tpf);
+ sl.setDirection(cam.getDirection());
+ sl.setPosition(cam.getLocation());
+
+ }
+
+ private void makeTerrain() {
+ // TERRAIN TEXTURE material
+ matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matTerrain.setBoolean("useTriPlanarMapping", false);
+ matTerrain.setBoolean("WardIso", true);
+
+ // ALPHA map (for splat textures)
+ matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png"));
+ matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png"));
+
+ // HEIGHTMAP image (for the terrain heightmap)
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+
+
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap", grass);
+ matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_1", dirt);
+ matTerrain.setFloat("DiffuseMap_1_scale", dirtScale);
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_2", rock);
+ matTerrain.setFloat("DiffuseMap_2_scale", rockScale);
+
+ // BRICK texture
+ Texture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");
+ brick.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_3", brick);
+ matTerrain.setFloat("DiffuseMap_3_scale", rockScale);
+
+ // RIVER ROCK texture
+ Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg");
+ riverRock.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_4", riverRock);
+ matTerrain.setFloat("DiffuseMap_4_scale", rockScale);
+
+
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("NormalMap", normalMap0);
+ matTerrain.setTexture("NormalMap_1", normalMap2);
+ matTerrain.setTexture("NormalMap_2", normalMap2);
+ matTerrain.setTexture("NormalMap_4", normalMap2);
+
+ // WIREFRAME material
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+
+ createSky();
+
+ // CREATE HEIGHTMAP
+ AbstractHeightMap heightmap = null;
+ try {
+ //heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3);
+
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);
+ heightmap.load();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
+ TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) );
+ terrain.addControl(control);
+ terrain.setMaterial(matTerrain);
+ terrain.setModelBound(new BoundingBox());
+ terrain.updateModelBound();
+ terrain.setLocalTranslation(0, -100, 0);
+ terrain.setLocalScale(1f, 1f, 1f);
+ rootNode.attachChild(terrain);
+ }
+
+ private void createSky() {
+ Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
+ Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
+ Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
+ Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
+ Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
+ Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
+
+ Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
+ rootNode.attachChild(sky);
+ }
+
+
+}
diff --git a/engine/src/test/jme3test/light/TestTangentGen.java b/engine/src/test/jme3test/light/TestTangentGen.java
new file mode 100644
index 0000000..a810d6a
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestTangentGen.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.shape.Quad;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TangentBinormalGenerator;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+
+public class TestTangentGen extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Geometry lightMdl;
+
+ public static void main(String[] args){
+ TestTangentGen app = new TestTangentGen();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(20);
+ Sphere sphereMesh = new Sphere(32, 32, 1);
+ sphereMesh.setTextureMode(Sphere.TextureMode.Projected);
+ sphereMesh.updateGeometry(32, 32, 1, false, false);
+ addMesh("Sphere", sphereMesh, new Vector3f(-1, 0, 0));
+
+ Quad quadMesh = new Quad(1, 1);
+ quadMesh.updateGeometry(1, 1);
+ addMesh("Quad", quadMesh, new Vector3f(1, 0, 0));
+
+ Mesh strip = createTriangleStripMesh();
+ addMesh("strip", strip, new Vector3f(0, -3, 0));
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, -1, -1).normalizeLocal());
+ dl.setColor(ColorRGBA.White);
+ rootNode.addLight(dl);
+ }
+
+ private void addMesh(String name, Mesh mesh, Vector3f translation) {
+ TangentBinormalGenerator.generate(mesh);
+
+ Geometry testGeom = new Geometry(name, mesh);
+ Material mat = assetManager.loadMaterial("Textures/BumpMapTest/Tangent.j3m");
+ testGeom.setMaterial(mat);
+ testGeom.getLocalTranslation().set(translation);
+ rootNode.attachChild(testGeom);
+
+ Geometry debug = new Geometry(
+ "Debug " + name,
+ TangentBinormalGenerator.genTbnLines(mesh, 0.08f)
+ );
+ Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
+ debug.setMaterial(debugMat);
+ debug.setCullHint(Spatial.CullHint.Never);
+ debug.getLocalTranslation().set(translation);
+ rootNode.attachChild(debug);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ }
+
+ private Mesh createTriangleStripMesh() {
+ Mesh strip = new Mesh();
+ strip.setMode(Mode.TriangleStrip);
+ FloatBuffer vb = BufferUtils.createFloatBuffer(3*3*3); // 3 rows * 3 columns * 3 floats
+ vb.rewind();
+ vb.put(new float[]{0,2,0}); vb.put(new float[]{1,2,0}); vb.put(new float[]{2,2,0});
+ vb.put(new float[]{0,1,0}); vb.put(new float[]{1,1,0}); vb.put(new float[]{2,1,0});
+ vb.put(new float[]{0,0,0}); vb.put(new float[]{1,0,0}); vb.put(new float[]{2,0,0});
+ FloatBuffer nb = BufferUtils.createFloatBuffer(3*3*3);
+ nb.rewind();
+ nb.put(new float[]{0,0,1}); nb.put(new float[]{0,0,1}); nb.put(new float[]{0,0,1});
+ nb.put(new float[]{0,0,1}); nb.put(new float[]{0,0,1}); nb.put(new float[]{0,0,1});
+ nb.put(new float[]{0,0,1}); nb.put(new float[]{0,0,1}); nb.put(new float[]{0,0,1});
+ FloatBuffer tb = BufferUtils.createFloatBuffer(3*3*2);
+ tb.rewind();
+ tb.put(new float[]{0,0}); tb.put(new float[]{0.5f,0}); tb.put(new float[]{1,0});
+ tb.put(new float[]{0,0.5f}); tb.put(new float[]{0.5f,0.5f}); tb.put(new float[]{1,0.5f});
+ tb.put(new float[]{0,1}); tb.put(new float[]{0.5f,1}); tb.put(new float[]{1,1});
+ int[] indexes = new int[]{0,3,1,4,2,5, 5,3, 3,6,4,7,5,8};
+ IntBuffer ib = BufferUtils.createIntBuffer(indexes.length);
+ ib.put(indexes);
+ strip.setBuffer(Type.Position, 3, vb);
+ strip.setBuffer(Type.Normal, 3, nb);
+ strip.setBuffer(Type.TexCoord, 2, tb);
+ strip.setBuffer(Type.Index, 3, ib);
+ strip.updateBound();
+ return strip;
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestTangentGenBadModels.java b/engine/src/test/jme3test/light/TestTangentGenBadModels.java
new file mode 100644
index 0000000..a786d76
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestTangentGenBadModels.java
@@ -0,0 +1,136 @@
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.*;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+/**
+ *
+ * @author Kirusha
+ */
+public class TestTangentGenBadModels extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Geometry lightMdl;
+
+ public static void main(String[] args){
+ TestTangentGenBadModels app = new TestTangentGenBadModels();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+// assetManager.registerLocator("http://jme-glsl-shaders.googlecode.com/hg/assets/Models/LightBlow/", UrlLocator.class);
+// assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/", UrlLocator.class);
+
+ final Spatial badModel = assetManager.loadModel("Models/TangentBugs/test.blend");
+// badModel.setLocalScale(1f);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setTexture("NormalMap", assetManager.loadTexture("Models/TangentBugs/test_normal.png"));
+// Material mat = assetManager.loadMaterial("Textures/BumpMapTest/Tangent.j3m");
+ badModel.setMaterial(mat);
+ rootNode.attachChild(badModel);
+
+ // TODO: For some reason blender loader fails to load this.
+ // need to check it
+// Spatial model = assetManager.loadModel("test.blend");
+// rootNode.attachChild(model);
+
+ final Node debugTangents = new Node("debug tangents");
+ debugTangents.setCullHint(CullHint.Always);
+ rootNode.attachChild(debugTangents);
+
+ final Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
+
+ badModel.depthFirstTraversal(new SceneGraphVisitorAdapter(){
+ @Override
+ public void visit(Geometry g){
+ Mesh m = g.getMesh();
+ Material mat = g.getMaterial();
+
+// if (mat.getParam("DiffuseMap") != null){
+// mat.setTexture("DiffuseMap", null);
+// }
+ TangentBinormalGenerator.generate(m);
+
+ Geometry debug = new Geometry(
+ "debug tangents geom",
+ TangentBinormalGenerator.genTbnLines(g.getMesh(), 0.2f)
+ );
+ debug.getMesh().setLineWidth(1);
+ debug.setMaterial(debugMat);
+ debug.setCullHint(Spatial.CullHint.Never);
+ debug.setLocalTransform(g.getWorldTransform());
+ debugTangents.attachChild(debug);
+ }
+ });
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.8f, -0.6f, -0.08f).normalizeLocal());
+ dl.setColor(new ColorRGBA(1,1,1,1));
+ rootNode.addLight(dl);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ lightMdl.getMesh().setStatic();
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+// rootNode.addLight(pl);
+
+
+ BitmapText info = new BitmapText(guiFont);
+ info.setText("Press SPACE to switch between lighting and tangent display");
+ info.setQueueBucket(Bucket.Gui);
+ info.move(0, settings.getHeight() - info.getLineHeight(), 0);
+ rootNode.attachChild(info);
+
+ inputManager.addMapping("space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(new ActionListener() {
+
+ private boolean isLit = true;
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (isPressed) return;
+ Material mat;
+ if (isLit){
+ mat = assetManager.loadMaterial("Textures/BumpMapTest/Tangent.j3m");
+ debugTangents.setCullHint(CullHint.Inherit);
+ }else{
+ mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setTexture("NormalMap", assetManager.loadTexture("Models/TangentBugs/test_normal.png"));
+ debugTangents.setCullHint(CullHint.Always);
+ }
+ isLit = !isLit;
+ badModel.setMaterial(mat);
+ }
+ }, "space");
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 2f, 2f, FastMath.sin(angle) * 2f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+
+}
diff --git a/engine/src/test/jme3test/light/TestTangentGenBadUV.java b/engine/src/test/jme3test/light/TestTangentGenBadUV.java
new file mode 100644
index 0000000..7816c71
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestTangentGenBadUV.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestTangentGenBadUV extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Geometry lightMdl;
+
+ public static void main(String[] args){
+ TestTangentGenBadUV app = new TestTangentGenBadUV();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ if (teapot instanceof Geometry){
+ Geometry g = (Geometry) teapot;
+ TangentBinormalGenerator.generate(g.getMesh());
+ }else{
+ throw new RuntimeException();
+ }
+ teapot.setLocalScale(2f);
+ Material mat = assetManager.loadMaterial("Textures/BumpMapTest/Tangent.j3m");
+ teapot.setMaterial(mat);
+ rootNode.attachChild(teapot);
+
+ Geometry debug = new Geometry(
+ "Debug Teapot",
+ TangentBinormalGenerator.genTbnLines(((Geometry) teapot).getMesh(), 0.03f)
+ );
+ Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
+ debug.setMaterial(debugMat);
+ debug.setCullHint(Spatial.CullHint.Never);
+ debug.getLocalTranslation().set(teapot.getLocalTranslation());
+ debug.getLocalScale().set(teapot.getLocalScale());
+ rootNode.attachChild(debug);
+
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1,-1,-1).normalizeLocal());
+ dl.setColor(ColorRGBA.White);
+ rootNode.addLight(dl);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ lightMdl.getMesh().setStatic();
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ //pl.setRadius(3f);
+ rootNode.addLight(pl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 2f, 0.5f, FastMath.sin(angle) * 2f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/light/TestTransparentShadow.java b/engine/src/test/jme3test/light/TestTransparentShadow.java
new file mode 100644
index 0000000..727dfa8
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestTransparentShadow.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.SceneGraphVisitorAdapter;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.LodControl;
+import com.jme3.scene.shape.Quad;
+import com.jme3.shadow.PssmShadowRenderer;
+import com.jme3.shadow.PssmShadowRenderer.CompareMode;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+
+public class TestTransparentShadow extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestTransparentShadow app = new TestTransparentShadow();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+
+ cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f));
+ cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f, -0.13857932f));
+
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+
+ Quad q = new Quad(20, 20);
+ q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5));
+ Geometry geom = new Geometry("floor", q);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ geom.setMaterial(mat);
+
+ geom.rotate(-FastMath.HALF_PI, 0, 0);
+ geom.center();
+ geom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(geom);
+
+ // create the geometry and attach it
+ Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
+ teaGeom.setQueueBucket(Bucket.Transparent);
+ teaGeom.setShadowMode(ShadowMode.Cast);
+
+ teaGeom.depthFirstTraversal(new SceneGraphVisitorAdapter(){
+ @Override
+ public void visit(Geometry geom) {
+ LodControl lodCtrl = new LodControl();
+ lodCtrl.setTrisPerPixel(0.25f);
+ geom.addControl(lodCtrl);
+ }
+ });
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White.mult(2));
+ rootNode.addLight(al);
+
+ DirectionalLight dl1 = new DirectionalLight();
+ dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal());
+ dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+ rootNode.addLight(dl1);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+ rootNode.addLight(dl);
+
+ rootNode.attachChild(teaGeom);
+
+ /** Uses Texture from jme3-test-data library! */
+ ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
+ Material mat_red = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat_red.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+ //mat_red.getAdditionalRenderState().setDepthTest(true);
+ //mat_red.getAdditionalRenderState().setDepthWrite(true);
+ fire.setMaterial(mat_red);
+ fire.setImagesX(2); fire.setImagesY(2); // 2x2 texture animation
+ fire.setEndColor( new ColorRGBA(1f, 0f, 0f, 1f)); // red
+ fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+ fire.setInitialVelocity(new Vector3f(0, 2, 0));
+ fire.setStartSize(0.6f);
+ fire.setEndSize(0.1f);
+ fire.setGravity(0, 0, 0);
+ fire.setLowLife(0.5f);
+ fire.setHighLife(1.5f);
+ fire.setVelocityVariation(0.3f);
+ fire.setLocalTranslation(1.0f, 0, 1.0f);
+ fire.setLocalScale(0.3f);
+ fire.setQueueBucket(Bucket.Translucent);
+ rootNode.attachChild(fire);
+
+
+ PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 1);
+ pssmRenderer.setDirection(new Vector3f(0.01f, -1f, 0.01f).normalizeLocal());
+ pssmRenderer.setLambda(0.55f);
+ pssmRenderer.setShadowIntensity(0.6f);
+ pssmRenderer.setCompareMode(CompareMode.Software);
+ pssmRenderer.setFilterMode(FilterMode.PCF4);
+ pssmRenderer.displayDebug();
+ viewPort.addProcessor(pssmRenderer);
+ }
+}
diff --git a/engine/src/test/jme3test/material/TestBumpModel.java b/engine/src/test/jme3test/material/TestBumpModel.java
new file mode 100644
index 0000000..c10ccbe
--- /dev/null
+++ b/engine/src/test/jme3test/material/TestBumpModel.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.ogre.OgreMeshKey;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestBumpModel extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestBumpModel app = new TestBumpModel();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Spatial signpost = (Spatial) assetManager.loadAsset(new OgreMeshKey("Models/Sign Post/Sign Post.mesh.xml"));
+ signpost.setMaterial( (Material) assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"));
+ TangentBinormalGenerator.generate(signpost);
+ rootNode.attachChild(signpost);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial( (Material) assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ // flourescent main light
+ pl = new PointLight();
+ pl.setColor(new ColorRGBA(0.88f, 0.92f, 0.95f, 1.0f));
+ rootNode.addLight(pl);
+
+ // sunset light
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f,-0.7f,1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.44f, 0.30f, 0.20f, 1.0f));
+ rootNode.addLight(dl);
+
+ // skylight
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.6f,-1,-0.6f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.10f, 0.22f, 0.44f, 1.0f));
+ rootNode.addLight(dl);
+
+ // white ambient light
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, -0.5f,-0.1f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.50f, 0.40f, 0.50f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 6f, 3f, FastMath.sin(angle) * 6f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/material/TestColoredTexture.java b/engine/src/test/jme3test/material/TestColoredTexture.java
new file mode 100644
index 0000000..8beb771
--- /dev/null
+++ b/engine/src/test/jme3test/material/TestColoredTexture.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+
+public class TestColoredTexture extends SimpleApplication {
+
+ private float time = 0;
+ private ColorRGBA nextColor;
+ private ColorRGBA prevColor;
+ private Material mat;
+
+ public static void main(String[] args){
+ TestColoredTexture app = new TestColoredTexture();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Quad quadMesh = new Quad(512,512);
+ Geometry quad = new Geometry("Quad", quadMesh);
+ quad.setQueueBucket(Bucket.Gui);
+
+ mat = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
+ quad.setMaterial(mat);
+ guiNode.attachChildAt(quad, 0);
+
+ nextColor = ColorRGBA.randomColor();
+ prevColor = ColorRGBA.Black;
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ time += tpf;
+ if (time > 1f){
+ time -= 1f;
+ prevColor = nextColor;
+ nextColor = ColorRGBA.randomColor();
+ }
+ ColorRGBA currentColor = new ColorRGBA();
+ currentColor.interpolate(prevColor, nextColor, time);
+
+ mat.setColor("Color", currentColor);
+ }
+
+}
diff --git a/engine/src/test/jme3test/material/TestNormalMapping.java b/engine/src/test/jme3test/material/TestNormalMapping.java
new file mode 100644
index 0000000..4ad3abe
--- /dev/null
+++ b/engine/src/test/jme3test/material/TestNormalMapping.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestNormalMapping extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestNormalMapping app = new TestNormalMapping();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Sphere sphMesh = new Sphere(32, 32, 1);
+ sphMesh.setTextureMode(Sphere.TextureMode.Projected);
+ sphMesh.updateGeometry(32, 32, 1, false, false);
+ TangentBinormalGenerator.generate(sphMesh);
+
+ Geometry sphere = new Geometry("Rock Ball", sphMesh);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ sphere.setMaterial(mat);
+ rootNode.attachChild(sphere);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ pl.setPosition(new Vector3f(0f, 0f, 4f));
+ rootNode.addLight(pl);
+
+// DirectionalLight dl = new DirectionalLight();
+// dl.setDirection(new Vector3f(1,-1,1).normalizeLocal());
+// dl.setColor(new ColorRGBA(0.22f, 0.15f, 0.1f, 1.0f));
+// rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 4f, 0.5f, FastMath.sin(angle) * 4f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/material/TestParallax.java b/engine/src/test/jme3test/material/TestParallax.java
new file mode 100644
index 0000000..b52ad3c
--- /dev/null
+++ b/engine/src/test/jme3test/material/TestParallax.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.FXAAFilter;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestParallax extends SimpleApplication {
+
+ private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal();
+
+ public static void main(String[] args) {
+ TestParallax app = new TestParallax();
+ app.start();
+ }
+
+ public void setupSkyBox() {
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false));
+ }
+ DirectionalLight dl;
+
+ public void setupLighting() {
+
+ dl = new DirectionalLight();
+ dl.setDirection(lightDir);
+ dl.setColor(new ColorRGBA(.9f, .9f, .9f, 1));
+ rootNode.addLight(dl);
+ }
+ Material mat;
+
+ public void setupFloor() {
+ mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall2.j3m");
+ mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.setFloat("Shininess", 0);
+
+ // Node floorGeom = (Node) assetManager.loadAsset("Models/WaterTest/WaterTest.mesh.xml");
+ //Geometry g = ((Geometry) floorGeom.getChild(0));
+ //g.getMesh().scaleTextureCoordinates(new Vector2f(10, 10));
+
+ Node floorGeom = new Node("floorGeom");
+ Quad q = new Quad(100, 100);
+ q.scaleTextureCoordinates(new Vector2f(10, 10));
+ Geometry g = new Geometry("geom", q);
+ g.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
+ floorGeom.attachChild(g);
+
+
+ TangentBinormalGenerator.generate(floorGeom);
+ floorGeom.setLocalTranslation(-50, 22, 60);
+ //floorGeom.setLocalScale(100);
+
+ floorGeom.setMaterial(mat);
+ rootNode.attachChild(floorGeom);
+ }
+
+ public void setupSignpost() {
+ Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
+ Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
+ TangentBinormalGenerator.generate(signpost);
+ signpost.setMaterial(mat);
+ signpost.rotate(0, FastMath.HALF_PI, 0);
+ signpost.setLocalTranslation(12, 23.5f, 30);
+ signpost.setLocalScale(4);
+ signpost.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(signpost);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(-15.445636f, 30.162927f, 60.252777f));
+ cam.setRotation(new Quaternion(0.05173137f, 0.92363626f, -0.13454558f, 0.35513034f));
+
+ flyCam.setMoveSpeed(30);
+
+
+ setupLighting();
+ setupSkyBox();
+ setupFloor();
+ setupSignpost();
+
+ inputManager.addListener(new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+ if ("heightUP".equals(name)) {
+ parallaxHeigh += 0.0001;
+ mat.setFloat("ParallaxHeight", parallaxHeigh);
+ }
+ if ("heightDown".equals(name)) {
+ parallaxHeigh -= 0.0001;
+ parallaxHeigh = Math.max(parallaxHeigh, 0);
+ mat.setFloat("ParallaxHeight", parallaxHeigh);
+ }
+
+ }
+ }, "heightUP", "heightDown");
+ inputManager.addMapping("heightUP", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("heightDown", new KeyTrigger(KeyInput.KEY_K));
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (isPressed && "toggleSteep".equals(name)) {
+ steep = !steep;
+ mat.setBoolean("SteepParallax", steep);
+ }
+ }
+ }, "toggleSteep");
+ inputManager.addMapping("toggleSteep", new KeyTrigger(KeyInput.KEY_SPACE));
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ FXAAFilter fxaa = new FXAAFilter();
+ fxaa.setReduceMul(0.08f);
+ fpp.addFilter(fxaa);
+ viewPort.addProcessor(fpp);
+ }
+ float parallaxHeigh = 0.05f;
+ float time = 0;
+ boolean steep = false;
+
+ @Override
+ public void simpleUpdate(float tpf) {
+// time+=tpf;
+// lightDir.set(FastMath.sin(time), -1, FastMath.cos(time));
+// bsr.setDirection(lightDir);
+// dl.setDirection(lightDir);
+ }
+}
diff --git a/engine/src/test/jme3test/material/TestSimpleBumps.java b/engine/src/test/jme3test/material/TestSimpleBumps.java
new file mode 100644
index 0000000..6155196
--- /dev/null
+++ b/engine/src/test/jme3test/material/TestSimpleBumps.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+// phong cutoff for light to normal angle > 90?
+public class TestSimpleBumps extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestSimpleBumps app = new TestSimpleBumps();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Quad quadMesh = new Quad(1, 1);
+
+ Geometry sphere = new Geometry("Rock Ball", quadMesh);
+ Material mat = assetManager.loadMaterial("Textures/BumpMapTest/SimpleBump.j3m");
+ sphere.setMaterial(mat);
+ TangentBinormalGenerator.generate(sphere);
+ rootNode.attachChild(sphere);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ pl.setPosition(new Vector3f(0f, 0f, 4f));
+ rootNode.addLight(pl);
+
+// DirectionalLight dl = new DirectionalLight();
+// dl.setDirection(new Vector3f(1, -1, -1).normalizeLocal());
+// dl.setColor(new ColorRGBA(0.22f, 0.15f, 0.1f, 1.0f));
+// rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 4f, 0.5f, FastMath.sin(angle) * 4f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/material/TestUnshadedModel.java b/engine/src/test/jme3test/material/TestUnshadedModel.java
new file mode 100644
index 0000000..826ef44
--- /dev/null
+++ b/engine/src/test/jme3test/material/TestUnshadedModel.java
@@ -0,0 +1,44 @@
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestUnshadedModel extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestUnshadedModel app = new TestUnshadedModel();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Sphere sphMesh = new Sphere(32, 32, 1);
+ sphMesh.setTextureMode(Sphere.TextureMode.Projected);
+ sphMesh.updateGeometry(32, 32, 1, false, false);
+ TangentBinormalGenerator.generate(sphMesh);
+
+ Geometry sphere = new Geometry("Rock Ball", sphMesh);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ mat.setColor("Ambient", ColorRGBA.DarkGray);
+ mat.setColor("Diffuse", ColorRGBA.White);
+ mat.setBoolean("UseMaterialColors", true);
+ sphere.setMaterial(mat);
+ rootNode.attachChild(sphere);
+
+ PointLight pl = new PointLight();
+ pl.setColor(ColorRGBA.White);
+ pl.setPosition(new Vector3f(4f, 0f, 0f));
+ rootNode.addLight(pl);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White);
+ rootNode.addLight(al);
+ }
+}
diff --git a/engine/src/test/jme3test/math/TestHalfFloat.java b/engine/src/test/jme3test/math/TestHalfFloat.java
new file mode 100644
index 0000000..17365d3
--- /dev/null
+++ b/engine/src/test/jme3test/math/TestHalfFloat.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.math;
+
+import com.jme3.math.FastMath;
+import java.util.Scanner;
+
+public class TestHalfFloat {
+ public static void main(String[] args){
+ Scanner scan = new Scanner(System.in);
+ while (true){
+ System.out.println("Enter float to convert or 'x' to exit: ");
+ String s = scan.nextLine();
+ if (s.equals("x"))
+ break;
+
+ float flt = Float.valueOf(s);
+ short half = FastMath.convertFloatToHalf(flt);
+ float flt2 = FastMath.convertHalfToFloat(half);
+
+ System.out.println("Input float: "+flt);
+ System.out.println("Result float: "+flt2);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/model/TestHoverTank.java b/engine/src/test/jme3test/model/TestHoverTank.java
new file mode 100644
index 0000000..62656c4
--- /dev/null
+++ b/engine/src/test/jme3test/model/TestHoverTank.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.model;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.ChaseCamera;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.LodControl;
+import jme3test.post.BloomUI;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TestHoverTank extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestHoverTank app = new TestHoverTank();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Node tank = (Node) assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml");
+
+ flyCam.setEnabled(false);
+ ChaseCamera chaseCam = new ChaseCamera(cam, tank, inputManager);
+ chaseCam.setSmoothMotion(true);
+ chaseCam.setMaxDistance(100000);
+ chaseCam.setMinVerticalRotation(-FastMath.PI / 2);
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+
+ Geometry tankGeom = (Geometry) tank.getChild(0);
+ LodControl control = new LodControl();
+ tankGeom.addControl(control);
+ rootNode.attachChild(tank);
+
+ Vector3f lightDir = new Vector3f(-0.8719428f, -0.46824604f, 0.14304268f);
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(new ColorRGBA(1.0f, 0.92f, 0.75f, 1f));
+ dl.setDirection(lightDir);
+
+ Vector3f lightDir2 = new Vector3f(0.70518064f, 0.5902297f, -0.39287305f);
+ DirectionalLight dl2 = new DirectionalLight();
+ dl2.setColor(new ColorRGBA(0.7f, 0.85f, 1.0f, 1f));
+ dl2.setDirection(lightDir2);
+
+ rootNode.addLight(dl);
+ rootNode.addLight(dl2);
+ rootNode.attachChild(tank);
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ BloomFilter bf = new BloomFilter(BloomFilter.GlowMode.Objects);
+ bf.setBloomIntensity(2.0f);
+ bf.setExposurePower(1.3f);
+ fpp.addFilter(bf);
+ BloomUI bui = new BloomUI(inputManager, bf);
+ viewPort.addProcessor(fpp);
+ }
+}
diff --git a/engine/src/test/jme3test/model/TestMonkeyHead.java b/engine/src/test/jme3test/model/TestMonkeyHead.java
new file mode 100644
index 0000000..396195f
--- /dev/null
+++ b/engine/src/test/jme3test/model/TestMonkeyHead.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+
+public class TestMonkeyHead extends SimpleApplication {
+
+ float angle;
+ PointLight pl;
+ Spatial lightMdl;
+
+ public static void main(String[] args){
+ TestMonkeyHead app = new TestMonkeyHead();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+
+ Spatial bumpy = (Spatial) assetManager.loadModel("Models/MonkeyHead/MonkeyHead.mesh.xml");
+ rootNode.attachChild(bumpy);
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ // flourescent main light
+ pl = new PointLight();
+ pl.setColor(new ColorRGBA(0.88f, 0.92f, 0.95f, 1.0f));
+ rootNode.addLight(pl);
+
+ // sunset light
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f,-0.7f,1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.44f, 0.30f, 0.20f, 1.0f));
+ rootNode.addLight(dl);
+
+ // skylight
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.6f,-1,-0.6f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.10f, 0.22f, 0.44f, 1.0f));
+ rootNode.addLight(dl);
+
+ // white ambient light
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, -0.5f,-0.1f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.50f, 0.40f, 0.50f, 1.0f));
+ rootNode.addLight(dl);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ angle += tpf * 0.25f;
+ angle %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle) * 6f, 3f, FastMath.sin(angle) * 6f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/TestObjLoading.java b/engine/src/test/jme3test/model/TestObjLoading.java
new file mode 100644
index 0000000..a94a708
--- /dev/null
+++ b/engine/src/test/jme3test/model/TestObjLoading.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.scene.Geometry;
+
+/**
+ * Tests OBJ format loading
+ */
+public class TestObjLoading extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestObjLoading app = new TestObjLoading();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ // create the geometry and attach it
+ Geometry teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+
+ // show normals as material
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+ teaGeom.setMaterial(mat);
+
+ rootNode.attachChild(teaGeom);
+ }
+}
diff --git a/engine/src/test/jme3test/model/TestOgreLoading.java b/engine/src/test/jme3test/model/TestOgreLoading.java
new file mode 100644
index 0000000..8676b1e
--- /dev/null
+++ b/engine/src/test/jme3test/model/TestOgreLoading.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.model;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+
+public class TestOgreLoading extends SimpleApplication {
+
+ float angle1;
+ float angle2;
+ PointLight pl;
+ PointLight p2;
+ Spatial lightMdl;
+ Spatial lightMd2;
+
+ public static void main(String[] args) {
+ TestOgreLoading app = new TestOgreLoading();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+// PointLight pl = new PointLight();
+// pl.setPosition(new Vector3f(10, 10, -10));
+// rootNode.addLight(pl);
+ flyCam.setMoveSpeed(10f);
+
+ // sunset light
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, 1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+
+
+ lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ rootNode.attachChild(lightMdl);
+
+ lightMd2 = new Geometry("Light", new Sphere(10, 10, 0.1f));
+ lightMd2.setMaterial(assetManager.loadMaterial("Common/Materials/WhiteColor.j3m"));
+ rootNode.attachChild(lightMd2);
+
+
+ pl = new PointLight();
+ pl.setColor(new ColorRGBA(1, 0.9f, 0.9f, 0));
+ pl.setPosition(new Vector3f(0f, 0f, 4f));
+ rootNode.addLight(pl);
+
+ p2 = new PointLight();
+ p2.setColor(new ColorRGBA(0.9f, 1, 0.9f, 0));
+ p2.setPosition(new Vector3f(0f, 0f, 3f));
+ rootNode.addLight(p2);
+
+
+ // create the geometry and attach it
+ Spatial elephant = (Spatial) assetManager.loadModel("Models/Elephant/Elephant.mesh.xml");
+ float scale = 0.05f;
+ elephant.scale(scale, scale, scale);
+ rootNode.attachChild(elephant);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ angle1 += tpf * 0.25f;
+ angle1 %= FastMath.TWO_PI;
+
+ angle2 += tpf * 0.50f;
+ angle2 %= FastMath.TWO_PI;
+
+ pl.setPosition(new Vector3f(FastMath.cos(angle1) * 4f, 0.5f, FastMath.sin(angle1) * 4f));
+ p2.setPosition(new Vector3f(FastMath.cos(angle2) * 4f, 0.5f, FastMath.sin(angle2) * 4f));
+ lightMdl.setLocalTranslation(pl.getPosition());
+ lightMd2.setLocalTranslation(p2.getPosition());
+ }
+}
diff --git a/engine/src/test/jme3test/model/anim/TestAnimBlendBug.java b/engine/src/test/jme3test/model/anim/TestAnimBlendBug.java
new file mode 100644
index 0000000..df487a3
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestAnimBlendBug.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.debug.SkeletonDebugger;
+
+public class TestAnimBlendBug extends SimpleApplication implements ActionListener {
+
+// private AnimControl control;
+ private AnimChannel channel1, channel2;
+ private String[] animNames;
+
+ private float blendTime = 0.5f;
+ private float lockAfterBlending = blendTime + 0.25f;
+ private float blendingAnimationLock;
+
+ public static void main(String[] args) {
+ TestAnimBlendBug app = new TestAnimBlendBug();
+ app.start();
+ }
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (name.equals("One") && value){
+ channel1.setAnim(animNames[4], blendTime);
+ channel2.setAnim(animNames[4], 0);
+ channel1.setSpeed(0.25f);
+ channel2.setSpeed(0.25f);
+ blendingAnimationLock = lockAfterBlending;
+ }
+ }
+
+ public void onPreUpdate(float tpf) {
+ }
+
+ public void onPostUpdate(float tpf) {
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ // Is there currently a blending underway?
+ if (blendingAnimationLock > 0f) {
+ blendingAnimationLock -= tpf;
+ }
+ }
+
+ @Override
+ public void simpleInitApp() {
+ inputManager.addMapping("One", new KeyTrigger(KeyInput.KEY_1));
+ inputManager.addListener(this, "One");
+
+ flyCam.setMoveSpeed(100f);
+ cam.setLocation( new Vector3f( 0f, 150f, -325f ) );
+ cam.lookAt( new Vector3f( 0f, 100f, 0f ), Vector3f.UNIT_Y );
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, 1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+
+ Node model1 = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+ Node model2 = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+// Node model2 = model1.clone();
+
+ model1.setLocalTranslation(-60, 0, 0);
+ model2.setLocalTranslation(60, 0, 0);
+
+ AnimControl control1 = model1.getControl(AnimControl.class);
+ animNames = control1.getAnimationNames().toArray(new String[0]);
+ channel1 = control1.createChannel();
+
+ AnimControl control2 = model2.getControl(AnimControl.class);
+ channel2 = control2.createChannel();
+
+ SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton1", control1.getSkeleton());
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Green);
+ mat.getAdditionalRenderState().setDepthTest(false);
+ skeletonDebug.setMaterial(mat);
+ model1.attachChild(skeletonDebug);
+
+ skeletonDebug = new SkeletonDebugger("skeleton2", control2.getSkeleton());
+ skeletonDebug.setMaterial(mat);
+ model2.attachChild(skeletonDebug);
+
+ rootNode.attachChild(model1);
+ rootNode.attachChild(model2);
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/anim/TestAnimationFactory.java b/engine/src/test/jme3test/model/anim/TestAnimationFactory.java
new file mode 100644
index 0000000..5576851
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestAnimationFactory.java
@@ -0,0 +1,85 @@
+package jme3test.model.anim;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.AnimationFactory;
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestAnimationFactory extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestSpatialAnim app = new TestSpatialAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ AmbientLight al = new AmbientLight();
+ rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(Vector3f.UNIT_XYZ.negate());
+ rootNode.addLight(dl);
+
+ // Create model
+ Box box = new Box(1, 1, 1);
+ Geometry geom = new Geometry("box", box);
+ geom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
+ Node model = new Node("model");
+ model.attachChild(geom);
+
+ Box child = new Box(0.5f, 0.5f, 0.5f);
+ Geometry childGeom = new Geometry("box", child);
+ childGeom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
+ Node childModel = new Node("childmodel");
+ childModel.setLocalTranslation(2, 2, 2);
+ childModel.attachChild(childGeom);
+ model.attachChild(childModel);
+ TangentBinormalGenerator.generate(model);
+
+ //creating quite complex animation witht the AnimationHelper
+ // animation of 6 seconds named "anim" and with 25 frames per second
+ AnimationFactory animationFactory = new AnimationFactory(6, "anim", 25);
+
+ //creating a translation keyFrame at time = 3 with a translation on the x axis of 5 WU
+ animationFactory.addTimeTranslation(3, new Vector3f(5, 0, 0));
+ //reseting the translation to the start position at time = 6
+ animationFactory.addTimeTranslation(6, new Vector3f(0, 0, 0));
+
+ //Creating a scale keyFrame at time = 2 with the unit scale.
+ animationFactory.addTimeScale(2, new Vector3f(1, 1, 1));
+ //Creating a scale keyFrame at time = 4 scaling to 1.5
+ animationFactory.addTimeScale(4, new Vector3f(1.5f, 1.5f, 1.5f));
+ //reseting the scale to the start value at time = 5
+ animationFactory.addTimeScale(5, new Vector3f(1, 1, 1));
+
+
+ //Creating a rotation keyFrame at time = 0.5 of quarter PI around the Z axis
+ animationFactory.addTimeRotation(0.5f,new Quaternion().fromAngleAxis(FastMath.QUARTER_PI, Vector3f.UNIT_Z));
+ //rotating back to initial rotation value at time = 1
+ animationFactory.addTimeRotation(1,Quaternion.IDENTITY);
+ //Creating a rotation keyFrame at time = 2. Note that i used the Euler angle version because the angle is higher than PI
+ //this should result in a complete revolution of the spatial around the x axis in 1 second (from 1 to 2)
+ animationFactory.addTimeRotationAngles(2, FastMath.TWO_PI,0, 0);
+
+
+ AnimControl control = new AnimControl();
+ control.addAnim(animationFactory.buildAnimation());
+
+ model.addControl(control);
+
+ rootNode.attachChild(model);
+
+ //run animation
+ control.createChannel().setAnim("anim");
+ }
+}
diff --git a/engine/src/test/jme3test/model/anim/TestBlenderAnim.java b/engine/src/test/jme3test/model/anim/TestBlenderAnim.java
new file mode 100644
index 0000000..999c276
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestBlenderAnim.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.BlenderKey;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+public class TestBlenderAnim extends SimpleApplication {
+
+ private AnimChannel channel;
+ private AnimControl control;
+
+ public static void main(String[] args) {
+ TestBlenderAnim app = new TestBlenderAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(10f);
+ cam.setLocation(new Vector3f(6.4013605f, 7.488437f, 12.843031f));
+ cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f));
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+
+ BlenderKey blenderKey = new BlenderKey("Blender/2.4x/BaseMesh_249.blend");
+
+ Spatial scene = (Spatial) assetManager.loadModel(blenderKey);
+ rootNode.attachChild(scene);
+
+ Spatial model = this.findNode(rootNode, "BaseMesh_01");
+ model.center();
+
+ control = model.getControl(AnimControl.class);
+ channel = control.createChannel();
+
+ channel.setAnim("run_01");
+ }
+
+ /**
+ * This method finds a node of a given name.
+ * @param rootNode the root node to search
+ * @param name the name of the searched node
+ * @return the found node or null
+ */
+ private Spatial findNode(Node rootNode, String name) {
+ if (name.equals(rootNode.getName())) {
+ return rootNode;
+ }
+ return rootNode.getChild(name);
+ }
+}
diff --git a/engine/src/test/jme3test/model/anim/TestBlenderObjectAnim.java b/engine/src/test/jme3test/model/anim/TestBlenderObjectAnim.java
new file mode 100644
index 0000000..ccd8e9e
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestBlenderObjectAnim.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.BlenderKey;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+public class TestBlenderObjectAnim extends SimpleApplication {
+
+ private AnimChannel channel;
+ private AnimControl control;
+
+ public static void main(String[] args) {
+ TestBlenderObjectAnim app = new TestBlenderObjectAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(10f);
+ cam.setLocation(new Vector3f(6.4013605f, 7.488437f, 12.843031f));
+ cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f));
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+
+ BlenderKey blenderKey = new BlenderKey("Blender/2.4x/animtest.blend");
+
+ Spatial scene = (Spatial) assetManager.loadModel(blenderKey);
+ rootNode.attachChild(scene);
+
+ Spatial model = this.findNode(rootNode, "TestAnim");
+ model.center();
+
+ control = model.getControl(AnimControl.class);
+ channel = control.createChannel();
+
+ channel.setAnim("TestAnim");
+ }
+
+ /**
+ * This method finds a node of a given name.
+ * @param rootNode the root node to search
+ * @param name the name of the searched node
+ * @return the found node or null
+ */
+ private Spatial findNode(Node rootNode, String name) {
+ if (name.equals(rootNode.getName())) {
+ return rootNode;
+ }
+ return rootNode.getChild(name);
+ }
+}
diff --git a/engine/src/test/jme3test/model/anim/TestCustomAnim.java b/engine/src/test/jme3test/model/anim/TestCustomAnim.java
new file mode 100644
index 0000000..f1b8933
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestCustomAnim.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.shape.Box;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+public class TestCustomAnim extends SimpleApplication {
+
+ private Bone bone;
+ private Skeleton skeleton;
+ private Quaternion rotation = new Quaternion();
+
+ public static void main(String[] args) {
+ TestCustomAnim app = new TestCustomAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ AmbientLight al = new AmbientLight();
+ rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(Vector3f.UNIT_XYZ.negate());
+ rootNode.addLight(dl);
+
+ Box box = new Box(1, 1, 1);
+
+ // Setup bone weight buffer
+ FloatBuffer weights = FloatBuffer.allocate( box.getVertexCount() * 4 );
+ VertexBuffer weightsBuf = new VertexBuffer(Type.BoneWeight);
+ weightsBuf.setupData(Usage.CpuOnly, 4, Format.Float, weights);
+ box.setBuffer(weightsBuf);
+
+ // Setup bone index buffer
+ ByteBuffer indices = ByteBuffer.allocate( box.getVertexCount() * 4 );
+ VertexBuffer indicesBuf = new VertexBuffer(Type.BoneIndex);
+ indicesBuf.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indices);
+ box.setBuffer(indicesBuf);
+
+ // Create bind pose buffers
+ box.generateBindPose(true);
+
+ // Create skeleton
+ bone = new Bone("root");
+ bone.setBindTransforms(Vector3f.ZERO, Quaternion.IDENTITY, Vector3f.UNIT_XYZ);
+ bone.setUserControl(true);
+ skeleton = new Skeleton(new Bone[]{ bone });
+
+ // Assign all verticies to bone 0 with weight 1
+ for (int i = 0; i < box.getVertexCount() * 4; i += 4){
+ // assign vertex to bone index 0
+ indices.array()[i+0] = 0;
+ indices.array()[i+1] = 0;
+ indices.array()[i+2] = 0;
+ indices.array()[i+3] = 0;
+
+ // set weight to 1 only for first entry
+ weights.array()[i+0] = 1;
+ weights.array()[i+1] = 0;
+ weights.array()[i+2] = 0;
+ weights.array()[i+3] = 0;
+ }
+
+ // Maximum number of weights per bone is 1
+ box.setMaxNumWeights(1);
+
+ // Create model
+ Geometry geom = new Geometry("box", box);
+ geom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
+ Node model = new Node("model");
+ model.attachChild(geom);
+
+ // Create skeleton control
+ SkeletonControl skeletonControl = new SkeletonControl(skeleton);
+ model.addControl(skeletonControl);
+
+ rootNode.attachChild(model);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ // Rotate around X axis
+ Quaternion rotate = new Quaternion();
+ rotate.fromAngleAxis(tpf, Vector3f.UNIT_X);
+
+ // Combine rotation with previous
+ rotation.multLocal(rotate);
+
+ // Set new rotation into bone
+ bone.setUserTransforms(Vector3f.ZERO, rotation, Vector3f.UNIT_XYZ);
+
+ // After changing skeleton transforms, must update world data
+ skeleton.updateWorldVectors();
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/anim/TestOgreAnim.java b/engine/src/test/jme3test/model/anim/TestOgreAnim.java
new file mode 100644
index 0000000..c5f8213
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestOgreAnim.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.*;
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+
+public class TestOgreAnim extends SimpleApplication
+ implements AnimEventListener, ActionListener {
+
+ private AnimChannel channel;
+ private AnimControl control;
+
+ public static void main(String[] args) {
+ TestOgreAnim app = new TestOgreAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(10f);
+ cam.setLocation(new Vector3f(6.4013605f, 7.488437f, 12.843031f));
+ cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f));
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+
+ Spatial model = (Spatial) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ model.center();
+
+ control = model.getControl(AnimControl.class);
+ control.addListener(this);
+ channel = control.createChannel();
+
+ for (String anim : control.getAnimationNames())
+ System.out.println(anim);
+
+ channel.setAnim("stand");
+
+ SkeletonControl skeletonControl = model.getControl(SkeletonControl.class);
+
+ Box b = new Box(.25f,3f,.25f);
+ Geometry item = new Geometry("Item", b);
+ item.move(0, 1.5f, 0);
+ item.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+ Node n = skeletonControl.getAttachmentsNode("hand.right");
+ n.attachChild(item);
+
+ rootNode.attachChild(model);
+
+ inputManager.addListener(this, "Attack");
+ inputManager.addMapping("Attack", new KeyTrigger(KeyInput.KEY_SPACE));
+ }
+
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+ if (animName.equals("Dodge")){
+ channel.setAnim("stand", 0.50f);
+ channel.setLoopMode(LoopMode.DontLoop);
+ channel.setSpeed(1f);
+ }
+ }
+
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ if (binding.equals("Attack") && value){
+ if (!channel.getAnimationName().equals("Dodge")){
+ channel.setAnim("Dodge", 0.50f);
+ channel.setLoopMode(LoopMode.Cycle);
+ channel.setSpeed(0.10f);
+ }
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/anim/TestOgreComplexAnim.java b/engine/src/test/jme3test/model/anim/TestOgreComplexAnim.java
new file mode 100644
index 0000000..77385bf
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestOgreComplexAnim.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.anim;
+
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Bone;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.debug.SkeletonDebugger;
+
+public class TestOgreComplexAnim extends SimpleApplication {
+
+ private AnimControl control;
+ private float angle = 0;
+ private float scale = 1;
+ private float rate = 1;
+
+ public static void main(String[] args) {
+ TestOgreComplexAnim app = new TestOgreComplexAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(10f);
+ cam.setLocation(new Vector3f(6.4013605f, 7.488437f, 12.843031f));
+ cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f));
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
+ rootNode.addLight(dl);
+
+ Node model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+
+ control = model.getControl(AnimControl.class);
+
+ AnimChannel feet = control.createChannel();
+ AnimChannel leftHand = control.createChannel();
+ AnimChannel rightHand = control.createChannel();
+
+ // feet will dodge
+ feet.addFromRootBone("hip.right");
+ feet.addFromRootBone("hip.left");
+ feet.setAnim("Dodge");
+ feet.setSpeed(2);
+ feet.setLoopMode(LoopMode.Cycle);
+
+ // will blend over 15 seconds to stand
+ feet.setAnim("Walk", 15);
+ feet.setSpeed(0.25f);
+ feet.setLoopMode(LoopMode.Cycle);
+
+ // left hand will pull
+ leftHand.addFromRootBone("uparm.right");
+ leftHand.setAnim("pull");
+ leftHand.setSpeed(.5f);
+
+ // will blend over 15 seconds to stand
+ leftHand.setAnim("stand", 15);
+
+ // right hand will push
+ rightHand.addBone("spinehigh");
+ rightHand.addFromRootBone("uparm.left");
+ rightHand.setAnim("push");
+
+ SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton());
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Green);
+ mat.getAdditionalRenderState().setDepthTest(false);
+ skeletonDebug.setMaterial(mat);
+
+ model.attachChild(skeletonDebug);
+ rootNode.attachChild(model);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ Bone b = control.getSkeleton().getBone("spinehigh");
+ Bone b2 = control.getSkeleton().getBone("uparm.left");
+
+ angle += tpf * rate;
+ if (angle > FastMath.HALF_PI / 2f){
+ angle = FastMath.HALF_PI / 2f;
+ rate = -1;
+ }else if (angle < -FastMath.HALF_PI / 2f){
+ angle = -FastMath.HALF_PI / 2f;
+ rate = 1;
+ }
+
+ Quaternion q = new Quaternion();
+ q.fromAngles(0, angle, 0);
+
+ b.setUserControl(true);
+ b.setUserTransforms(Vector3f.ZERO, q, Vector3f.UNIT_XYZ);
+
+ b2.setUserControl(true);
+ b2.setUserTransforms(Vector3f.ZERO, Quaternion.IDENTITY, new Vector3f(1+angle,1+ angle, 1+angle));
+
+
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/anim/TestSpatialAnim.java b/engine/src/test/jme3test/model/anim/TestSpatialAnim.java
new file mode 100644
index 0000000..94dfd03
--- /dev/null
+++ b/engine/src/test/jme3test/model/anim/TestSpatialAnim.java
@@ -0,0 +1,87 @@
+package jme3test.model.anim;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.SpatialTrack;
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import java.util.HashMap;
+
+public class TestSpatialAnim extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestSpatialAnim app = new TestSpatialAnim();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ AmbientLight al = new AmbientLight();
+ rootNode.addLight(al);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(Vector3f.UNIT_XYZ.negate());
+ rootNode.addLight(dl);
+
+ // Create model
+ Box box = new Box(1, 1, 1);
+ Geometry geom = new Geometry("box", box);
+ geom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
+ Node model = new Node("model");
+ model.attachChild(geom);
+
+ Box child = new Box(0.5f, 0.5f, 0.5f);
+ Geometry childGeom = new Geometry("box", child);
+ childGeom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
+ Node childModel = new Node("childmodel");
+ childModel.setLocalTranslation(2, 2, 2);
+ childModel.attachChild(childGeom);
+ model.attachChild(childModel);
+
+ //animation parameters
+ float animTime = 5;
+ int fps = 25;
+ float totalXLength = 10;
+
+ //calculating frames
+ int totalFrames = (int) (fps * animTime);
+ float dT = animTime / totalFrames, t = 0;
+ float dX = totalXLength / totalFrames, x = 0;
+ float[] times = new float[totalFrames];
+ Vector3f[] translations = new Vector3f[totalFrames];
+ Quaternion[] rotations = new Quaternion[totalFrames];
+ Vector3f[] scales = new Vector3f[totalFrames];
+ for (int i = 0; i < totalFrames; ++i) {
+ times[i] = t;
+ t += dT;
+ translations[i] = new Vector3f(x, 0, 0);
+ x += dX;
+ rotations[i] = Quaternion.IDENTITY;
+ scales[i] = Vector3f.UNIT_XYZ;
+ }
+ SpatialTrack spatialTrack = new SpatialTrack(times, translations, rotations, scales);
+
+ //creating the animation
+ Animation spatialAnimation = new Animation("anim", animTime);
+ spatialAnimation.setTracks(new SpatialTrack[] { spatialTrack });
+
+ //create spatial animation control
+ AnimControl control = new AnimControl();
+ HashMap<String, Animation> animations = new HashMap<String, Animation>();
+ animations.put("anim", spatialAnimation);
+ control.setAnimations(animations);
+ model.addControl(control);
+
+ rootNode.attachChild(model);
+
+ //run animation
+ control.createChannel().setAnim("anim");
+ }
+}
diff --git a/engine/src/test/jme3test/model/shape/TestBillboard.java b/engine/src/test/jme3test/model/shape/TestBillboard.java
new file mode 100644
index 0000000..dc5be7f
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestBillboard.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.shape;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.BillboardControl;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Quad;
+
+/**
+ *
+ * @author Kirill Vainer
+ */
+public class TestBillboard extends SimpleApplication {
+
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(10);
+
+ Quad q = new Quad(2, 2);
+ Geometry g = new Geometry("Quad", q);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+ g.setMaterial(mat);
+
+ Quad q2 = new Quad(1, 1);
+ Geometry g3 = new Geometry("Quad2", q2);
+ Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat2.setColor("Color", ColorRGBA.Yellow);
+ g3.setMaterial(mat2);
+ g3.setLocalTranslation(.5f, .5f, .01f);
+
+ Box b = new Box(new Vector3f(0, 0, 3), .25f, .5f, .25f);
+ Geometry g2 = new Geometry("Box", b);
+ g2.setMaterial(mat);
+
+ Node bb = new Node("billboard");
+
+ BillboardControl control=new BillboardControl();
+
+ bb.addControl(control);
+ bb.attachChild(g);
+ bb.attachChild(g3);
+
+
+ n=new Node("parent");
+ n.attachChild(g2);
+ n.attachChild(bb);
+ rootNode.attachChild(n);
+
+ n2=new Node("parentParent");
+ n2.setLocalTranslation(Vector3f.UNIT_X.mult(5));
+ n2.attachChild(n);
+
+ rootNode.attachChild(n2);
+
+
+// rootNode.attachChild(bb);
+// rootNode.attachChild(g2);
+ }
+ Node n;
+ Node n2;
+ @Override
+ public void simpleUpdate(float tpf) {
+ super.simpleUpdate(tpf);
+ n.rotate(0, tpf, 0);
+ n.move(0.1f*tpf, 0, 0);
+ n2.rotate(0, 0, -tpf);
+ }
+
+
+
+ public static void main(String[] args) {
+ TestBillboard app = new TestBillboard();
+ app.start();
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/model/shape/TestBox.java b/engine/src/test/jme3test/model/shape/TestBox.java
new file mode 100644
index 0000000..606805e
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestBox.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.shape;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+public class TestBox extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestBox app = new TestBox();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/shape/TestCustomMesh.java b/engine/src/test/jme3test/model/shape/TestCustomMesh.java
new file mode 100644
index 0000000..1ea48fb
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestCustomMesh.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.shape;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+
+/**
+ * How to create custom meshes by specifying vertices
+ * We render the mesh in three different ways, once with a solid blue color,
+ * once with vertex colors, and once with a wireframe material.
+ * @author KayTrance
+ */
+public class TestCustomMesh extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestCustomMesh app = new TestCustomMesh();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ Mesh m = new Mesh();
+
+ // Vertex positions in space
+ Vector3f [] vertices = new Vector3f[4];
+ vertices[0] = new Vector3f(0,0,0);
+ vertices[1] = new Vector3f(3,0,0);
+ vertices[2] = new Vector3f(0,3,0);
+ vertices[3] = new Vector3f(3,3,0);
+
+ // Texture coordinates
+ Vector2f [] texCoord = new Vector2f[4];
+ texCoord[0] = new Vector2f(0,0);
+ texCoord[1] = new Vector2f(1,0);
+ texCoord[2] = new Vector2f(0,1);
+ texCoord[3] = new Vector2f(1,1);
+
+ // Indexes. We define the order in which mesh should be constructed
+ int [] indexes = {2,0,1,1,3,2};
+
+ // Setting buffers
+ m.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
+ m.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
+ m.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes));
+ m.updateBound();
+
+ // *************************************************************************
+ // First mesh uses one solid color
+ // *************************************************************************
+
+ // Creating a geometry, and apply a single color material to it
+ Geometry geom = new Geometry("OurMesh", m);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+ geom.setMaterial(mat);
+
+ // Attaching our geometry to the root node.
+ rootNode.attachChild(geom);
+
+ // *************************************************************************
+ // Second mesh uses vertex colors to color each vertex
+ // *************************************************************************
+ Mesh cMesh = m.clone();
+ Geometry coloredMesh = new Geometry ("ColoredMesh", cMesh);
+ Material matVC = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matVC.setBoolean("VertexColor", true);
+
+ //We have 4 vertices and 4 color values for each of them.
+ //If you have more vertices, you need 'new float[yourVertexCount * 4]' here!
+ float[] colorArray = new float[4*4];
+ int colorIndex = 0;
+
+ //Set custom RGBA value for each Vertex. Values range from 0.0f to 1.0f
+ for(int i = 0; i < 4; i++){
+ // Red value (is increased by .2 on each next vertex here)
+ colorArray[colorIndex++]= 0.1f+(.2f*i);
+ // Green value (is reduced by .2 on each next vertex)
+ colorArray[colorIndex++]= 0.9f-(0.2f*i);
+ // Blue value (remains the same in our case)
+ colorArray[colorIndex++]= 0.5f;
+ // Alpha value (no transparency set here)
+ colorArray[colorIndex++]= 1.0f;
+ }
+ // Set the color buffer
+ cMesh.setBuffer(Type.Color, 4, colorArray);
+ coloredMesh.setMaterial(matVC);
+ // move mesh a bit so that it doesn't intersect with the first one
+ coloredMesh.setLocalTranslation(4, 0, 0);
+ rootNode.attachChild(coloredMesh);
+
+// /** Alternatively, you can show the mesh vertixes as points
+// * instead of coloring the faces. */
+// cMesh.setMode(Mesh.Mode.Points);
+// cMesh.setPointSize(10f);
+// cMesh.updateBound();
+// cMesh.setStatic();
+// Geometry points = new Geometry("Points", m);
+// points.setMaterial(mat);
+// rootNode.attachChild(points);
+
+ // *************************************************************************
+ // Third mesh will use a wireframe shader to show wireframe
+ // *************************************************************************
+ Mesh wfMesh = m.clone();
+ Geometry wfGeom = new Geometry("wireframeGeometry", wfMesh);
+ Material matWireframe = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWireframe.setColor("Color", ColorRGBA.Green);
+ matWireframe.getAdditionalRenderState().setWireframe(true);
+ wfGeom.setMaterial(matWireframe);
+ wfGeom.setLocalTranslation(4, 4, 0);
+ rootNode.attachChild(wfGeom);
+
+ }
+}
diff --git a/engine/src/test/jme3test/model/shape/TestCylinder.java b/engine/src/test/jme3test/model/shape/TestCylinder.java
new file mode 100644
index 0000000..4765dd7
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestCylinder.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.shape;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.Material;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Cylinder;
+import com.jme3.texture.Texture;
+
+public class TestCylinder extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestCylinder app = new TestCylinder();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Cylinder t = new Cylinder(20, 50, 1, 2, true);
+ Geometry geom = new Geometry("Cylinder", t);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key = new TextureKey("Interface/Logo/Monkey.jpg", true);
+ key.setGenerateMips(true);
+ Texture tex = assetManager.loadTexture(key);
+ tex.setMinFilter(Texture.MinFilter.Trilinear);
+ mat.setTexture("ColorMap", tex);
+
+ geom.setMaterial(mat);
+
+ rootNode.attachChild(geom);
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/shape/TestDebugShapes.java b/engine/src/test/jme3test/model/shape/TestDebugShapes.java
new file mode 100644
index 0000000..7c34ecf
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestDebugShapes.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.shape;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.debug.Arrow;
+import com.jme3.scene.debug.Grid;
+import com.jme3.scene.debug.WireBox;
+import com.jme3.scene.debug.WireSphere;
+
+public class TestDebugShapes extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestDebugShapes app = new TestDebugShapes();
+ app.start();
+ }
+
+ public Geometry putShape(Mesh shape, ColorRGBA color){
+ Geometry g = new Geometry("shape", shape);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", color);
+ g.setMaterial(mat);
+ rootNode.attachChild(g);
+ return g;
+ }
+
+ public void putArrow(Vector3f pos, Vector3f dir, ColorRGBA color){
+ Arrow arrow = new Arrow(dir);
+ arrow.setLineWidth(4); // make arrow thicker
+ putShape(arrow, color).setLocalTranslation(pos);
+ }
+
+ public void putBox(Vector3f pos, float size, ColorRGBA color){
+ putShape(new WireBox(size, size, size), color).setLocalTranslation(pos);
+ }
+
+ public void putGrid(Vector3f pos, ColorRGBA color){
+ putShape(new Grid(6, 6, 0.2f), color).center().move(pos);
+ }
+
+ public void putSphere(Vector3f pos, ColorRGBA color){
+ putShape(new WireSphere(1), color).setLocalTranslation(pos);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(2,1.5f,2));
+ cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+
+ putArrow(Vector3f.ZERO, Vector3f.UNIT_X, ColorRGBA.Red);
+ putArrow(Vector3f.ZERO, Vector3f.UNIT_Y, ColorRGBA.Green);
+ putArrow(Vector3f.ZERO, Vector3f.UNIT_Z, ColorRGBA.Blue);
+
+ putBox(new Vector3f(2, 0, 0), 0.5f, ColorRGBA.Yellow);
+ putGrid(new Vector3f(3.5f, 0, 0), ColorRGBA.White);
+ putSphere(new Vector3f(4.5f, 0, 0), ColorRGBA.Magenta);
+ }
+
+}
diff --git a/engine/src/test/jme3test/model/shape/TestSphere.java b/engine/src/test/jme3test/model/shape/TestSphere.java
new file mode 100644
index 0000000..77e9139
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestSphere.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.model.shape;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+
+public class TestSphere extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestSphere app = new TestSphere();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ Sphere sphMesh = new Sphere(14, 14, 1);
+ Material solidColor = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+
+ for (int y = -5; y < 5; y++){
+ for (int x = -5; x < 5; x++){
+ Geometry sphere = new Geometry("sphere", sphMesh);
+ sphere.setMaterial(solidColor);
+ sphere.setLocalTranslation(x * 2, 0, y * 2);
+ rootNode.attachChild(sphere);
+ }
+ }
+ cam.setLocation(new Vector3f(0, 5, 0));
+ cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+ }
+
+}
diff --git a/engine/src/test/jme3test/network/MovingAverage.java b/engine/src/test/jme3test/network/MovingAverage.java
new file mode 100644
index 0000000..0fed2d5
--- /dev/null
+++ b/engine/src/test/jme3test/network/MovingAverage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.network;
+
+@Deprecated
+public class MovingAverage {
+
+ private long[] samples;
+ private long sum;
+ private int count, index;
+
+ public MovingAverage(int numSamples){
+ samples = new long[numSamples];
+ }
+
+ public void add(long sample){
+ sum = sum - samples[index] + sample;
+ samples[index++] = sample;
+ if (index > count){
+ count = index;
+ }
+ if (index >= samples.length){
+ index = 0;
+ }
+ }
+
+ public long getAverage(){
+ if (count == 0)
+ return 0;
+ else
+ return (long) ((float) sum / (float) count);
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/network/TestChatClient.java b/engine/src/test/jme3test/network/TestChatClient.java
new file mode 100644
index 0000000..2300c40
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestChatClient.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.network;
+
+import com.jme3.network.Client;
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+import com.jme3.network.Network;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import javax.swing.*;
+import jme3test.network.TestChatServer.ChatMessage;
+
+/**
+ * A simple test chat server. When SM implements a set
+ * of standard chat classes this can become a lot simpler.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class TestChatClient extends JFrame {
+
+ private Client client;
+ private JEditorPane chatLog;
+ private StringBuilder chatMessages = new StringBuilder();
+ private JTextField nameField;
+ private JTextField messageField;
+
+ public TestChatClient(String host) throws IOException {
+ super("jME3 Test Chat Client - to:" + host);
+
+ // Build out the UI
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setSize(800, 600);
+
+ chatLog = new JEditorPane();
+ chatLog.setEditable(false);
+ chatLog.setContentType("text/html");
+ chatLog.setText("<html><body>");
+
+ getContentPane().add(new JScrollPane(chatLog), "Center");
+
+ // A crude form
+ JPanel p = new JPanel();
+ p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
+ p.add(new JLabel("Name:"));
+ nameField = new JTextField(System.getProperty("user.name", "yourname"));
+ Dimension d = nameField.getPreferredSize();
+ nameField.setMaximumSize(new Dimension(120, d.height + 6));
+ p.add(nameField);
+ p.add(new JLabel(" Message:"));
+ messageField = new JTextField();
+ p.add(messageField);
+ p.add(new JButton(new SendAction(true)));
+ p.add(new JButton(new SendAction(false)));
+
+ getContentPane().add(p, "South");
+
+ client = Network.connectToServer(TestChatServer.NAME, TestChatServer.VERSION,
+ host, TestChatServer.PORT, TestChatServer.UDP_PORT);
+ client.addMessageListener(new ChatHandler(), ChatMessage.class);
+ client.start();
+ }
+
+ public static String getString(Component owner, String title, String message, String initialValue) {
+ return (String) JOptionPane.showInputDialog(owner, message, title, JOptionPane.PLAIN_MESSAGE,
+ null, null, initialValue);
+ }
+
+ public static void main(String... args) throws Exception {
+ TestChatServer.initializeClasses();
+
+ // Grab a host string from the user
+ String s = getString(null, "Host Info", "Enter chat host:", "localhost");
+ if (s == null) {
+ System.out.println("User cancelled.");
+ return;
+ }
+
+ TestChatClient test = new TestChatClient(s);
+ test.setVisible(true);
+ }
+
+ private class ChatHandler implements MessageListener<Client> {
+
+ public void messageReceived(Client source, Message m) {
+ ChatMessage chat = (ChatMessage) m;
+
+ System.out.println("Received:" + chat);
+
+ // One of the least efficient ways to add text to a
+ // JEditorPane
+ chatMessages.append("<font color='#00a000'>" + (m.isReliable() ? "TCP" : "UDP") + "</font>");
+ chatMessages.append(" -- <font color='#000080'><b>" + chat.getName() + "</b></font> : ");
+ chatMessages.append(chat.getMessage());
+ chatMessages.append("<br />");
+ String s = "<html><body>" + chatMessages + "</body></html>";
+ chatLog.setText(s);
+
+ // Set selection to the end so that the scroll panel will scroll
+ // down.
+ chatLog.select(s.length(), s.length());
+ }
+ }
+
+ private class SendAction extends AbstractAction {
+
+ private boolean reliable;
+
+ public SendAction(boolean reliable) {
+ super(reliable ? "TCP" : "UDP");
+ this.reliable = reliable;
+ }
+
+ public void actionPerformed(ActionEvent evt) {
+ String name = nameField.getText();
+ String message = messageField.getText();
+
+ ChatMessage chat = new ChatMessage(name, message);
+ chat.setReliable(reliable);
+ System.out.println("Sending:" + chat);
+ client.send(chat);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/network/TestChatServer.java b/engine/src/test/jme3test/network/TestChatServer.java
new file mode 100644
index 0000000..7d37c0c
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestChatServer.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.network;
+
+import com.jme3.network.*;
+import com.jme3.network.serializing.Serializable;
+import com.jme3.network.serializing.Serializer;
+
+/**
+ * A simple test chat server. When SM implements a set
+ * of standard chat classes this can become a lot simpler.
+ *
+ * @version $Revision: 8843 $
+ * @author Paul Speed
+ */
+public class TestChatServer {
+ // Normally these and the initialized method would
+ // be in shared constants or something.
+
+ public static final String NAME = "Test Chat Server";
+ public static final int VERSION = 1;
+ public static final int PORT = 5110;
+ public static final int UDP_PORT = 5110;
+
+ public static void initializeClasses() {
+ // Doing it here means that the client code only needs to
+ // call our initialize.
+ Serializer.registerClass(ChatMessage.class);
+ }
+
+ public static void main(String... args) throws Exception {
+ initializeClasses();
+
+ // Use this to test the client/server name version check
+ Server server = Network.createServer(NAME, VERSION, PORT, UDP_PORT);
+ server.start();
+
+ ChatHandler handler = new ChatHandler();
+ server.addMessageListener(handler, ChatMessage.class);
+
+ // Keep running basically forever
+ synchronized (NAME) {
+ NAME.wait();
+ }
+ }
+
+ private static class ChatHandler implements MessageListener<HostedConnection> {
+
+ public ChatHandler() {
+ }
+
+ public void messageReceived(HostedConnection source, Message m) {
+ if (m instanceof ChatMessage) {
+ // Keep track of the name just in case we
+ // want to know it for some other reason later and it's
+ // a good example of session data
+ source.setAttribute("name", ((ChatMessage) m).getName());
+
+ System.out.println("Broadcasting:" + m + " reliable:" + m.isReliable());
+
+ // Just rebroadcast... the reliable flag will stay the
+ // same so if it came in on UDP it will go out on that too
+ source.getServer().broadcast(m);
+ } else {
+ System.err.println("Received odd message:" + m);
+ }
+ }
+ }
+
+ @Serializable
+ public static class ChatMessage extends AbstractMessage {
+
+ private String name;
+ private String message;
+
+ public ChatMessage() {
+ }
+
+ public ChatMessage(String name, String message) {
+ setName(name);
+ setMessage(message);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setMessage(String s) {
+ this.message = s;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String toString() {
+ return name + ":" + message;
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/network/TestLatency.java b/engine/src/test/jme3test/network/TestLatency.java
new file mode 100644
index 0000000..91b04c4
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestLatency.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.network;
+
+import com.jme3.network.*;
+import com.jme3.network.serializing.Serializable;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+
+public class TestLatency {
+
+ private static long startTime;
+ private static Client client;
+ private static MovingAverage average = new MovingAverage(100);
+
+ static {
+ startTime = System.currentTimeMillis();
+ }
+
+ private static long getTime(){
+ return System.currentTimeMillis() - startTime;
+ }
+
+ @Serializable
+ public static class TimestampMessage extends AbstractMessage {
+
+ long timeSent = 0;
+ long timeReceived = 0;
+
+ public TimestampMessage(){
+ setReliable(false);
+ }
+
+ public TimestampMessage(long timeSent, long timeReceived){
+ setReliable(false);
+ this.timeSent = timeSent;
+ this.timeReceived = timeReceived;
+ }
+
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException{
+ Serializer.registerClass(TimestampMessage.class);
+
+ Server server = Network.createServer(5110);
+ server.start();
+
+ client = Network.connectToServer("localhost", 5110);
+ client.start();
+
+ client.addMessageListener(new MessageListener<Client>(){
+ public void messageReceived(Client source, Message m) {
+ TimestampMessage timeMsg = (TimestampMessage) m;
+
+ long curTime = getTime();
+ //System.out.println("Time sent: " + timeMsg.timeSent);
+ //System.out.println("Time received by server: " + timeMsg.timeReceived);
+ //System.out.println("Time recieved by client: " + curTime);
+
+ long latency = (curTime - timeMsg.timeSent);
+ System.out.println("Latency: " + (latency) + " ms");
+ //long timeOffset = ((timeMsg.timeSent + curTime) / 2) - timeMsg.timeReceived;
+ //System.out.println("Approximate timeoffset: "+ (timeOffset) + " ms");
+
+ average.add(latency);
+ System.out.println("Average latency: " + average.getAverage());
+
+ long latencyOffset = latency - average.getAverage();
+ System.out.println("Latency offset: " + latencyOffset);
+
+ client.send(new TimestampMessage(getTime(), 0));
+ }
+ }, TimestampMessage.class);
+
+ server.addMessageListener(new MessageListener<HostedConnection>(){
+ public void messageReceived(HostedConnection source, Message m) {
+ TimestampMessage timeMsg = (TimestampMessage) m;
+ TimestampMessage outMsg = new TimestampMessage(timeMsg.timeSent, getTime());
+ source.send(outMsg);
+ }
+ }, TimestampMessage.class);
+
+ Thread.sleep(1);
+
+ client.send(new TimestampMessage(getTime(), 0));
+
+ Object obj = new Object();
+ synchronized(obj){
+ obj.wait();
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/network/TestMessages.java b/engine/src/test/jme3test/network/TestMessages.java
new file mode 100644
index 0000000..b99e32b
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestMessages.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.network;
+
+import com.jme3.network.*;
+import com.jme3.network.serializing.Serializable;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+
+public class TestMessages {
+
+ @Serializable
+ public static class PingMessage extends AbstractMessage {
+ }
+
+ @Serializable
+ public static class PongMessage extends AbstractMessage {
+ }
+
+ private static class ServerPingResponder implements MessageListener<HostedConnection> {
+ public void messageReceived(HostedConnection source, com.jme3.network.Message message) {
+ if (message instanceof PingMessage){
+ System.out.println("Server: Received ping message!");
+ source.send(new PongMessage());
+ }
+ }
+ }
+
+ private static class ClientPingResponder implements MessageListener<Client> {
+ public void messageReceived(Client source, com.jme3.network.Message message) {
+ if (message instanceof PongMessage){
+ System.out.println("Client: Received pong message!");
+ }
+ }
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException{
+ Serializer.registerClass(PingMessage.class);
+ Serializer.registerClass(PongMessage.class);
+
+ Server server = Network.createServer(5110);
+ server.start();
+
+ Client client = Network.connectToServer("localhost", 5110);
+ client.start();
+
+ server.addMessageListener(new ServerPingResponder(), PingMessage.class);
+ client.addMessageListener(new ClientPingResponder(), PongMessage.class);
+
+ System.out.println("Client: Sending ping message..");
+ client.send(new PingMessage());
+
+ Object obj = new Object();
+ synchronized (obj){
+ obj.wait();
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/network/TestNetworkStress.java b/engine/src/test/jme3test/network/TestNetworkStress.java
new file mode 100644
index 0000000..ba8893c
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestNetworkStress.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.network;
+
+import com.jme3.network.*;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class TestNetworkStress implements ConnectionListener {
+
+ public void connectionAdded(Server server, HostedConnection conn) {
+ System.out.println("Client Connected: "+conn.getId());
+ //conn.close("goodbye");
+ }
+
+ public void connectionRemoved(Server server, HostedConnection conn) {
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException{
+ Logger.getLogger("").getHandlers()[0].setLevel(Level.OFF);
+
+ Server server = Network.createServer(5110);
+ server.start();
+ server.addConnectionListener(new TestNetworkStress());
+
+ for (int i = 0; i < 1000; i++){
+ Client client = Network.connectToServer("localhost", 5110);
+ client.start();
+
+ Thread.sleep(10);
+
+ client.close();
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/network/TestRemoteCall.java b/engine/src/test/jme3test/network/TestRemoteCall.java
new file mode 100644
index 0000000..cd1d38e
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestRemoteCall.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.network;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.export.Savable;
+import com.jme3.network.Client;
+import com.jme3.network.Network;
+import com.jme3.network.Server;
+import com.jme3.network.rmi.ObjectStore;
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.serializers.SavableSerializer;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+public class TestRemoteCall {
+
+ private static SimpleApplication serverApp;
+
+ /**
+ * Interface implemented by the server, exposing
+ * RMI calls that clients can use.
+ */
+ public static interface ServerAccess {
+ /**
+ * Attaches the model with the given name to the server's scene.
+ *
+ * @param model The model name
+ *
+ * @return True if the model was attached.
+ *
+ * @throws RuntimeException If some error occurs.
+ */
+ public boolean attachChild(String model);
+ }
+
+ public static class ServerAccessImpl implements ServerAccess {
+ public boolean attachChild(String model) {
+ if (model == null)
+ throw new RuntimeException("Cannot be null. .. etc");
+
+ final String finalModel = model;
+ serverApp.enqueue(new Callable<Void>() {
+ public Void call() throws Exception {
+ Spatial spatial = serverApp.getAssetManager().loadModel(finalModel);
+ serverApp.getRootNode().attachChild(spatial);
+ return null;
+ }
+ });
+ return true;
+ }
+ }
+
+ public static void createServer(){
+ serverApp = new SimpleApplication() {
+ @Override
+ public void simpleInitApp() {
+ }
+ };
+ serverApp.start();
+
+ try {
+ Server server = Network.createServer(5110);
+ server.start();
+
+ ObjectStore store = new ObjectStore(server);
+ store.exposeObject("access", new ServerAccessImpl());
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException{
+ Serializer.registerClass(Savable.class, new SavableSerializer());
+
+ createServer();
+
+ Client client = Network.connectToServer("localhost", 5110);
+ client.start();
+
+ ObjectStore store = new ObjectStore(client);
+ ServerAccess access = store.getExposedObject("access", ServerAccess.class, true);
+ boolean result = access.attachChild("Models/Oto/Oto.mesh.xml");
+ System.out.println(result);
+ }
+}
diff --git a/engine/src/test/jme3test/network/TestSerialization.java b/engine/src/test/jme3test/network/TestSerialization.java
new file mode 100644
index 0000000..07854ce
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestSerialization.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.network;
+
+import com.jme3.network.*;
+import com.jme3.network.serializing.Serializable;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+import java.util.*;
+
+public class TestSerialization implements MessageListener<HostedConnection> {
+
+ @Serializable
+ public static class SomeObject {
+
+ private int val;
+
+ public SomeObject(){
+ }
+
+ public SomeObject(int val){
+ this.val = val;
+ }
+
+ public int getVal(){
+ return val;
+ }
+
+ @Override
+ public String toString(){
+ return "SomeObject[val="+val+"]";
+ }
+ }
+
+ public enum Status {
+ High,
+ Middle,
+ Low;
+ }
+
+ @Serializable
+ public static class TestSerializationMessage extends AbstractMessage {
+
+ boolean z;
+ byte b;
+ char c;
+ short s;
+ int i;
+ float f;
+ long l;
+ double d;
+
+ int[] ia;
+ List<Object> ls;
+ Map<String, SomeObject> mp;
+
+ Status status1;
+ Status status2;
+
+ Date date;
+
+ public TestSerializationMessage(){
+ super(true);
+ }
+
+ public TestSerializationMessage(boolean initIt){
+ super(true);
+ if (initIt){
+ z = true;
+ b = -88;
+ c = 'Y';
+ s = 9999;
+ i = 123;
+ f = -75.4e8f;
+ l = 9438345072805034L;
+ d = -854834.914703e88;
+ ia = new int[]{ 456, 678, 999 };
+
+ ls = new ArrayList<Object>();
+ ls.add("hello");
+ ls.add(new SomeObject(-22));
+
+ mp = new HashMap<String, SomeObject>();
+ mp.put("abc", new SomeObject(555));
+
+ status1 = Status.High;
+ status2 = Status.Middle;
+
+ date = new Date(System.currentTimeMillis());
+ }
+ }
+ }
+
+ public void messageReceived(HostedConnection source, Message m) {
+ TestSerializationMessage cm = (TestSerializationMessage) m;
+ System.out.println(cm.z);
+ System.out.println(cm.b);
+ System.out.println(cm.c);
+ System.out.println(cm.s);
+ System.out.println(cm.i);
+ System.out.println(cm.f);
+ System.out.println(cm.l);
+ System.out.println(cm.d);
+ System.out.println(Arrays.toString(cm.ia));
+ System.out.println(cm.ls);
+ System.out.println(cm.mp);
+ System.out.println(cm.status1);
+ System.out.println(cm.status2);
+ System.out.println(cm.date);
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException{
+ Serializer.registerClass(SomeObject.class);
+ Serializer.registerClass(TestSerializationMessage.class);
+
+ Server server = Network.createServer( 5110 );
+ server.start();
+
+ Client client = Network.connectToServer( "localhost", 5110 );
+ client.start();
+
+ server.addMessageListener(new TestSerialization(), TestSerializationMessage.class);
+ client.send(new TestSerializationMessage(true));
+
+ Thread.sleep(10000);
+ }
+
+}
diff --git a/engine/src/test/jme3test/network/TestThroughput.java b/engine/src/test/jme3test/network/TestThroughput.java
new file mode 100644
index 0000000..c261ec0
--- /dev/null
+++ b/engine/src/test/jme3test/network/TestThroughput.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.network;
+
+import com.jme3.network.*;
+import com.jme3.network.serializing.Serializable;
+import com.jme3.network.serializing.Serializer;
+import java.io.IOException;
+
+public class TestThroughput implements MessageListener<MessageConnection> { //extends MessageAdapter {
+
+ private static long lastTime = -1;
+ private static long counter = 0;
+ private static long total = 0;
+ // Change this flag to test UDP instead of TCP
+ private static boolean testReliable = false;
+ private boolean isOnServer;
+
+ public TestThroughput(boolean isOnServer) {
+ this.isOnServer = isOnServer;
+ }
+
+ @Serializable
+ public static class TestMessage extends AbstractMessage {
+
+ public TestMessage() {
+ setReliable(testReliable);
+ }
+ }
+
+ @Override
+ public void messageReceived(MessageConnection source, Message msg) {
+
+ if (!isOnServer) {
+ // It's local to the client so we got it back
+ counter++;
+ total++;
+ long time = System.currentTimeMillis();
+//System.out.println( "total:" + total + " counter:" + counter + " lastTime:" + lastTime + " time:" + time );
+ if (lastTime < 0) {
+ lastTime = time;
+ } else if (time - lastTime > 1000) {
+ long delta = time - lastTime;
+ double scale = delta / 1000.0;
+ double pps = counter / scale;
+ System.out.println("messages per second:" + pps + " total messages:" + total);
+ counter = 0;
+ lastTime = time;
+ }
+ } else {
+ if (source == null) {
+ System.out.println("Received a message from a not fully connected source, msg:" + msg);
+ } else {
+//System.out.println( "sending:" + msg + " back to client:" + source );
+ // The 'reliable' flag is transient and the server doesn't
+ // (yet) reset this value for us.
+ ((com.jme3.network.Message) msg).setReliable(testReliable);
+ source.send(msg);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+
+ Serializer.registerClass(TestMessage.class);
+
+ // Use this to test the client/server name version check
+ //Server server = Network.createServer( "bad name", 42, 5110, 5110 );
+ Server server = Network.createServer(5110, 5110);
+ server.start();
+
+ Client client = Network.connectToServer("localhost", 5110);
+ client.start();
+
+ client.addMessageListener(new TestThroughput(false), TestMessage.class);
+ server.addMessageListener(new TestThroughput(true), TestMessage.class);
+
+ Thread.sleep(1);
+
+ TestMessage test = new TestMessage();
+// for( int i = 0; i < 10; i++ ) {
+ while (true) {
+//System.out.println( "sending." );
+ client.send(test);
+ }
+
+ //Thread.sleep(5000);
+ }
+}
diff --git a/engine/src/test/jme3test/niftygui/TestNiftyExamples.java b/engine/src/test/jme3test/niftygui/TestNiftyExamples.java
new file mode 100644
index 0000000..e6a114c
--- /dev/null
+++ b/engine/src/test/jme3test/niftygui/TestNiftyExamples.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.niftygui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import de.lessvoid.nifty.Nifty;
+
+public class TestNiftyExamples extends SimpleApplication {
+
+ private Nifty nifty;
+
+ public static void main(String[] args){
+ TestNiftyExamples app = new TestNiftyExamples();
+ app.setPauseOnLostFocus(false);
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
+ inputManager,
+ audioRenderer,
+ guiViewPort);
+ nifty = niftyDisplay.getNifty();
+
+ nifty.fromXml("all/intro.xml", "start");
+
+ // attach the nifty display to the gui view port as a processor
+ guiViewPort.addProcessor(niftyDisplay);
+
+ // disable the fly cam
+ flyCam.setEnabled(false);
+ }
+
+}
diff --git a/engine/src/test/jme3test/niftygui/TestNiftyGui.java b/engine/src/test/jme3test/niftygui/TestNiftyGui.java
new file mode 100644
index 0000000..5a1ea1c
--- /dev/null
+++ b/engine/src/test/jme3test/niftygui/TestNiftyGui.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.niftygui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.screen.Screen;
+import de.lessvoid.nifty.screen.ScreenController;
+
+public class TestNiftyGui extends SimpleApplication implements ScreenController {
+
+ private Nifty nifty;
+
+ public static void main(String[] args){
+ TestNiftyGui app = new TestNiftyGui();
+ app.setPauseOnLostFocus(false);
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+
+ NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
+ inputManager,
+ audioRenderer,
+ guiViewPort);
+ nifty = niftyDisplay.getNifty();
+ nifty.fromXml("Interface/Nifty/HelloJme.xml", "start", this);
+
+ // attach the nifty display to the gui view port as a processor
+ guiViewPort.addProcessor(niftyDisplay);
+
+ // disable the fly cam
+// flyCam.setEnabled(false);
+// flyCam.setDragToRotate(true);
+ inputManager.setCursorVisible(true);
+ }
+
+ public void bind(Nifty nifty, Screen screen) {
+ System.out.println("bind( " + screen.getScreenId() + ")");
+ }
+
+ public void onStartScreen() {
+ System.out.println("onStartScreen");
+ }
+
+ public void onEndScreen() {
+ System.out.println("onEndScreen");
+ }
+
+ public void quit(){
+ nifty.gotoScreen("end");
+ }
+
+}
diff --git a/engine/src/test/jme3test/niftygui/TestNiftyToMesh.java b/engine/src/test/jme3test/niftygui/TestNiftyToMesh.java
new file mode 100644
index 0000000..2aacbc9
--- /dev/null
+++ b/engine/src/test/jme3test/niftygui/TestNiftyToMesh.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.niftygui;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture.MagFilter;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture2D;
+import de.lessvoid.nifty.Nifty;
+
+public class TestNiftyToMesh extends SimpleApplication{
+
+ private Nifty nifty;
+
+ public static void main(String[] args){
+ TestNiftyToMesh app = new TestNiftyToMesh();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ ViewPort niftyView = renderManager.createPreView("NiftyView", new Camera(1024, 768));
+ niftyView.setClearFlags(true, true, true);
+ NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
+ inputManager,
+ audioRenderer,
+ niftyView);
+ nifty = niftyDisplay.getNifty();
+ nifty.fromXml("all/intro.xml", "start");
+ niftyView.addProcessor(niftyDisplay);
+
+ Texture2D depthTex = new Texture2D(1024, 768, Format.Depth);
+ FrameBuffer fb = new FrameBuffer(1024, 768, 1);
+ fb.setDepthTexture(depthTex);
+
+ Texture2D tex = new Texture2D(1024, 768, Format.RGBA8);
+ tex.setMinFilter(MinFilter.Trilinear);
+ tex.setMagFilter(MagFilter.Bilinear);
+
+ fb.setColorTexture(tex);
+ niftyView.setClearFlags(true, true, true);
+ niftyView.setOutputFrameBuffer(fb);
+
+ Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", tex);
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+ }
+}
diff --git a/engine/src/test/jme3test/post/BloomUI.java b/engine/src/test/jme3test/post/BloomUI.java
new file mode 100644
index 0000000..5029732
--- /dev/null
+++ b/engine/src/test/jme3test/post/BloomUI.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.post;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.post.filters.BloomFilter;
+
+/**
+ *
+ * @author nehon
+ */
+public class BloomUI {
+
+ public BloomUI(InputManager inputManager, final BloomFilter filter) {
+
+ System.out.println("----------------- Bloom UI Debugger --------------------");
+ System.out.println("-- blur Scale : press Y to increase, H to decrease");
+ System.out.println("-- exposure Power : press U to increase, J to decrease");
+ System.out.println("-- exposure CutOff : press I to increase, K to decrease");
+ System.out.println("-- bloom Intensity : press O to increase, P to decrease");
+ System.out.println("-------------------------------------------------------");
+
+ inputManager.addMapping("blurScaleUp", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("blurScaleDown", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("exposurePowerUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("exposurePowerDown", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("exposureCutOffUp", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("exposureCutOffDown", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("bloomIntensityUp", new KeyTrigger(KeyInput.KEY_O));
+ inputManager.addMapping("bloomIntensityDown", new KeyTrigger(KeyInput.KEY_L));
+
+
+ AnalogListener anl = new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals("blurScaleUp")) {
+ filter.setBlurScale(filter.getBlurScale() + 0.01f);
+ System.out.println("blurScale : " + filter.getBlurScale());
+ }
+ if (name.equals("blurScaleDown")) {
+ filter.setBlurScale(filter.getBlurScale() - 0.01f);
+ System.out.println("blurScale : " + filter.getBlurScale());
+ }
+ if (name.equals("exposurePowerUp")) {
+ filter.setExposurePower(filter.getExposurePower() + 0.01f);
+ System.out.println("exposurePower : " + filter.getExposurePower());
+ }
+ if (name.equals("exposurePowerDown")) {
+ filter.setExposurePower(filter.getExposurePower() - 0.01f);
+ System.out.println("exposurePower : " + filter.getExposurePower());
+ }
+ if (name.equals("exposureCutOffUp")) {
+ filter.setExposureCutOff(Math.min(1.0f, Math.max(0.0f, filter.getExposureCutOff() + 0.001f)));
+ System.out.println("exposure CutOff : " + filter.getExposureCutOff());
+ }
+ if (name.equals("exposureCutOffDown")) {
+ filter.setExposureCutOff(Math.min(1.0f, Math.max(0.0f, filter.getExposureCutOff() - 0.001f)));
+ System.out.println("exposure CutOff : " + filter.getExposureCutOff());
+ }
+ if (name.equals("bloomIntensityUp")) {
+ filter.setBloomIntensity(filter.getBloomIntensity() + 0.01f);
+ System.out.println("bloom Intensity : " + filter.getBloomIntensity());
+ }
+ if (name.equals("bloomIntensityDown")) {
+ filter.setBloomIntensity(filter.getBloomIntensity() - 0.01f);
+ System.out.println("bloom Intensity : " + filter.getBloomIntensity());
+ }
+
+
+ }
+ };
+
+ inputManager.addListener(anl, "blurScaleUp", "blurScaleDown", "exposurePowerUp", "exposurePowerDown",
+ "exposureCutOffUp", "exposureCutOffDown", "bloomIntensityUp", "bloomIntensityDown");
+
+ }
+}
diff --git a/engine/src/test/jme3test/post/LightScatteringUI.java b/engine/src/test/jme3test/post/LightScatteringUI.java
new file mode 100644
index 0000000..22cc8c7
--- /dev/null
+++ b/engine/src/test/jme3test/post/LightScatteringUI.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.post.filters.LightScatteringFilter;
+
+/**
+ *
+ * @author nehon
+ */
+public class LightScatteringUI {
+ private LightScatteringFilter filter;
+ public LightScatteringUI(InputManager inputManager, LightScatteringFilter proc) {
+ filter=proc;
+
+
+ System.out.println("----------------- LightScattering UI Debugger --------------------");
+ System.out.println("-- Sample number : press Y to increase, H to decrease");
+ System.out.println("-- blur start : press U to increase, J to decrease");
+ System.out.println("-- blur width : press I to increase, K to decrease");
+ System.out.println("-- Light density : press O to increase, P to decrease");
+// System.out.println("-- Toggle AO on/off : press space bar");
+// System.out.println("-- Use only AO : press Num pad 0");
+// System.out.println("-- Output config declaration : press P");
+ System.out.println("-------------------------------------------------------");
+
+ inputManager.addMapping("sampleUp", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("sampleDown", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("blurStartUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("blurStartDown", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("blurWidthUp", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("blurWidthDown", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("lightDensityUp", new KeyTrigger(KeyInput.KEY_O));
+ inputManager.addMapping("lightDensityDown", new KeyTrigger(KeyInput.KEY_L));
+ inputManager.addMapping("outputConfig", new KeyTrigger(KeyInput.KEY_P));
+// inputManager.addMapping("toggleUseAO", new KeyTrigger(KeyInput.KEY_SPACE));
+// inputManager.addMapping("toggleUseOnlyAo", new KeyTrigger(KeyInput.KEY_NUMPAD0));
+
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+
+ if (name.equals("sampleUp")) {
+ filter.setNbSamples(filter.getNbSamples()+1);
+ System.out.println("Nb Samples : "+filter.getNbSamples());
+ }
+ if (name.equals("sampleDown")) {
+ filter.setNbSamples(filter.getNbSamples()-1);
+ System.out.println("Nb Samples : "+filter.getNbSamples());
+ }
+ if (name.equals("outputConfig") && keyPressed) {
+ System.out.println("lightScatteringFilter.setNbSamples("+filter.getNbSamples()+");");
+ System.out.println("lightScatteringFilter.setBlurStart("+filter.getBlurStart()+"f);");
+ System.out.println("lightScatteringFilter.setBlurWidth("+filter.getBlurWidth()+"f);");
+ System.out.println("lightScatteringFilter.setLightDensity("+filter.getLightDensity()+"f);");
+ }
+
+
+ }
+ };
+
+ AnalogListener anl = new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+
+ if (name.equals("blurStartUp")) {
+ filter.setBlurStart(filter.getBlurStart()+0.001f);
+ System.out.println("Blur start : "+filter.getBlurStart());
+ }
+ if (name.equals("blurStartDown")) {
+ filter.setBlurStart(filter.getBlurStart()-0.001f);
+ System.out.println("Blur start : "+filter.getBlurStart());
+ }
+ if (name.equals("blurWidthUp")) {
+ filter.setBlurWidth(filter.getBlurWidth()+0.001f);
+ System.out.println("Blur Width : "+filter.getBlurWidth());
+ }
+ if (name.equals("blurWidthDown")) {
+ filter.setBlurWidth(filter.getBlurWidth()-0.001f);
+ System.out.println("Blur Width : "+filter.getBlurWidth());
+ }
+ if (name.equals("lightDensityUp")) {
+ filter.setLightDensity(filter.getLightDensity()+0.001f);
+ System.out.println("light Density : "+filter.getLightDensity());
+ }
+ if (name.equals("lightDensityDown")) {
+ filter.setLightDensity(filter.getLightDensity()-0.001f);
+ System.out.println("light Density : "+filter.getLightDensity());
+ }
+
+ }
+ };
+ inputManager.addListener(acl,"sampleUp","sampleDown","outputConfig");
+
+ inputManager.addListener(anl, "blurStartUp","blurStartDown","blurWidthUp", "blurWidthDown","lightDensityUp", "lightDensityDown");
+
+ }
+
+
+
+}
diff --git a/engine/src/test/jme3test/post/SSAOUI.java b/engine/src/test/jme3test/post/SSAOUI.java
new file mode 100644
index 0000000..21509f5
--- /dev/null
+++ b/engine/src/test/jme3test/post/SSAOUI.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.post;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.post.ssao.SSAOFilter;
+
+/**
+ *
+ * @author nehon
+ */
+public class SSAOUI {
+
+ SSAOFilter filter;
+
+ public SSAOUI(InputManager inputManager, SSAOFilter filter) {
+ this.filter = filter;
+ init(inputManager);
+ }
+
+ private void init(InputManager inputManager) {
+ System.out.println("----------------- Water UI Debugger --------------------");
+ System.out.println("-- Sample Radius : press Y to increase, H to decrease");
+ System.out.println("-- AO Intensity : press U to increase, J to decrease");
+ System.out.println("-- AO scale : press I to increase, K to decrease");
+ System.out.println("-- AO bias : press O to increase, P to decrease");
+ System.out.println("-- Toggle AO on/off : press space bar");
+ System.out.println("-- Use only AO : press Num pad 0");
+ System.out.println("-- Output config declaration : press P");
+ System.out.println("-------------------------------------------------------");
+
+ inputManager.addMapping("sampleRadiusUp", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("sampleRadiusDown", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("intensityUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("intensityDown", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("scaleUp", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addMapping("scaleDown", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("biasUp", new KeyTrigger(KeyInput.KEY_O));
+ inputManager.addMapping("biasDown", new KeyTrigger(KeyInput.KEY_L));
+ inputManager.addMapping("outputConfig", new KeyTrigger(KeyInput.KEY_P));
+ inputManager.addMapping("toggleUseAO", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("toggleUseOnlyAo", new KeyTrigger(KeyInput.KEY_NUMPAD0));
+
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+
+ if (name.equals("toggleUseAO") && keyPressed) {
+ filter.setEnabled(!filter.isEnabled());
+ // filter.setUseAo(!filter.isUseAo());
+ System.out.println("use AO : " + filter.isEnabled());
+ }
+ if (name.equals("toggleUseOnlyAo") && keyPressed) {
+ filter.setUseOnlyAo(!filter.isUseOnlyAo());
+ System.out.println("use Only AO : " + filter.isUseOnlyAo());
+
+ }
+ if (name.equals("outputConfig") && keyPressed) {
+ System.out.println("new SSAOFilter(" + filter.getSampleRadius() + "f," + filter.getIntensity() + "f," + filter.getScale() + "f," + filter.getBias() + "f);");
+ }
+ }
+ };
+
+ AnalogListener anl = new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals("sampleRadiusUp")) {
+ filter.setSampleRadius(filter.getSampleRadius() + 0.01f);
+ System.out.println("Sample Radius : " + filter.getSampleRadius());
+ }
+ if (name.equals("sampleRadiusDown")) {
+ filter.setSampleRadius(filter.getSampleRadius() - 0.01f);
+ System.out.println("Sample Radius : " + filter.getSampleRadius());
+ }
+ if (name.equals("intensityUp")) {
+ filter.setIntensity(filter.getIntensity() + 0.01f);
+ System.out.println("Intensity : " + filter.getIntensity());
+ }
+ if (name.equals("intensityDown")) {
+ filter.setIntensity(filter.getIntensity() - 0.01f);
+ System.out.println("Intensity : " + filter.getIntensity());
+ }
+ if (name.equals("scaleUp")) {
+ filter.setScale(filter.getScale() + 0.01f);
+ System.out.println("scale : " + filter.getScale());
+ }
+ if (name.equals("scaleDown")) {
+ filter.setScale(filter.getScale() - 0.01f);
+ System.out.println("scale : " + filter.getScale());
+ }
+ if (name.equals("biasUp")) {
+ filter.setBias(filter.getBias() + 0.001f);
+ System.out.println("bias : " + filter.getBias());
+ }
+ if (name.equals("biasDown")) {
+ filter.setBias(filter.getBias() - 0.001f);
+ System.out.println("bias : " + filter.getBias());
+ }
+
+ }
+ };
+ inputManager.addListener(acl, "toggleUseAO", "toggleUseOnlyAo", "outputConfig");
+ inputManager.addListener(anl, "sampleRadiusUp", "sampleRadiusDown", "intensityUp", "intensityDown", "scaleUp", "scaleDown",
+ "biasUp", "biasDown");
+
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestBloom.java b/engine/src/test/jme3test/post/TestBloom.java
new file mode 100644
index 0000000..1c371b4
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestBloom.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.scene.shape.Box;
+import com.jme3.util.SkyFactory;
+
+public class TestBloom extends SimpleApplication {
+
+ float angle;
+ Spatial lightMdl;
+ Spatial teapot;
+ Geometry frustumMdl;
+ WireFrustum frustum;
+ boolean active=true;
+ FilterPostProcessor fpp;
+
+ public static void main(String[] args){
+ TestBloom app = new TestBloom();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // put the camera in a bad position
+ cam.setLocation(new Vector3f(-2.336393f, 11.91392f, -7.139601f));
+ cam.setRotation(new Quaternion(0.23602544f, 0.11321983f, -0.027698677f, 0.96473104f));
+ //cam.setFrustumFar(1000);
+
+
+ Material mat = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 15f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Yellow.mult(0.2f));
+ mat.setColor("Diffuse", ColorRGBA.Yellow.mult(0.2f));
+ mat.setColor("Specular", ColorRGBA.Yellow.mult(0.8f));
+
+
+
+
+ Material matSoil = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
+ matSoil.setFloat("Shininess", 15f);
+ matSoil.setBoolean("UseMaterialColors", true);
+ matSoil.setColor("Ambient", ColorRGBA.Gray);
+ matSoil.setColor("Diffuse", ColorRGBA.Black);
+ matSoil.setColor("Specular", ColorRGBA.Gray);
+
+
+
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalTranslation(0,0,10);
+
+ teapot.setMaterial(mat);
+ teapot.setShadowMode(ShadowMode.CastAndReceive);
+ teapot.setLocalScale(10.0f);
+ rootNode.attachChild(teapot);
+
+
+
+ Geometry soil=new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700));
+ soil.setMaterial(matSoil);
+ soil.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(soil);
+
+ DirectionalLight light=new DirectionalLight();
+ light.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ light.setColor(ColorRGBA.White.mult(1.5f));
+ rootNode.addLight(light);
+
+ // load sky
+ Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false);
+ sky.setCullHint(Spatial.CullHint.Never);
+ rootNode.attachChild(sky);
+
+ fpp=new FilterPostProcessor(assetManager);
+ // fpp.setNumSamples(4);
+ BloomFilter bloom=new BloomFilter();
+ bloom.setDownSamplingFactor(2);
+ bloom.setBlurScale(1.37f);
+ bloom.setExposurePower(3.30f);
+ bloom.setExposureCutOff(0.2f);
+ bloom.setBloomIntensity(2.45f);
+ BloomUI ui=new BloomUI(inputManager, bloom);
+
+
+ viewPort.addProcessor(fpp);
+ fpp.addFilter(bloom);
+ initInputs();
+
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("toggle") && keyPressed) {
+ if(active){
+ active=false;
+ viewPort.removeProcessor(fpp);
+ }else{
+ active=true;
+ viewPort.addProcessor(fpp);
+ }
+ }
+ }
+ };
+
+ inputManager.addListener(acl, "toggle");
+
+ }
+
+
+
+}
diff --git a/engine/src/test/jme3test/post/TestCartoonEdge.java b/engine/src/test/jme3test/post/TestCartoonEdge.java
new file mode 100644
index 0000000..a57656d
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestCartoonEdge.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.CartoonEdgeFilter;
+import com.jme3.renderer.Caps;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.texture.Texture;
+
+public class TestCartoonEdge extends SimpleApplication {
+
+ private FilterPostProcessor fpp;
+
+ public static void main(String[] args){
+ TestCartoonEdge app = new TestCartoonEdge();
+ app.start();
+ }
+
+ public void setupFilters(){
+ if (renderer.getCaps().contains(Caps.GLSL100)){
+ fpp=new FilterPostProcessor(assetManager);
+ //fpp.setNumSamples(4);
+ CartoonEdgeFilter toon=new CartoonEdgeFilter();
+ toon.setEdgeColor(ColorRGBA.Yellow);
+ fpp.addFilter(toon);
+ viewPort.addProcessor(fpp);
+ }
+ }
+
+ public void makeToonish(Spatial spatial){
+ if (spatial instanceof Node){
+ Node n = (Node) spatial;
+ for (Spatial child : n.getChildren())
+ makeToonish(child);
+ }else if (spatial instanceof Geometry){
+ Geometry g = (Geometry) spatial;
+ Material m = g.getMaterial();
+ if (m.getMaterialDef().getName().equals("Phong Lighting")){
+ Texture t = assetManager.loadTexture("Textures/ColorRamp/toon.png");
+// t.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+// t.setMagFilter(Texture.MagFilter.Nearest);
+ m.setTexture("ColorRamp", t);
+ m.setBoolean("UseMaterialColors", true);
+ m.setColor("Specular", ColorRGBA.Black);
+ m.setColor("Diffuse", ColorRGBA.White);
+ m.setBoolean("VertexLighting", true);
+ }
+ }
+ }
+
+ public void setupLighting(){
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, 1).normalizeLocal());
+ dl.setColor(new ColorRGBA(2,2,2,1));
+
+ rootNode.addLight(dl);
+ }
+
+ public void setupModel(){
+ Spatial model = assetManager.loadModel("Models/MonkeyHead/MonkeyHead.mesh.xml");
+ makeToonish(model);
+ model.rotate(0, FastMath.PI, 0);
+// signpost.setLocalTranslation(12, 3.5f, 30);
+// model.scale(0.10f);
+// signpost.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(model);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ viewPort.setBackgroundColor(ColorRGBA.Gray);
+
+ cam.setLocation(new Vector3f(-5.6310086f, 5.0892987f, -13.000479f));
+ cam.setRotation(new Quaternion(0.1779095f, 0.20036356f, -0.03702727f, 0.96272093f));
+ cam.update();
+
+ cam.setFrustumFar(300);
+ flyCam.setMoveSpeed(30);
+
+ rootNode.setCullHint(CullHint.Never);
+
+ setupLighting();
+ setupModel();
+ setupFilters();
+ }
+
+}
diff --git a/engine/src/test/jme3test/post/TestCrossHatch.java b/engine/src/test/jme3test/post/TestCrossHatch.java
new file mode 100644
index 0000000..d5230fa
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestCrossHatch.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.CrossHatchFilter;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.scene.shape.Box;
+import com.jme3.util.SkyFactory;
+
+public class TestCrossHatch extends SimpleApplication {
+
+ float angle;
+ Spatial lightMdl;
+ Spatial teapot;
+ Geometry frustumMdl;
+ WireFrustum frustum;
+ boolean active=true;
+ FilterPostProcessor fpp;
+
+ public static void main(String[] args){
+ TestCrossHatch app = new TestCrossHatch();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // put the camera in a bad position
+ cam.setLocation(new Vector3f(-2.336393f, 11.91392f, -7.139601f));
+ cam.setRotation(new Quaternion(0.23602544f, 0.11321983f, -0.027698677f, 0.96473104f));
+ //cam.setFrustumFar(1000);
+
+
+ Material mat = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 15f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Yellow.mult(0.2f));
+ mat.setColor("Diffuse", ColorRGBA.Yellow.mult(0.2f));
+ mat.setColor("Specular", ColorRGBA.Yellow.mult(0.8f));
+
+
+
+
+ Material matSoil = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
+ matSoil.setFloat("Shininess", 15f);
+ matSoil.setBoolean("UseMaterialColors", true);
+ matSoil.setColor("Ambient", ColorRGBA.Gray);
+ matSoil.setColor("Diffuse", ColorRGBA.Black);
+ matSoil.setColor("Specular", ColorRGBA.Gray);
+
+
+
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalTranslation(0,0,10);
+
+ teapot.setMaterial(mat);
+ teapot.setShadowMode(ShadowMode.CastAndReceive);
+ teapot.setLocalScale(10.0f);
+ rootNode.attachChild(teapot);
+
+
+
+ Geometry soil=new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700));
+ soil.setMaterial(matSoil);
+ soil.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(soil);
+
+ DirectionalLight light=new DirectionalLight();
+ light.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ light.setColor(ColorRGBA.White.mult(1.5f));
+ rootNode.addLight(light);
+
+ // load sky
+ Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false);
+ sky.setCullHint(Spatial.CullHint.Never);
+ rootNode.attachChild(sky);
+
+ fpp=new FilterPostProcessor(assetManager);
+ CrossHatchFilter chf=new CrossHatchFilter();
+
+
+
+ viewPort.addProcessor(fpp);
+ fpp.addFilter(chf);
+ initInputs();
+
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("toggle") && keyPressed) {
+ if(active){
+ active=false;
+ viewPort.removeProcessor(fpp);
+ }else{
+ active=true;
+ viewPort.addProcessor(fpp);
+ }
+ }
+ }
+ };
+
+ inputManager.addListener(acl, "toggle");
+
+ }
+
+
+
+}
diff --git a/engine/src/test/jme3test/post/TestDepthOfField.java b/engine/src/test/jme3test/post/TestDepthOfField.java
new file mode 100644
index 0000000..5426a33
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestDepthOfField.java
@@ -0,0 +1,200 @@
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.DepthOfFieldFilter;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * test
+ * @author Nehon
+ */
+public class TestDepthOfField extends SimpleApplication {
+
+ private FilterPostProcessor fpp;
+ private Vector3f lightDir = new Vector3f(-4.9236743f, -1.27054665f, 5.896916f);
+ TerrainQuad terrain;
+ Material matRock;
+ DepthOfFieldFilter dofFilter;
+
+ public static void main(String[] args) {
+ TestDepthOfField app = new TestDepthOfField();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+
+ Node mainScene = new Node("Main Scene");
+ rootNode.attachChild(mainScene);
+
+ createTerrain(mainScene);
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(lightDir);
+ sun.setColor(ColorRGBA.White.clone().multLocal(1.7f));
+ mainScene.addLight(sun);
+
+ DirectionalLight l = new DirectionalLight();
+ l.setDirection(Vector3f.UNIT_Y.mult(-1));
+ l.setColor(ColorRGBA.White.clone().multLocal(0.3f));
+ mainScene.addLight(l);
+
+ flyCam.setMoveSpeed(50);
+ cam.setFrustumFar(3000);
+ cam.setLocation(new Vector3f(-700, 100, 300));
+ cam.setRotation(new Quaternion().fromAngles(new float[]{FastMath.PI * 0.06f, FastMath.PI * 0.65f, 0}));
+
+
+ Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false);
+ sky.setLocalScale(350);
+ mainScene.attachChild(sky);
+
+
+
+ fpp = new FilterPostProcessor(assetManager);
+ // fpp.setNumSamples(4);
+
+ dofFilter = new DepthOfFieldFilter();
+ dofFilter.setFocusDistance(0);
+ dofFilter.setFocusRange(50);
+ dofFilter.setBlurScale(1.4f);
+ fpp.addFilter(dofFilter);
+ viewPort.addProcessor(fpp);
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (isPressed) {
+ if (name.equals("toggle")) {
+ dofFilter.setEnabled(!dofFilter.isEnabled());
+ }
+
+
+ }
+ }
+ }, "toggle");
+ inputManager.addListener(new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals("blurScaleUp")) {
+ dofFilter.setBlurScale(dofFilter.getBlurScale() + 0.01f);
+ System.out.println("blurScale : " + dofFilter.getBlurScale());
+ }
+ if (name.equals("blurScaleDown")) {
+ dofFilter.setBlurScale(dofFilter.getBlurScale() - 0.01f);
+ System.out.println("blurScale : " + dofFilter.getBlurScale());
+ }
+ if (name.equals("focusRangeUp")) {
+ dofFilter.setFocusRange(dofFilter.getFocusRange() + 1f);
+ System.out.println("focusRange : " + dofFilter.getFocusRange());
+ }
+ if (name.equals("focusRangeDown")) {
+ dofFilter.setFocusRange(dofFilter.getFocusRange() - 1f);
+ System.out.println("focusRange : " + dofFilter.getFocusRange());
+ }
+ if (name.equals("focusDistanceUp")) {
+ dofFilter.setFocusDistance(dofFilter.getFocusDistance() + 1f);
+ System.out.println("focusDistance : " + dofFilter.getFocusDistance());
+ }
+ if (name.equals("focusDistanceDown")) {
+ dofFilter.setFocusDistance(dofFilter.getFocusDistance() - 1f);
+ System.out.println("focusDistance : " + dofFilter.getFocusDistance());
+ }
+
+ }
+ }, "blurScaleUp", "blurScaleDown", "focusRangeUp", "focusRangeDown", "focusDistanceUp", "focusDistanceDown");
+
+
+ inputManager.addMapping("toggle", new KeyTrigger(keyInput.KEY_SPACE));
+ inputManager.addMapping("blurScaleUp", new KeyTrigger(keyInput.KEY_U));
+ inputManager.addMapping("blurScaleDown", new KeyTrigger(keyInput.KEY_J));
+ inputManager.addMapping("focusRangeUp", new KeyTrigger(keyInput.KEY_I));
+ inputManager.addMapping("focusRangeDown", new KeyTrigger(keyInput.KEY_K));
+ inputManager.addMapping("focusDistanceUp", new KeyTrigger(keyInput.KEY_O));
+ inputManager.addMapping("focusDistanceDown", new KeyTrigger(keyInput.KEY_L));
+
+ }
+
+ private void createTerrain(Node rootNode) {
+ matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matRock.setBoolean("useTriPlanarMapping", false);
+ matRock.setBoolean("WardIso", true);
+ matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap", grass);
+ matRock.setFloat("DiffuseMap_0_scale", 64);
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_1", dirt);
+ matRock.setFloat("DiffuseMap_1_scale", 16);
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_2", rock);
+ matRock.setFloat("DiffuseMap_2_scale", 128);
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matRock.setTexture("NormalMap", normalMap0);
+ matRock.setTexture("NormalMap_1", normalMap2);
+ matRock.setTexture("NormalMap_2", normalMap2);
+
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
+ heightmap.load();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ List<Camera> cameras = new ArrayList<Camera>();
+ cameras.add(getCamera());
+ terrain.setMaterial(matRock);
+ terrain.setLocalScale(new Vector3f(5, 5, 5));
+ terrain.setLocalTranslation(new Vector3f(0, -30, 0));
+ terrain.setLocked(false); // unlock it so we can edit the height
+
+ terrain.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(terrain);
+
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ Vector3f origin = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.0f);
+ Vector3f direction = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.3f);
+ direction.subtractLocal(origin).normalizeLocal();
+ Ray ray = new Ray(origin, direction);
+ CollisionResults results = new CollisionResults();
+ int numCollisions = terrain.collideWith(ray, results);
+ if (numCollisions > 0) {
+ CollisionResult hit = results.getClosestCollision();
+ fpsText.setText(""+hit.getDistance());
+ dofFilter.setFocusDistance(hit.getDistance()/10.0f);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestFBOPassthrough.java b/engine/src/test/jme3test/post/TestFBOPassthrough.java
new file mode 100644
index 0000000..fee11fe
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestFBOPassthrough.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+
+/**
+ * Demonstrates FrameBuffer usage.
+ * The scene is first rendered to an FB with a texture attached,
+ * the texture is then rendered onto the screen in ortho mode.
+ *
+ * @author Kirill
+ */
+public class TestFBOPassthrough extends SimpleApplication {
+
+ private Node fbNode = new Node("Framebuffer Node");
+ private FrameBuffer fb;
+
+ public static void main(String[] args){
+ TestFBOPassthrough app = new TestFBOPassthrough();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ int w = settings.getWidth();
+ int h = settings.getHeight();
+
+ //setup framebuffer
+ fb = new FrameBuffer(w, h, 1);
+
+ Texture2D fbTex = new Texture2D(w, h, Format.RGBA8);
+ fb.setDepthBuffer(Format.Depth);
+ fb.setColorTexture(fbTex);
+
+ // setup framebuffer's scene
+ Sphere sphMesh = new Sphere(20, 20, 1);
+ Material solidColor = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+
+ Geometry sphere = new Geometry("sphere", sphMesh);
+ sphere.setMaterial(solidColor);
+ fbNode.attachChild(sphere);
+
+ //setup main scene
+ Picture p = new Picture("Picture");
+ p.setPosition(0, 0);
+ p.setWidth(w);
+ p.setHeight(h);
+ p.setTexture(assetManager, fbTex, false);
+
+ rootNode.attachChild(p);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ fbNode.updateLogicalState(tpf);
+ fbNode.updateGeometricState();
+ }
+
+ @Override
+ public void simpleRender(RenderManager rm){
+ Renderer r = rm.getRenderer();
+
+ //do FBO rendering
+ r.setFrameBuffer(fb);
+
+ rm.setCamera(cam, false); // FBO uses current camera
+ r.clearBuffers(true, true, true);
+ rm.renderScene(fbNode, viewPort);
+ rm.flushQueue(viewPort);
+
+ //go back to default rendering and let
+ //SimpleApplication render the default scene
+ r.setFrameBuffer(null);
+ }
+
+}
diff --git a/engine/src/test/jme3test/post/TestFog.java b/engine/src/test/jme3test/post/TestFog.java
new file mode 100644
index 0000000..220a070
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestFog.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.FogFilter;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.util.SkyFactory;
+import java.io.File;
+
+public class TestFog extends SimpleApplication {
+
+ private FilterPostProcessor fpp;
+ private boolean enabled=true;
+ private FogFilter fog;
+
+ // set default for applets
+ private static boolean useHttp = true;
+
+ public static void main(String[] args) {
+ File file = new File("wildhouse.zip");
+ if (file.exists()) {
+ useHttp = false;
+ }
+ TestFog app = new TestFog();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ this.flyCam.setMoveSpeed(10);
+ Node mainScene=new Node();
+ cam.setLocation(new Vector3f(-27.0f, 1.0f, 75.0f));
+ cam.setRotation(new Quaternion(0.03f, 0.9f, 0f, 0.4f));
+
+ // load sky
+ mainScene.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+ // create the geometry and attach it
+ // load the level from zip or http zip
+ if (useHttp) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", HttpZipLocator.class.getName());
+ } else {
+ assetManager.registerLocator("wildhouse.zip", ZipLocator.class.getName());
+ }
+ Spatial scene = assetManager.loadModel("main.scene");
+
+ DirectionalLight sun = new DirectionalLight();
+ Vector3f lightDir=new Vector3f(-0.37352666f, -0.50444174f, -0.7784704f);
+ sun.setDirection(lightDir);
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ scene.addLight(sun);
+
+
+ mainScene.attachChild(scene);
+ rootNode.attachChild(mainScene);
+
+ fpp=new FilterPostProcessor(assetManager);
+ //fpp.setNumSamples(4);
+ fog=new FogFilter();
+ fog.setFogColor(new ColorRGBA(0.9f, 0.9f, 0.9f, 1.0f));
+ fog.setFogDistance(155);
+ fog.setFogDensity(2.0f);
+ fpp.addFilter(fog);
+ viewPort.addProcessor(fpp);
+ initInputs();
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("DensityUp", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("DensityDown", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("DistanceUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("DistanceDown", new KeyTrigger(KeyInput.KEY_J));
+
+
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("toggle") && keyPressed) {
+ if(enabled){
+ enabled=false;
+ viewPort.removeProcessor(fpp);
+ }else{
+ enabled=true;
+ viewPort.addProcessor(fpp);
+ }
+ }
+
+ }
+ };
+
+ AnalogListener anl=new AnalogListener() {
+
+ public void onAnalog(String name, float isPressed, float tpf) {
+ if(name.equals("DensityUp")){
+ fog.setFogDensity(fog.getFogDensity()+0.001f);
+ System.out.println("Fog density : "+fog.getFogDensity());
+ }
+ if(name.equals("DensityDown")){
+ fog.setFogDensity(fog.getFogDensity()-0.010f);
+ System.out.println("Fog density : "+fog.getFogDensity());
+ }
+ if(name.equals("DistanceUp")){
+ fog.setFogDistance(fog.getFogDistance()+0.5f);
+ System.out.println("Fog Distance : "+fog.getFogDistance());
+ }
+ if(name.equals("DistanceDown")){
+ fog.setFogDistance(fog.getFogDistance()-0.5f);
+ System.out.println("Fog Distance : "+fog.getFogDistance());
+ }
+
+ }
+ };
+
+ inputManager.addListener(acl, "toggle");
+ inputManager.addListener(anl, "DensityUp","DensityDown","DistanceUp","DistanceDown");
+
+ }
+}
+
diff --git a/engine/src/test/jme3test/post/TestHDR.java b/engine/src/test/jme3test/post/TestHDR.java
new file mode 100644
index 0000000..4b26069
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestHDR.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.post.HDRRenderer;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.ui.Picture;
+
+public class TestHDR extends SimpleApplication {
+
+ private HDRRenderer hdrRender;
+ private Picture dispQuad;
+
+ public static void main(String[] args){
+ TestHDR app = new TestHDR();
+ app.start();
+ }
+
+ public Geometry createHDRBox(){
+ Box boxMesh = new Box(Vector3f.ZERO, 1, 1, 1);
+ Geometry box = new Geometry("Box", boxMesh);
+
+// Material mat = assetManager.loadMaterial("Textures/HdrTest/Memorial.j3m");
+// box.setMaterial(mat);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Textures/HdrTest/Memorial.hdr"));
+ box.setMaterial(mat);
+
+ return box;
+ }
+
+// private Material disp;
+
+ @Override
+ public void simpleInitApp() {
+ hdrRender = new HDRRenderer(assetManager, renderer);
+ hdrRender.setSamples(0);
+ hdrRender.setMaxIterations(20);
+ hdrRender.setExposure(0.87f);
+ hdrRender.setThrottle(0.33f);
+
+ viewPort.addProcessor(hdrRender);
+
+// config.setVisible(true);
+
+ rootNode.attachChild(createHDRBox());
+ }
+
+ public void simpleUpdate(float tpf){
+ if (hdrRender.isInitialized() && dispQuad == null){
+ dispQuad = hdrRender.createDisplayQuad();
+ dispQuad.setWidth(128);
+ dispQuad.setHeight(128);
+ dispQuad.setPosition(30, cam.getHeight() - 128 - 30);
+ guiNode.attachChild(dispQuad);
+ }
+ }
+
+// public void displayAvg(Renderer r){
+// r.setFrameBuffer(null);
+// disp = prepare(-1, -1, settings.getWidth(), settings.getHeight(), 3, -1, scene64, disp);
+// r.clearBuffers(true, true, true);
+// r.renderGeometry(pic);
+// }
+
+}
diff --git a/engine/src/test/jme3test/post/TestLightScattering.java b/engine/src/test/jme3test/post/TestLightScattering.java
new file mode 100644
index 0000000..cd07952
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestLightScattering.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.StatsView;
+import com.jme3.font.BitmapText;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.LightScatteringFilter;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.shadow.PssmShadowRenderer;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestLightScattering extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestLightScattering app = new TestLightScattering();
+
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // put the camera in a bad position
+ cam.setLocation(new Vector3f(55.35316f, -0.27061665f, 27.092093f));
+ cam.setRotation(new Quaternion(0.010414706f, 0.9874893f, 0.13880467f, -0.07409228f));
+// cam.setDirection(new Vector3f(0,-0.5f,1.0f));
+// cam.setLocation(new Vector3f(0, 300, -500));
+ //cam.setFrustumFar(1000);
+ flyCam.setMoveSpeed(10);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Rocky/Rocky.j3m");
+ Spatial scene = assetManager.loadModel("Models/Terrain/Terrain.mesh.xml");
+ TangentBinormalGenerator.generate(((Geometry)((Node)scene).getChild(0)).getMesh());
+ scene.setMaterial(mat);
+ scene.setShadowMode(ShadowMode.CastAndReceive);
+ scene.setLocalScale(400);
+ scene.setLocalTranslation(0, -10, -120);
+
+ rootNode.attachChild(scene);
+
+ // load sky
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false));
+
+ DirectionalLight sun = new DirectionalLight();
+ Vector3f lightDir = new Vector3f(-0.12f, -0.3729129f, 0.74847335f);
+ sun.setDirection(lightDir);
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ scene.addLight(sun);
+
+ PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager,1024,4);
+ pssmRenderer.setDirection(lightDir);
+ pssmRenderer.setShadowIntensity(0.55f);
+ // viewPort.addProcessor(pssmRenderer);
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+// SSAOFilter ssaoFilter= new SSAOFilter(viewPort, new SSAOConfig(0.36f,1.8f,0.84f,0.16f,false,true));
+// fpp.addFilter(ssaoFilter);
+
+
+// Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+// mat2.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+//
+// Sphere lite=new Sphere(8, 8, 10.0f);
+// Geometry lightSphere=new Geometry("lightsphere", lite);
+// lightSphere.setMaterial(mat2);
+ Vector3f lightPos = lightDir.multLocal(-3000);
+// lightSphere.setLocalTranslation(lightPos);
+ // rootNode.attachChild(lightSphere);
+ LightScatteringFilter filter = new LightScatteringFilter(lightPos);
+ LightScatteringUI ui = new LightScatteringUI(inputManager, filter);
+ fpp.addFilter(filter);
+//fpp.setNumSamples(4);
+ //fpp.addFilter(new RadialBlurFilter(0.3f,15.0f));
+ // SSAOUI ui=new SSAOUI(inputManager, ssaoFilter.getConfig());
+
+ viewPort.addProcessor(fpp);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestMultiRenderTarget.java b/engine/src/test/jme3test/post/TestMultiRenderTarget.java
new file mode 100644
index 0000000..5b31bdd
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestMultiRenderTarget.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+
+public class TestMultiRenderTarget extends SimpleApplication implements SceneProcessor {
+
+ private FrameBuffer fb;
+ private Texture2D diffuseData, normalData, specularData, depthData;
+ private Geometry sphere;
+ private Picture display1, display2, display3, display4;
+
+ private Picture display;
+ private Material mat;
+
+ public static void main(String[] args){
+ TestMultiRenderTarget app = new TestMultiRenderTarget();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ viewPort.addProcessor(this);
+ renderManager.setForcedTechnique("GBuf");
+
+// flyCam.setEnabled(false);
+ cam.setLocation(new Vector3f(4.8037705f, 4.851632f, 10.789033f));
+ cam.setRotation(new Quaternion(-0.05143692f, 0.9483723f, -0.21131563f, -0.230846f));
+
+ Node tank = (Node) assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml");
+
+ //tankMesh.getMaterial().setColor("Specular", ColorRGBA.Black);
+ rootNode.attachChild(tank);
+
+ display1 = new Picture("Picture");
+ display1.move(0, 0, -1); // make it appear behind stats view
+ display2 = (Picture) display1.clone();
+ display3 = (Picture) display1.clone();
+ display4 = (Picture) display1.clone();
+ display = (Picture) display1.clone();
+
+ ColorRGBA[] colors = new ColorRGBA[]{
+ ColorRGBA.White,
+ ColorRGBA.Blue,
+ ColorRGBA.Cyan,
+ ColorRGBA.DarkGray,
+ ColorRGBA.Green,
+ ColorRGBA.Magenta,
+ ColorRGBA.Orange,
+ ColorRGBA.Pink,
+ ColorRGBA.Red,
+ ColorRGBA.Yellow
+ };
+
+ for (int i = 0; i < 3; i++){
+ PointLight pl = new PointLight();
+ float angle = 0.314159265f * i;
+ pl.setPosition( new Vector3f(FastMath.cos(angle)*2f, 0,
+ FastMath.sin(angle)*2f));
+ pl.setColor(colors[i]);
+ pl.setRadius(5);
+ rootNode.addLight(pl);
+ display.addLight(pl);
+ }
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight());
+ viewPort.setOutputFrameBuffer(fb);
+ guiViewPort.setClearFlags(true, true, true);
+ guiNode.attachChild(display);
+// guiNode.attachChild(display1);
+// guiNode.attachChild(display2);
+// guiNode.attachChild(display3);
+// guiNode.attachChild(display4);
+ guiNode.updateGeometricState();
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ diffuseData = new Texture2D(w, h, Format.RGBA8);
+ normalData = new Texture2D(w, h, Format.RGBA8);
+ specularData = new Texture2D(w, h, Format.RGBA8);
+ depthData = new Texture2D(w, h, Format.Depth);
+
+ mat = new Material(assetManager, "Common/MatDefs/Light/Deferred.j3md");
+ mat.setTexture("DiffuseData", diffuseData);
+ mat.setTexture("SpecularData", specularData);
+ mat.setTexture("NormalData", normalData);
+ mat.setTexture("DepthData", depthData);
+
+ display.setMaterial(mat);
+ display.setPosition(0, 0);
+ display.setWidth(w);
+ display.setHeight(h);
+
+ display1.setTexture(assetManager, diffuseData, false);
+ display2.setTexture(assetManager, normalData, false);
+ display3.setTexture(assetManager, specularData, false);
+ display4.setTexture(assetManager, depthData, false);
+
+ display1.setPosition(0, 0);
+ display2.setPosition(w/2, 0);
+ display3.setPosition(0, h/2);
+ display4.setPosition(w/2, h/2);
+
+ display1.setWidth(w/2);
+ display1.setHeight(h/2);
+
+ display2.setWidth(w/2);
+ display2.setHeight(h/2);
+
+ display3.setWidth(w/2);
+ display3.setHeight(h/2);
+
+ display4.setWidth(w/2);
+ display4.setHeight(h/2);
+
+ guiNode.updateGeometricState();
+
+ fb = new FrameBuffer(w, h, 1);
+ fb.setDepthTexture(depthData);
+ fb.addColorTexture(diffuseData);
+ fb.addColorTexture(normalData);
+ fb.addColorTexture(specularData);
+ fb.setMultiTarget(true);
+
+ /*
+ * Marks pixels in front of the far light boundary
+ Render back-faces of light volume
+ Depth test GREATER-EQUAL
+ Write to stencil on depth pass
+ Skipped for very small distant lights
+ */
+
+ /*
+ * Find amount of lit pixels inside the volume
+ Start pixel query
+ Render front faces of light volume
+ Depth test LESS-EQUAL
+ Don’t write anything – only EQUAL stencil test
+ */
+
+ /*
+ * Enable conditional rendering
+ Based on query results from previous stage
+ GPU skips rendering for invisible lights
+ */
+
+ /*
+ * Render front-faces of light volume
+ Depth test - LESS-EQUAL
+ Stencil test - EQUAL
+ Runs only on marked pixels inside light
+ */
+ }
+
+ public boolean isInitialized() {
+ return diffuseData != null;
+ }
+
+ public void preFrame(float tpf) {
+ Matrix4f inverseViewProj = cam.getViewProjectionMatrix().invert();
+ mat.setMatrix4("ViewProjectionMatrixInverse", inverseViewProj);
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ public void postFrame(FrameBuffer out) {
+ }
+
+ public void cleanup() {
+ }
+
+}
diff --git a/engine/src/test/jme3test/post/TestMultiViewsFilters.java b/engine/src/test/jme3test/post/TestMultiViewsFilters.java
new file mode 100644
index 0000000..d2ef9d4
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestMultiViewsFilters.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.*;
+import com.jme3.post.ssao.SSAOFilter;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.util.SkyFactory;
+
+public class TestMultiViewsFilters extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestMultiViewsFilters app = new TestMultiViewsFilters();
+ app.start();
+ }
+ private boolean filterEnabled = true;
+
+ public void simpleInitApp() {
+ // create the geometry and attach it
+ Geometry teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teaGeom.scale(3);
+ teaGeom.getMaterial().setColor("GlowColor", ColorRGBA.Green);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White);
+ dl.setDirection(Vector3f.UNIT_XYZ.negate());
+
+ rootNode.addLight(dl);
+ rootNode.attachChild(teaGeom);
+
+ // Setup first view
+ cam.setViewPort(.5f, 1f, 0f, 0.5f);
+ cam.setLocation(new Vector3f(3.3212643f, 4.484704f, 4.2812433f));
+ cam.setRotation(new Quaternion(-0.07680723f, 0.92299235f, -0.2564353f, -0.27645364f));
+
+ // Setup second view
+ Camera cam2 = cam.clone();
+ cam2.setViewPort(0f, 0.5f, 0f, 0.5f);
+ cam2.setLocation(new Vector3f(-0.10947256f, 1.5760219f, 4.81758f));
+ cam2.setRotation(new Quaternion(0.0010108891f, 0.99857414f, -0.04928594f, 0.020481428f));
+
+ final ViewPort view2 = renderManager.createMainView("Bottom Left", cam2);
+ view2.setClearFlags(true, true, true);
+ view2.attachScene(rootNode);
+
+ // Setup third view
+ Camera cam3 = cam.clone();
+ cam3.setName("cam3");
+ cam3.setViewPort(0f, .5f, .5f, 1f);
+ cam3.setLocation(new Vector3f(0.2846221f, 6.4271426f, 0.23380789f));
+ cam3.setRotation(new Quaternion(0.004381671f, 0.72363687f, -0.69015175f, 0.0045953835f));
+
+ final ViewPort view3 = renderManager.createMainView("Top Left", cam3);
+ view3.setClearFlags(true, true, true);
+ view3.attachScene(rootNode);
+
+
+ // Setup fourth view
+ Camera cam4 = cam.clone();
+ cam4.setName("cam4");
+ cam4.setViewPort(.5f, 1f, .5f, 1f);
+
+ cam4.setLocation(new Vector3f(4.775564f, 1.4548365f, 0.11491505f));
+ cam4.setRotation(new Quaternion(0.02356979f, -0.74957186f, 0.026729556f, 0.66096294f));
+
+ final ViewPort view4 = renderManager.createMainView("Top Right", cam4);
+ view4.setClearFlags(true, true, true);
+ view4.attachScene(rootNode);
+
+// Camera cam5 = new Camera(200, 200);
+// cam5.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
+// cam5.setName("cam5");
+// cam5.setViewPort(5.23f, 6.33f, 0.56f, 1.66f);
+// this.setViewPortAreas(5.23f, 6.33f, 0.56f, 1.66f);
+// this.setViewPortCamSize(200, 200);
+// 1046,1266,112,332
+ Camera cam5 = cam.clone();
+ cam5.setName("cam5");
+ cam5.setViewPort(1046f/settings.getWidth(), 1266f/settings.getWidth(), 112f/settings.getHeight(), 332f/settings.getHeight());
+ cam5.setLocation(new Vector3f(0.2846221f, 6.4271426f, 0.23380789f));
+ cam5.setRotation(new Quaternion(0.004381671f, 0.72363687f, -0.69015175f, 0.0045953835f));
+
+ final ViewPort view5 = renderManager.createMainView("center", cam5);
+ view5.setClearFlags(true, true, true);
+ view5.attachScene(rootNode);
+
+
+
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+ final FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ final FilterPostProcessor fpp2 = new FilterPostProcessor(assetManager);
+ final FilterPostProcessor fpp3 = new FilterPostProcessor(assetManager);
+ final FilterPostProcessor fpp4 = new FilterPostProcessor(assetManager);
+ final FilterPostProcessor fpp5 = new FilterPostProcessor(assetManager);
+
+
+ // fpp.addFilter(new WaterFilter(rootNode, Vector3f.UNIT_Y.mult(-1)));
+ fpp3.addFilter(new CartoonEdgeFilter());
+
+ fpp2.addFilter(new CrossHatchFilter());
+ final FogFilter ff = new FogFilter(ColorRGBA.Yellow, 0.7f, 2);
+ fpp.addFilter(ff);
+
+ final RadialBlurFilter rbf = new RadialBlurFilter(1, 10);
+ // rbf.setEnabled(false);
+ fpp.addFilter(rbf);
+
+
+ SSAOFilter f = new SSAOFilter(1.8899765f, 20.490374f, 0.4699998f, 0.1f);;
+ fpp4.addFilter(f);
+ SSAOUI ui = new SSAOUI(inputManager, f);
+
+ fpp5.addFilter(new BloomFilter(BloomFilter.GlowMode.Objects));
+
+ viewPort.addProcessor(fpp);
+ view2.addProcessor(fpp2);
+ view3.addProcessor(fpp3);
+ view4.addProcessor(fpp4);
+ view5.addProcessor(fpp5);
+
+
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("press") && isPressed) {
+ if (filterEnabled) {
+ viewPort.removeProcessor(fpp);
+ view2.removeProcessor(fpp2);
+ view3.removeProcessor(fpp3);
+ view4.removeProcessor(fpp4);
+ view5.removeProcessor(fpp5);
+ } else {
+ viewPort.addProcessor(fpp);
+ view2.addProcessor(fpp2);
+ view3.addProcessor(fpp3);
+ view4.addProcessor(fpp4);
+ view5.addProcessor(fpp5);
+ }
+ filterEnabled = !filterEnabled;
+ }
+ if (name.equals("filter") && isPressed) {
+ ff.setEnabled(!ff.isEnabled());
+ rbf.setEnabled(!rbf.isEnabled());
+ }
+ }
+ }, "press", "filter");
+
+ inputManager.addMapping("press", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("filter", new KeyTrigger(KeyInput.KEY_F));
+
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestMultiplesFilters.java b/engine/src/test/jme3test/post/TestMultiplesFilters.java
new file mode 100644
index 0000000..87e4c4a
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestMultiplesFilters.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.post.filters.ColorOverlayFilter;
+import com.jme3.post.ssao.SSAOFilter;
+import com.jme3.scene.Spatial;
+import com.jme3.util.SkyFactory;
+import com.jme3.water.WaterFilter;
+import java.io.File;
+
+public class TestMultiplesFilters extends SimpleApplication {
+
+ private static boolean useHttp = false;
+
+ public static void main(String[] args) {
+ File file = new File("wildhouse.zip");
+ if (!file.exists()) {
+ useHttp = true;
+ }
+ TestMultiplesFilters app = new TestMultiplesFilters();
+ app.start();
+ }
+ SSAOFilter ssaoFilter;
+ FilterPostProcessor fpp;
+ boolean en = true;
+
+ public void simpleInitApp() {
+ this.flyCam.setMoveSpeed(10);
+ cam.setLocation(new Vector3f(6.0344796f, 1.5054002f, 55.572033f));
+ cam.setRotation(new Quaternion(0.0016069f, 0.9810479f, -0.008143323f, 0.19358753f));
+
+ // load sky
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+ // create the geometry and attach it
+ // load the level from zip or http zip
+ if (useHttp) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", HttpZipLocator.class.getName());
+ } else {
+ assetManager.registerLocator("wildhouse.zip", ZipLocator.class.getName());
+ }
+ Spatial scene = assetManager.loadModel("main.scene");
+
+
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(-0.4790551f, -0.39247334f, -0.7851566f));
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ scene.addLight(sun);
+
+ fpp = new FilterPostProcessor(assetManager);
+ // fpp.setNumSamples(4);
+ ssaoFilter = new SSAOFilter(0.92f, 2.2f, 0.46f, 0.2f);
+ final WaterFilter water=new WaterFilter(rootNode,new Vector3f(-0.4790551f, -0.39247334f, -0.7851566f));
+ water.setWaterHeight(-20);
+ SSAOUI ui=new SSAOUI(inputManager,ssaoFilter);
+ final BloomFilter bloom = new BloomFilter();
+ final ColorOverlayFilter overlay = new ColorOverlayFilter(ColorRGBA.LightGray);
+
+
+ fpp.addFilter(ssaoFilter);
+
+ fpp.addFilter(water);
+
+ fpp.addFilter(bloom);
+
+ fpp.addFilter(overlay);
+
+ viewPort.addProcessor(fpp);
+
+ rootNode.attachChild(scene);
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if ("toggleSSAO".equals(name) && isPressed) {
+ if (ssaoFilter.isEnabled()) {
+ ssaoFilter.setEnabled(false);
+ } else {
+ ssaoFilter.setEnabled(true);
+ }
+ }
+ if ("toggleWater".equals(name) && isPressed) {
+ if (water.isEnabled()) {
+ water.setEnabled(false);
+ } else {
+ water.setEnabled(true);
+ }
+ }
+ if ("toggleBloom".equals(name) && isPressed) {
+ if (bloom.isEnabled()) {
+ bloom.setEnabled(false);
+ } else {
+ bloom.setEnabled(true);
+ }
+ }
+ if ("toggleOverlay".equals(name) && isPressed) {
+ if (overlay.isEnabled()) {
+ overlay.setEnabled(false);
+ } else {
+ overlay.setEnabled(true);
+ }
+ }
+ }
+ }, "toggleSSAO", "toggleBloom", "toggleWater","toggleOverlay");
+ inputManager.addMapping("toggleSSAO", new KeyTrigger(KeyInput.KEY_1));
+ inputManager.addMapping("toggleWater", new KeyTrigger(KeyInput.KEY_2));
+ inputManager.addMapping("toggleBloom", new KeyTrigger(KeyInput.KEY_3));
+ inputManager.addMapping("toggleOverlay", new KeyTrigger(KeyInput.KEY_4));
+
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestPostFilters.java b/engine/src/test/jme3test/post/TestPostFilters.java
new file mode 100644
index 0000000..6b41059
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestPostFilters.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.ColorOverlayFilter;
+import com.jme3.post.filters.FadeFilter;
+import com.jme3.post.filters.RadialBlurFilter;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.shape.Box;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestPostFilters extends SimpleApplication implements ActionListener {
+
+ private FilterPostProcessor fpp;
+ private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal();
+ FadeFilter fade;
+
+ public static void main(String[] args) {
+ TestPostFilters app = new TestPostFilters();
+ app.start();
+ }
+
+ public void setupFilters() {
+ if (renderer.getCaps().contains(Caps.GLSL100)) {
+ fpp = new FilterPostProcessor(assetManager);
+ // fpp.setNumSamples(4);
+ fpp.addFilter(new ColorOverlayFilter(ColorRGBA.LightGray));
+ fpp.addFilter(new RadialBlurFilter());
+ //fade=new FadeFilter(1.0f);
+ //fpp.addFilter(fade);
+
+
+ viewPort.addProcessor(fpp);
+ }
+ }
+
+ public void setupSkyBox() {
+ Texture envMap;
+ if (renderer.getCaps().contains(Caps.FloatTexture)) {
+ envMap = assetManager.loadTexture("Textures/Sky/St Peters/StPeters.hdr");
+ } else {
+ envMap = assetManager.loadTexture("Textures/Sky/St Peters/StPeters.jpg");
+ }
+ rootNode.attachChild(SkyFactory.createSky(assetManager, envMap, new Vector3f(-1, -1, -1), true));
+ }
+
+ public void setupLighting() {
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(lightDir);
+
+ dl.setColor(new ColorRGBA(.9f, .9f, .9f, 1));
+
+ rootNode.addLight(dl);
+
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, 0, -1).normalizeLocal());
+
+ dl.setColor(new ColorRGBA(.4f, .4f, .4f, 1));
+
+ rootNode.addLight(dl);
+ }
+
+ public void setupFloor() {
+ Material mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m");
+ mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+ mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
+ Box floor = new Box(Vector3f.ZERO, 50, 1f, 50);
+ TangentBinormalGenerator.generate(floor);
+ floor.scaleTextureCoordinates(new Vector2f(5, 5));
+ Geometry floorGeom = new Geometry("Floor", floor);
+ floorGeom.setMaterial(mat);
+ floorGeom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(floorGeom);
+ }
+
+ public void setupSignpost() {
+ Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
+ Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
+ signpost.setMaterial(mat);
+ signpost.rotate(0, FastMath.HALF_PI, 0);
+ signpost.setLocalTranslation(12, 3.5f, 30);
+ signpost.setLocalScale(4);
+ signpost.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(signpost);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(-32.295086f, 54.80136f, 79.59805f));
+ cam.setRotation(new Quaternion(0.074364014f, 0.92519957f, -0.24794696f, 0.27748522f));
+ cam.update();
+
+ cam.setFrustumFar(300);
+ flyCam.setMoveSpeed(30);
+
+ rootNode.setCullHint(CullHint.Never);
+
+ setupLighting();
+ setupSkyBox();
+
+
+ setupFloor();
+
+ setupSignpost();
+
+ setupFilters();
+
+ initInput();
+
+ }
+
+ protected void initInput() {
+ flyCam.setMoveSpeed(3);
+ //init input
+ inputManager.addMapping("fadein", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addListener(this, "fadein");
+ inputManager.addMapping("fadeout", new KeyTrigger(KeyInput.KEY_O));
+ inputManager.addListener(this, "fadeout");
+
+ }
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (name.equals("fadein") && value) {
+ fade.fadeIn();
+ System.out.println("fade in");
+
+ }
+ if (name.equals("fadeout") && value) {
+ fade.fadeOut();
+ System.out.println("fade out");
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestPosterization.java b/engine/src/test/jme3test/post/TestPosterization.java
new file mode 100644
index 0000000..e2c24e8
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestPosterization.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.PosterizationFilter;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.scene.shape.Box;
+import com.jme3.util.SkyFactory;
+
+public class TestPosterization extends SimpleApplication {
+
+ float angle;
+ Spatial lightMdl;
+ Spatial teapot;
+ Geometry frustumMdl;
+ WireFrustum frustum;
+ boolean active=true;
+ FilterPostProcessor fpp;
+
+ public static void main(String[] args){
+ TestPosterization app = new TestPosterization();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ // put the camera in a bad position
+ cam.setLocation(new Vector3f(-2.336393f, 11.91392f, -7.139601f));
+ cam.setRotation(new Quaternion(0.23602544f, 0.11321983f, -0.027698677f, 0.96473104f));
+ //cam.setFrustumFar(1000);
+
+
+ Material mat = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 15f);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Yellow.mult(0.2f));
+ mat.setColor("Diffuse", ColorRGBA.Yellow.mult(0.2f));
+ mat.setColor("Specular", ColorRGBA.Yellow.mult(0.8f));
+
+
+
+
+ Material matSoil = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
+ matSoil.setFloat("Shininess", 15f);
+ matSoil.setBoolean("UseMaterialColors", true);
+ matSoil.setColor("Ambient", ColorRGBA.Gray);
+ matSoil.setColor("Diffuse", ColorRGBA.Black);
+ matSoil.setColor("Specular", ColorRGBA.Gray);
+
+
+
+ teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teapot.setLocalTranslation(0,0,10);
+
+ teapot.setMaterial(mat);
+ teapot.setShadowMode(ShadowMode.CastAndReceive);
+ teapot.setLocalScale(10.0f);
+ rootNode.attachChild(teapot);
+
+
+
+ Geometry soil=new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700));
+ soil.setMaterial(matSoil);
+ soil.setShadowMode(ShadowMode.CastAndReceive);
+ rootNode.attachChild(soil);
+
+ DirectionalLight light=new DirectionalLight();
+ light.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ light.setColor(ColorRGBA.White.mult(1.5f));
+ rootNode.addLight(light);
+
+ // load sky
+ Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false);
+ sky.setCullHint(Spatial.CullHint.Never);
+ rootNode.attachChild(sky);
+
+ fpp=new FilterPostProcessor(assetManager);
+ PosterizationFilter pf=new PosterizationFilter();
+
+
+
+ viewPort.addProcessor(fpp);
+ fpp.addFilter(pf);
+ initInputs();
+
+ }
+
+ private void initInputs() {
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+
+ ActionListener acl = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ if (name.equals("toggle") && keyPressed) {
+ if(active){
+ active=false;
+ viewPort.removeProcessor(fpp);
+ }else{
+ active=true;
+ viewPort.addProcessor(fpp);
+ }
+ }
+ }
+ };
+
+ inputManager.addListener(acl, "toggle");
+
+ }
+
+
+
+}
diff --git a/engine/src/test/jme3test/post/TestRenderToMemory.java b/engine/src/test/jme3test/post/TestRenderToMemory.java
new file mode 100644
index 0000000..4a97844
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestRenderToMemory.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.Screenshots;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+/**
+ * This test renders a scene to an offscreen framebuffer, then copies
+ * the contents to a Swing JFrame. Note that some parts are done inefficently,
+ * this is done to make the code more readable.
+ */
+public class TestRenderToMemory extends SimpleApplication implements SceneProcessor {
+
+ private Geometry offBox;
+ private float angle = 0;
+
+ private FrameBuffer offBuffer;
+ private ViewPort offView;
+ private Texture2D offTex;
+ private Camera offCamera;
+ private ImageDisplay display;
+
+ private static final int width = 800, height = 600;
+
+ private final ByteBuffer cpuBuf = BufferUtils.createByteBuffer(width * height * 4);
+ private final byte[] cpuArray = new byte[width * height * 4];
+ private final BufferedImage image = new BufferedImage(width, height,
+ BufferedImage.TYPE_4BYTE_ABGR);
+
+ private class ImageDisplay extends JPanel {
+
+ private long t;
+ private long total;
+ private int frames;
+ private int fps;
+
+ @Override
+ public void paintComponent(Graphics gfx) {
+ super.paintComponent(gfx);
+ Graphics2D g2d = (Graphics2D) gfx;
+
+ if (t == 0)
+ t = timer.getTime();
+
+// g2d.setBackground(Color.BLACK);
+// g2d.clearRect(0,0,width,height);
+
+ synchronized (image){
+ g2d.drawImage(image, null, 0, 0);
+ }
+
+ long t2 = timer.getTime();
+ long dt = t2 - t;
+ total += dt;
+ frames ++;
+ t = t2;
+
+ if (total > 1000){
+ fps = frames;
+ total = 0;
+ frames = 0;
+ }
+
+ g2d.setColor(Color.white);
+ g2d.drawString("FPS: "+fps, 0, getHeight() - 100);
+ }
+ }
+
+ public static void main(String[] args){
+ TestRenderToMemory app = new TestRenderToMemory();
+ app.setPauseOnLostFocus(false);
+ AppSettings settings = new AppSettings(true);
+ settings.setResolution(1, 1);
+ app.setSettings(settings);
+ app.start(Type.OffscreenSurface);
+ }
+
+ public void createDisplayFrame(){
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ JFrame frame = new JFrame("Render Display");
+ display = new ImageDisplay();
+ display.setPreferredSize(new Dimension(width, height));
+ frame.getContentPane().add(display);
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter(){
+ public void windowClosed(WindowEvent e){
+ stop();
+ }
+ });
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setResizable(false);
+ frame.setVisible(true);
+ }
+ });
+ }
+
+ public void updateImageContents(){
+ cpuBuf.clear();
+ renderer.readFrameBuffer(offBuffer, cpuBuf);
+
+ synchronized (image) {
+ Screenshots.convertScreenShot(cpuBuf, image);
+ }
+
+ if (display != null)
+ display.repaint();
+ }
+
+ public void setupOffscreenView(){
+ offCamera = new Camera(width, height);
+
+ // create a pre-view. a view that is rendered before the main view
+ offView = renderManager.createPreView("Offscreen View", offCamera);
+ offView.setBackgroundColor(ColorRGBA.DarkGray);
+ offView.setClearFlags(true, true, true);
+
+ // this will let us know when the scene has been rendered to the
+ // frame buffer
+ offView.addProcessor(this);
+
+ // create offscreen framebuffer
+ offBuffer = new FrameBuffer(width, height, 1);
+
+ //setup framebuffer's cam
+ offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);
+ offCamera.setLocation(new Vector3f(0f, 0f, -5f));
+ offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
+
+ //setup framebuffer's texture
+// offTex = new Texture2D(width, height, Format.RGBA8);
+
+ //setup framebuffer to use renderbuffer
+ // this is faster for gpu -> cpu copies
+ offBuffer.setDepthBuffer(Format.Depth);
+ offBuffer.setColorBuffer(Format.RGBA8);
+// offBuffer.setColorTexture(offTex);
+
+ //set viewport to render to offscreen framebuffer
+ offView.setOutputFrameBuffer(offBuffer);
+
+ // setup framebuffer's scene
+ Box boxMesh = new Box(Vector3f.ZERO, 1,1,1);
+ Material material = assetManager.loadMaterial("Interface/Logo/Logo.j3m");
+ offBox = new Geometry("box", boxMesh);
+ offBox.setMaterial(material);
+
+ // attach the scene to the viewport to be rendered
+ offView.attachScene(offBox);
+ }
+
+ @Override
+ public void simpleInitApp() {
+ setupOffscreenView();
+ createDisplayFrame();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ Quaternion q = new Quaternion();
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+ q.fromAngles(angle, 0, angle);
+
+ offBox.setLocalRotation(q);
+ offBox.updateLogicalState(tpf);
+ offBox.updateGeometricState();
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public boolean isInitialized() {
+ return true;
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ }
+
+ /**
+ * Update the CPU image's contents after the scene has
+ * been rendered to the framebuffer.
+ */
+ public void postFrame(FrameBuffer out) {
+ updateImageContents();
+ }
+
+ public void cleanup() {
+ }
+
+
+}
diff --git a/engine/src/test/jme3test/post/TestRenderToTexture.java b/engine/src/test/jme3test/post/TestRenderToTexture.java
new file mode 100644
index 0000000..ccc993a
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestRenderToTexture.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+
+/**
+ * This test renders a scene to a texture, then displays the texture on a cube.
+ */
+public class TestRenderToTexture extends SimpleApplication implements ActionListener {
+
+ private static final String TOGGLE_UPDATE = "Toggle Update";
+ private Geometry offBox;
+ private float angle = 0;
+ private ViewPort offView;
+
+ public static void main(String[] args){
+ TestRenderToTexture app = new TestRenderToTexture();
+ app.start();
+ }
+
+ public Texture setupOffscreenView(){
+ Camera offCamera = new Camera(512, 512);
+
+ offView = renderManager.createPreView("Offscreen View", offCamera);
+ offView.setClearFlags(true, true, true);
+ offView.setBackgroundColor(ColorRGBA.DarkGray);
+
+ // create offscreen framebuffer
+ FrameBuffer offBuffer = new FrameBuffer(512, 512, 1);
+
+ //setup framebuffer's cam
+ offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);
+ offCamera.setLocation(new Vector3f(0f, 0f, -5f));
+ offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
+
+ //setup framebuffer's texture
+ Texture2D offTex = new Texture2D(512, 512, Format.RGBA8);
+ offTex.setMinFilter(Texture.MinFilter.Trilinear);
+ offTex.setMagFilter(Texture.MagFilter.Bilinear);
+
+ //setup framebuffer to use texture
+ offBuffer.setDepthBuffer(Format.Depth);
+ offBuffer.setColorTexture(offTex);
+
+ //set viewport to render to offscreen framebuffer
+ offView.setOutputFrameBuffer(offBuffer);
+
+ // setup framebuffer's scene
+ Box boxMesh = new Box(Vector3f.ZERO, 1,1,1);
+ Material material = assetManager.loadMaterial("Interface/Logo/Logo.j3m");
+ offBox = new Geometry("box", boxMesh);
+ offBox.setMaterial(material);
+
+ // attach the scene to the viewport to be rendered
+ offView.attachScene(offBox);
+
+ return offTex;
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(3, 3, 3));
+ cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+
+ //setup main scene
+ Geometry quad = new Geometry("box", new Box(Vector3f.ZERO, 1,1,1));
+
+ Texture offTex = setupOffscreenView();
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", offTex);
+ quad.setMaterial(mat);
+ rootNode.attachChild(quad);
+ inputManager.addMapping(TOGGLE_UPDATE, new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addListener(this, TOGGLE_UPDATE);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ Quaternion q = new Quaternion();
+
+ if (offView.isEnabled()) {
+ angle += tpf;
+ angle %= FastMath.TWO_PI;
+ q.fromAngles(angle, 0, angle);
+
+ offBox.setLocalRotation(q);
+ offBox.updateLogicalState(tpf);
+ offBox.updateGeometricState();
+ }
+ }
+
+ @Override
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals(TOGGLE_UPDATE) && isPressed) {
+ offView.setEnabled(!offView.isEnabled());
+ }
+ }
+
+
+}
diff --git a/engine/src/test/jme3test/post/TestSSAO.java b/engine/src/test/jme3test/post/TestSSAO.java
new file mode 100644
index 0000000..e9cef98
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestSSAO.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.ssao.SSAOFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.texture.Texture;
+
+public class TestSSAO extends SimpleApplication {
+
+ Geometry model;
+
+ public static void main(String[] args) {
+ TestSSAO app = new TestSSAO();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ cam.setLocation(new Vector3f(68.45442f, 8.235511f, 7.9676695f));
+ cam.setRotation(new Quaternion(0.046916496f, -0.69500375f, 0.045538206f, 0.7160271f));
+
+
+ flyCam.setMoveSpeed(50);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ Texture diff = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");
+ diff.setWrap(Texture.WrapMode.Repeat);
+ Texture norm = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_normal.jpg");
+ norm.setWrap(Texture.WrapMode.Repeat);
+ mat.setTexture("DiffuseMap", diff);
+ mat.setTexture("NormalMap", norm);
+ mat.setFloat("Shininess", 2.0f);
+
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(new ColorRGBA(1.8f, 1.8f, 1.8f, 1.0f));
+
+ rootNode.addLight(al);
+
+ model = (Geometry) assetManager.loadModel("Models/Sponza/Sponza.j3o");
+ model.getMesh().scaleTextureCoordinates(new Vector2f(2, 2));
+
+ model.setMaterial(mat);
+
+ rootNode.attachChild(model);
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ SSAOFilter ssaoFilter = new SSAOFilter(12.940201f, 43.928635f, 0.32999992f, 0.6059958f);
+ fpp.addFilter(ssaoFilter);
+ SSAOUI ui = new SSAOUI(inputManager, ssaoFilter);
+
+ viewPort.addProcessor(fpp);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestTransparentCartoonEdge.java b/engine/src/test/jme3test/post/TestTransparentCartoonEdge.java
new file mode 100644
index 0000000..46ba0ac
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestTransparentCartoonEdge.java
@@ -0,0 +1,97 @@
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.CartoonEdgeFilter;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture;
+
+public class TestTransparentCartoonEdge extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestTransparentCartoonEdge app = new TestTransparentCartoonEdge();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ renderManager.setAlphaToCoverage(true);
+ cam.setLocation(new Vector3f(0.14914267f, 0.58147097f, 4.7686534f));
+ cam.setRotation(new Quaternion(-0.0044764364f, 0.9767943f, 0.21314798f, 0.020512417f));
+
+// cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f));
+// cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f, -0.13857932f));
+
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+
+ Quad q = new Quad(20, 20);
+ q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5));
+ Geometry geom = new Geometry("floor", q);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ geom.setMaterial(mat);
+
+ geom.rotate(-FastMath.HALF_PI, 0, 0);
+ geom.center();
+ geom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(geom);
+
+ // create the geometry and attach it
+ Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
+ teaGeom.setQueueBucket(Bucket.Transparent);
+ teaGeom.setShadowMode(ShadowMode.Cast);
+ makeToonish(teaGeom);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White.mult(2));
+ rootNode.addLight(al);
+
+ DirectionalLight dl1 = new DirectionalLight();
+ dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal());
+ dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+ rootNode.addLight(dl1);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+ rootNode.addLight(dl);
+
+ rootNode.attachChild(teaGeom);
+
+ FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
+ CartoonEdgeFilter toon=new CartoonEdgeFilter();
+ toon.setEdgeWidth(0.5f);
+ toon.setEdgeIntensity(1.0f);
+ toon.setNormalThreshold(0.8f);
+ fpp.addFilter(toon);
+ viewPort.addProcessor(fpp);
+ }
+
+ public void makeToonish(Spatial spatial){
+ if (spatial instanceof Node){
+ Node n = (Node) spatial;
+ for (Spatial child : n.getChildren())
+ makeToonish(child);
+ }else if (spatial instanceof Geometry){
+ Geometry g = (Geometry) spatial;
+ Material m = g.getMaterial();
+ if (m.getMaterialDef().getName().equals("Phong Lighting")){
+ Texture t = assetManager.loadTexture("Textures/ColorRamp/toon.png");
+// t.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+// t.setMagFilter(Texture.MagFilter.Nearest);
+ m.setTexture("ColorRamp", t);
+ m.setBoolean("UseMaterialColors", true);
+ m.setColor("Specular", ColorRGBA.Black);
+ m.setColor("Diffuse", ColorRGBA.White);
+ m.setBoolean("VertexLighting", true);
+ }
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/post/TestTransparentSSAO.java b/engine/src/test/jme3test/post/TestTransparentSSAO.java
new file mode 100644
index 0000000..3d45b4a
--- /dev/null
+++ b/engine/src/test/jme3test/post/TestTransparentSSAO.java
@@ -0,0 +1,74 @@
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.ssao.SSAOFilter;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+
+public class TestTransparentSSAO extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestTransparentSSAO app = new TestTransparentSSAO();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ renderManager.setAlphaToCoverage(true);
+ cam.setLocation(new Vector3f(0.14914267f, 0.58147097f, 4.7686534f));
+ cam.setRotation(new Quaternion(-0.0044764364f, 0.9767943f, 0.21314798f, 0.020512417f));
+
+// cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f));
+// cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f, -0.13857932f));
+
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+
+ Quad q = new Quad(20, 20);
+ q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5));
+ Geometry geom = new Geometry("floor", q);
+ Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+ geom.setMaterial(mat);
+
+ geom.rotate(-FastMath.HALF_PI, 0, 0);
+ geom.center();
+ geom.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(geom);
+
+ // create the geometry and attach it
+ Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
+ teaGeom.setQueueBucket(Bucket.Transparent);
+ teaGeom.setShadowMode(ShadowMode.Cast);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White.mult(2));
+ rootNode.addLight(al);
+
+ DirectionalLight dl1 = new DirectionalLight();
+ dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal());
+ dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+ rootNode.addLight(dl1);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+ rootNode.addLight(dl);
+
+ rootNode.attachChild(teaGeom);
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+
+ SSAOFilter ssao = new SSAOFilter(0.49997783f, 42.598858f, 35.999966f, 0.39299846f);
+ fpp.addFilter(ssao);
+
+ SSAOUI ui = new SSAOUI(inputManager, ssao);
+
+ viewPort.addProcessor(fpp);
+ }
+}
diff --git a/engine/src/test/jme3test/renderer/TestMultiViews.java b/engine/src/test/jme3test/renderer/TestMultiViews.java
new file mode 100644
index 0000000..aa6b67e
--- /dev/null
+++ b/engine/src/test/jme3test/renderer/TestMultiViews.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.renderer;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+
+public class TestMultiViews extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestMultiViews app = new TestMultiViews();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ // create the geometry and attach it
+ Geometry teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+ teaGeom.scale(3);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White);
+ dl.setDirection(Vector3f.UNIT_XYZ.negate());
+
+ rootNode.addLight(dl);
+ rootNode.attachChild(teaGeom);
+
+ // Setup first view
+ viewPort.setBackgroundColor(ColorRGBA.Blue);
+ cam.setViewPort(.5f, 1f, 0f, 0.5f);
+ cam.setLocation(new Vector3f(3.3212643f, 4.484704f, 4.2812433f));
+ cam.setRotation(new Quaternion(-0.07680723f, 0.92299235f, -0.2564353f, -0.27645364f));
+
+ // Setup second view
+ Camera cam2 = cam.clone();
+ cam2.setViewPort(0f, 0.5f, 0f, 0.5f);
+ cam2.setLocation(new Vector3f(-0.10947256f, 1.5760219f, 4.81758f));
+ cam2.setRotation(new Quaternion(0.0010108891f, 0.99857414f, -0.04928594f, 0.020481428f));
+
+ ViewPort view2 = renderManager.createMainView("Bottom Left", cam2);
+ view2.setClearFlags(true, true, true);
+ view2.attachScene(rootNode);
+
+ // Setup third view
+ Camera cam3 = cam.clone();
+ cam3.setViewPort(0f, .5f, .5f, 1f);
+ cam3.setLocation(new Vector3f(0.2846221f, 6.4271426f, 0.23380789f));
+ cam3.setRotation(new Quaternion(0.004381671f, 0.72363687f, -0.69015175f, 0.0045953835f));
+
+ ViewPort view3 = renderManager.createMainView("Top Left", cam3);
+ view3.setClearFlags(true, true, true);
+ view3.attachScene(rootNode);
+
+ // Setup fourth view
+ Camera cam4 = cam.clone();
+ cam4.setViewPort(.5f, 1f, .5f, 1f);
+ cam4.setLocation(new Vector3f(4.775564f, 1.4548365f, 0.11491505f));
+ cam4.setRotation(new Quaternion(0.02356979f, -0.74957186f, 0.026729556f, 0.66096294f));
+
+ ViewPort view4 = renderManager.createMainView("Top Right", cam4);
+ view4.setClearFlags(true, true, true);
+ view4.attachScene(rootNode);
+
+ //test multiview for gui
+ guiViewPort.getCamera().setViewPort(.5f, 1f, .5f, 1f);
+
+ // Setup second gui view
+ Camera guiCam2 = guiViewPort.getCamera().clone();
+ guiCam2.setViewPort(0f, 0.5f, 0f, 0.5f);
+ ViewPort guiViewPort2 = renderManager.createPostView("Gui 2", guiCam2);
+ guiViewPort2.setClearFlags(false, false, false);
+ guiViewPort2.attachScene(guiViewPort.getScenes().get(0));
+
+ }
+}
diff --git a/engine/src/test/jme3test/renderer/TestParallelProjection.java b/engine/src/test/jme3test/renderer/TestParallelProjection.java
new file mode 100644
index 0000000..2b90027
--- /dev/null
+++ b/engine/src/test/jme3test/renderer/TestParallelProjection.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.renderer;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+
+public class TestParallelProjection extends SimpleApplication implements AnalogListener {
+
+ private float frustumSize = 1;
+
+ public static void main(String[] args){
+ TestParallelProjection app = new TestParallelProjection();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Geometry teaGeom = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White);
+ dl.setDirection(Vector3f.UNIT_XYZ.negate());
+
+ rootNode.addLight(dl);
+ rootNode.attachChild(teaGeom);
+
+ // Setup first view
+ cam.setParallelProjection(true);
+ float aspect = (float) cam.getWidth() / cam.getHeight();
+ cam.setFrustum(-1000, 1000, -aspect * frustumSize, aspect * frustumSize, frustumSize, -frustumSize);
+
+ inputManager.addListener(this, "Size+", "Size-");
+ inputManager.addMapping("Size+", new KeyTrigger(KeyInput.KEY_W));
+ inputManager.addMapping("Size-", new KeyTrigger(KeyInput.KEY_S));
+ }
+
+ public void onAnalog(String name, float value, float tpf) {
+ // Instead of moving closer/farther to object, we zoom in/out.
+ if (name.equals("Size-"))
+ frustumSize += 0.3f * tpf;
+ else
+ frustumSize -= 0.3f * tpf;
+
+ float aspect = (float) cam.getWidth() / cam.getHeight();
+ cam.setFrustum(-1000, 1000, -aspect * frustumSize, aspect * frustumSize, frustumSize, -frustumSize);
+ }
+}
diff --git a/engine/src/test/jme3test/scene/TestSceneLoading.java b/engine/src/test/jme3test/scene/TestSceneLoading.java
new file mode 100644
index 0000000..fddef16
--- /dev/null
+++ b/engine/src/test/jme3test/scene/TestSceneLoading.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.scene;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.SkyFactory;
+import java.io.File;
+
+public class TestSceneLoading extends SimpleApplication {
+
+ private Sphere sphereMesh = new Sphere(32, 32, 10, false, true);
+ private Geometry sphere = new Geometry("Sky", sphereMesh);
+ private static boolean useHttp = false;
+
+ public static void main(String[] args) {
+
+ TestSceneLoading app = new TestSceneLoading();
+ app.start();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ sphere.setLocalTranslation(cam.getLocation());
+ }
+
+ public void simpleInitApp() {
+ this.flyCam.setMoveSpeed(10);
+
+ // load sky
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+ File file = new File("wildhouse.zip");
+ if (!file.exists()) {
+ useHttp = true;
+ }
+ // create the geometry and attach it
+ // load the level from zip or http zip
+ if (useHttp) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", HttpZipLocator.class.getName());
+ } else {
+ assetManager.registerLocator("wildhouse.zip", ZipLocator.class.getName());
+ }
+ Spatial scene = assetManager.loadModel("main.scene");
+
+ AmbientLight al = new AmbientLight();
+ scene.addLight(al);
+
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(0.69077975f, -0.6277887f, -0.35875428f).normalizeLocal());
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ scene.addLight(sun);
+
+ rootNode.attachChild(scene);
+ }
+}
diff --git a/engine/src/test/jme3test/scene/TestUserData.java b/engine/src/test/jme3test/scene/TestUserData.java
new file mode 100644
index 0000000..10bafd6
--- /dev/null
+++ b/engine/src/test/jme3test/scene/TestUserData.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.scene;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+public class TestUserData extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestUserData app = new TestUserData();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Node scene = (Node) assetManager.loadModel("Scenes/DotScene/DotScene.scene");
+ System.out.println("Scene: " + scene);
+
+ Spatial testNode = scene.getChild("TestNode");
+ System.out.println("TestNode: "+ testNode);
+
+ for (String key : testNode.getUserDataKeys()){
+ System.out.println("Property " + key + " = " + testNode.getUserData(key));
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/stress/TestBatchLod.java b/engine/src/test/jme3test/stress/TestBatchLod.java
new file mode 100644
index 0000000..f5d2660
--- /dev/null
+++ b/engine/src/test/jme3test/stress/TestBatchLod.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.stress;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.LodControl;
+import jme3tools.optimize.GeometryBatchFactory;
+
+public class TestBatchLod extends SimpleApplication {
+
+ private boolean lod = false;
+
+ public static void main(String[] args) {
+ TestBatchLod app = new TestBatchLod();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+// inputManager.registerKeyBinding("USELOD", KeyInput.KEY_L);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ rootNode.addLight(dl);
+
+ Node teapotNode = (Node) assetManager.loadModel("Models/Teapot/Teapot.mesh.xml");
+ Geometry teapot = (Geometry) teapotNode.getChild(0);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 16f);
+ mat.setBoolean("VertexLighting", true);
+ teapot.setMaterial(mat);
+
+ // show normals as material
+ //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+ flyCam.setMoveSpeed(5);
+ for (int y = -5; y < 5; y++) {
+ for (int x = -5; x < 5; x++) {
+ Geometry clonePot = teapot.clone();
+
+ //clonePot.setMaterial(mat);
+ clonePot.setLocalTranslation(x * .5f, 0, y * .5f);
+ clonePot.setLocalScale(.15f);
+ clonePot.setMaterial(mat);
+ rootNode.attachChild(clonePot);
+ }
+ }
+ GeometryBatchFactory.optimize(rootNode, true);
+ LodControl control = new LodControl();
+ rootNode.getChild(0).addControl(control);
+ cam.setLocation(new Vector3f(-1.0748308f, 1.35778f, -1.5380064f));
+ cam.setRotation(new Quaternion(0.18343268f, 0.34531063f, -0.069015436f, 0.9177962f));
+
+ }
+}
diff --git a/engine/src/test/jme3test/stress/TestLeakingGL.java b/engine/src/test/jme3test/stress/TestLeakingGL.java
new file mode 100644
index 0000000..dba4863
--- /dev/null
+++ b/engine/src/test/jme3test/stress/TestLeakingGL.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.stress;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.NativeObjectManager;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Generates 400 new meshes every frame then leaks them.
+ * Notice how memory usage stays constant and OpenGL objects
+ * are properly destroyed.
+ */
+public class TestLeakingGL extends SimpleApplication {
+
+ private Material solidColor;
+ private Sphere original;
+
+ public static void main(String[] args){
+ TestLeakingGL app = new TestLeakingGL();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ original = new Sphere(4, 4, 1);
+ original.setStatic();
+ original.setInterleaved();
+
+ // this will make sure all spheres are rendered always
+ rootNode.setCullHint(CullHint.Never);
+ solidColor = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+ cam.setLocation(new Vector3f(0, 5, 0));
+ cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+
+ Logger.getLogger(Node.class.getName()).setLevel(Level.WARNING);
+ Logger.getLogger(NativeObjectManager.class.getName()).setLevel(Level.WARNING);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ rootNode.detachAllChildren();
+ for (int y = -15; y < 15; y++){
+ for (int x = -15; x < 15; x++){
+ Mesh sphMesh = original.deepClone();
+ Geometry sphere = new Geometry("sphere", sphMesh);
+
+ sphere.setMaterial(solidColor);
+ sphere.setLocalTranslation(x * 1.5f, 0, y * 1.5f);
+ rootNode.attachChild(sphere);
+ }
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/stress/TestLodStress.java b/engine/src/test/jme3test/stress/TestLodStress.java
new file mode 100644
index 0000000..84a3376
--- /dev/null
+++ b/engine/src/test/jme3test/stress/TestLodStress.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.stress;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.LodControl;
+
+public class TestLodStress extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestLodStress app = new TestLodStress();
+ app.setShowSettings(false);
+ app.setPauseOnLostFocus(false);
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1,-1,-1).normalizeLocal());
+ rootNode.addLight(dl);
+
+ Node teapotNode = (Node) assetManager.loadModel("Models/Teapot/Teapot.mesh.xml");
+ Geometry teapot = (Geometry) teapotNode.getChild(0);
+
+// Sphere sph = new Sphere(16, 16, 4);
+// Geometry teapot = new Geometry("teapot", sph);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setFloat("Shininess", 16f);
+ mat.setBoolean("VertexLighting", true);
+ teapot.setMaterial(mat);
+
+ // show normals as material
+ //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+
+ for (int y = -10; y < 10; y++){
+ for (int x = -10; x < 10; x++){
+ Geometry clonePot = teapot.clone();
+
+ //clonePot.setMaterial(mat);
+ clonePot.setLocalTranslation(x * .5f, 0, y * .5f);
+ clonePot.setLocalScale(.15f);
+
+ LodControl control = new LodControl();
+ clonePot.addControl(control);
+ rootNode.attachChild(clonePot);
+ }
+ }
+
+ cam.setLocation(new Vector3f(8.378951f, 5.4324f, 8.795956f));
+ cam.setRotation(new Quaternion(-0.083419204f, 0.90370524f, -0.20599906f, -0.36595422f));
+ }
+
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainFractalGridTest.java b/engine/src/test/jme3test/terrain/TerrainFractalGridTest.java
new file mode 100644
index 0000000..499a8d6
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainFractalGridTest.java
@@ -0,0 +1,147 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.FilteredBasis;
+import com.jme3.terrain.noise.filter.IterativeFilter;
+import com.jme3.terrain.noise.filter.OptimizedErode;
+import com.jme3.terrain.noise.filter.PerturbFilter;
+import com.jme3.terrain.noise.filter.SmoothFilter;
+import com.jme3.terrain.noise.fractal.FractalSum;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+public class TerrainFractalGridTest extends SimpleApplication {
+
+ private Material mat_terrain;
+ private TerrainGrid terrain;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+
+ public static void main(final String[] args) {
+ TerrainFractalGridTest app = new TerrainFractalGridTest();
+ app.start();
+ }
+ private CharacterControl player3;
+ private FractalSum base;
+ private PerturbFilter perturb;
+ private OptimizedErode therm;
+ private SmoothFilter smooth;
+ private IterativeFilter iterate;
+
+ @Override
+ public void simpleInitApp() {
+ this.flyCam.setMoveSpeed(100f);
+ ScreenshotAppState state = new ScreenshotAppState();
+ this.stateManager.attach(state);
+
+ // TERRAIN TEXTURE material
+ this.mat_terrain = new Material(this.assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
+
+ // Parameters to material:
+ // regionXColorMap: X = 1..4 the texture that should be appliad to state X
+ // regionX: a Vector3f containing the following information:
+ // regionX.x: the start height of the region
+ // regionX.y: the end height of the region
+ // regionX.z: the texture scale for the region
+ // it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
+ // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
+ // slopeTileFactor: the texture scale for slopes
+ // terrainSize: the total size of the terrain (used for scaling the texture)
+ // GRASS texture
+ Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region1ColorMap", grass);
+ this.mat_terrain.setVector3("region1", new Vector3f(15, 200, this.grassScale));
+
+ // DIRT texture
+ Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region2ColorMap", dirt);
+ this.mat_terrain.setVector3("region2", new Vector3f(0, 20, this.dirtScale));
+
+ // ROCK texture
+ Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region3ColorMap", rock);
+ this.mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));
+
+ this.mat_terrain.setTexture("region4ColorMap", rock);
+ this.mat_terrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));
+
+ this.mat_terrain.setTexture("slopeColorMap", rock);
+ this.mat_terrain.setFloat("slopeTileFactor", 32);
+
+ this.mat_terrain.setFloat("terrainSize", 513);
+
+ this.base = new FractalSum();
+ this.base.setRoughness(0.7f);
+ this.base.setFrequency(1.0f);
+ this.base.setAmplitude(1.0f);
+ this.base.setLacunarity(2.12f);
+ this.base.setOctaves(8);
+ this.base.setScale(0.02125f);
+ this.base.addModulator(new NoiseModulator() {
+
+ @Override
+ public float value(float... in) {
+ return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
+ }
+ });
+
+ FilteredBasis ground = new FilteredBasis(this.base);
+
+ this.perturb = new PerturbFilter();
+ this.perturb.setMagnitude(0.119f);
+
+ this.therm = new OptimizedErode();
+ this.therm.setRadius(5);
+ this.therm.setTalus(0.011f);
+
+ this.smooth = new SmoothFilter();
+ this.smooth.setRadius(1);
+ this.smooth.setEffect(0.7f);
+
+ this.iterate = new IterativeFilter();
+ this.iterate.addPreFilter(this.perturb);
+ this.iterate.addPostFilter(this.smooth);
+ this.iterate.setFilter(this.therm);
+ this.iterate.setIterations(1);
+
+ ground.addPreFilter(this.iterate);
+
+ this.terrain = new TerrainGrid("terrain", 33, 129, new FractalTileLoader(ground, 256f));
+
+ this.terrain.setMaterial(this.mat_terrain);
+ this.terrain.setLocalTranslation(0, 0, 0);
+ this.terrain.setLocalScale(2f, 1f, 2f);
+ this.rootNode.attachChild(this.terrain);
+
+ TerrainLodControl control = new TerrainLodControl(this.terrain, this.getCamera());
+ control.setLodCalculator(new DistanceLodCalculator(33, 2.7f)); // patch size, and a multiplier
+ this.terrain.addControl(control);
+
+
+
+ this.getCamera().setLocation(new Vector3f(0, 300, 0));
+
+ this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+
+ }
+
+ @Override
+ public void simpleUpdate(final float tpf) {
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java b/engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java
new file mode 100644
index 0000000..6c45e72
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java
@@ -0,0 +1,359 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.FilteredBasis;
+import com.jme3.terrain.noise.filter.IterativeFilter;
+import com.jme3.terrain.noise.filter.OptimizedErode;
+import com.jme3.terrain.noise.filter.PerturbFilter;
+import com.jme3.terrain.noise.filter.SmoothFilter;
+import com.jme3.terrain.noise.fractal.FractalSum;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
+
+public class TerrainGridAlphaMapTest extends SimpleApplication {
+
+ private TerrainGrid terrain;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+ private boolean usePhysics = false;
+
+ public static void main(final String[] args) {
+ TerrainGridAlphaMapTest app = new TerrainGridAlphaMapTest();
+ app.start();
+ }
+ private CharacterControl player3;
+ private FractalSum base;
+ private PerturbFilter perturb;
+ private OptimizedErode therm;
+ private SmoothFilter smooth;
+ private IterativeFilter iterate;
+ private Material material;
+ private Material matWire;
+
+ @Override
+ public void simpleInitApp() {
+ DirectionalLight sun = new DirectionalLight();
+ sun.setColor(ColorRGBA.White);
+ sun.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ rootNode.addLight(sun);
+
+ AmbientLight al = new AmbientLight();
+ al.setColor(ColorRGBA.White.mult(1.3f));
+ rootNode.addLight(al);
+
+ File file = new File("TerrainGridTestData.zip");
+ if (!file.exists()) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
+ } else {
+ assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
+ }
+
+ this.flyCam.setMoveSpeed(100f);
+ ScreenshotAppState state = new ScreenshotAppState();
+ this.stateManager.attach(state);
+
+ // TERRAIN TEXTURE material
+ material = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ material.setBoolean("useTriPlanarMapping", false);
+ //material.setBoolean("isTerrainGrid", true);
+ material.setFloat("Shininess", 0.0f);
+
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ material.setTexture("DiffuseMap", grass);
+ material.setFloat("DiffuseMap_0_scale", grassScale);
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ material.setTexture("DiffuseMap_1", dirt);
+ material.setFloat("DiffuseMap_1_scale", dirtScale);
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ material.setTexture("DiffuseMap_2", rock);
+ material.setFloat("DiffuseMap_2_scale", rockScale);
+
+ // WIREFRAME material
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+
+ this.base = new FractalSum();
+ this.base.setRoughness(0.7f);
+ this.base.setFrequency(1.0f);
+ this.base.setAmplitude(1.0f);
+ this.base.setLacunarity(2.12f);
+ this.base.setOctaves(8);
+ this.base.setScale(0.02125f);
+ this.base.addModulator(new NoiseModulator() {
+
+ @Override
+ public float value(float... in) {
+ return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
+ }
+ });
+
+ FilteredBasis ground = new FilteredBasis(this.base);
+
+ this.perturb = new PerturbFilter();
+ this.perturb.setMagnitude(0.119f);
+
+ this.therm = new OptimizedErode();
+ this.therm.setRadius(5);
+ this.therm.setTalus(0.011f);
+
+ this.smooth = new SmoothFilter();
+ this.smooth.setRadius(1);
+ this.smooth.setEffect(0.7f);
+
+ this.iterate = new IterativeFilter();
+ this.iterate.addPreFilter(this.perturb);
+ this.iterate.addPostFilter(this.smooth);
+ this.iterate.setFilter(this.therm);
+ this.iterate.setIterations(1);
+
+ ground.addPreFilter(this.iterate);
+
+ this.terrain = new TerrainGrid("terrain", 33, 257, new FractalTileLoader(ground, 256));
+ this.terrain.setMaterial(this.material);
+
+ this.terrain.setLocalTranslation(0, 0, 0);
+ this.terrain.setLocalScale(2f, 1f, 2f);
+ this.rootNode.attachChild(this.terrain);
+
+ List<Camera> cameras = new ArrayList<Camera>();
+ cameras.add(this.getCamera());
+ TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
+ control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
+ this.terrain.addControl(control);
+
+ final BulletAppState bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+
+
+ this.getCamera().setLocation(new Vector3f(0, 256, 0));
+
+ this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+ if (usePhysics) {
+ CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
+ player3 = new CharacterControl(capsuleShape, 0.5f);
+ player3.setJumpSpeed(20);
+ player3.setFallSpeed(10);
+ player3.setGravity(10);
+
+ player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
+
+ bulletAppState.getPhysicsSpace().add(player3);
+
+ }
+ terrain.addListener(new TerrainGridListener() {
+
+ public void gridMoved(Vector3f newCenter) {
+ }
+
+ public void tileAttached(Vector3f cell, TerrainQuad quad) {
+ Texture alpha = null;
+ try {
+ alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_" + (int)cell.x+ "_" + (int)cell.z + ".png");
+ } catch (Exception e) {
+ alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_default.png");
+ }
+ quad.getMaterial().setTexture("AlphaMap", alpha);
+ if (usePhysics) {
+ quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
+ bulletAppState.getPhysicsSpace().add(quad);
+ }
+ updateMarkerElevations();
+ }
+
+ public void tileDetached(Vector3f cell, TerrainQuad quad) {
+ if (usePhysics) {
+ bulletAppState.getPhysicsSpace().remove(quad);
+ quad.removeControl(RigidBodyControl.class);
+ }
+ updateMarkerElevations();
+ }
+ });
+
+ this.initKeys();
+
+ markers = new Node();
+ rootNode.attachChild(markers);
+ createMarkerPoints(1);
+ }
+
+ Node markers;
+
+
+ private void createMarkerPoints(float count) {
+ Node center = createAxisMarker(10);
+ markers.attachChild(center);
+
+ float xS = (count-1)*terrain.getTerrainSize() - (terrain.getTerrainSize()/2);
+ float zS = (count-1)*terrain.getTerrainSize() - (terrain.getTerrainSize()/2);
+ float xSi = xS;
+ float zSi = zS;
+ for (int x=0; x<count*2; x++) {
+ for (int z=0; z<count*2; z++) {
+ Node m = createAxisMarker(5);
+ m.setLocalTranslation(xSi, 0, zSi);
+ markers.attachChild(m);
+ zSi += terrain.getTerrainSize();
+ }
+ zSi = zS;
+ xSi += terrain.getTerrainSize();
+ }
+ }
+
+ private void updateMarkerElevations() {
+ for (Spatial s : markers.getChildren()) {
+ float h = terrain.getHeight(new Vector2f(s.getLocalTranslation().x, s.getLocalTranslation().z));
+ s.setLocalTranslation(s.getLocalTranslation().x, h+1, s.getLocalTranslation().z);
+ }
+ }
+
+ private void initKeys() {
+ // You can map one or several inputs to one named action
+ this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+ this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+ this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+ this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+ this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
+ this.inputManager.addListener(this.actionListener, "Lefts");
+ this.inputManager.addListener(this.actionListener, "Rights");
+ this.inputManager.addListener(this.actionListener, "Ups");
+ this.inputManager.addListener(this.actionListener, "Downs");
+ this.inputManager.addListener(this.actionListener, "Jumps");
+ }
+ private boolean left;
+ private boolean right;
+ private boolean up;
+ private boolean down;
+ private final ActionListener actionListener = new ActionListener() {
+
+ @Override
+ public void onAction(final String name, final boolean keyPressed, final float tpf) {
+ if (name.equals("Lefts")) {
+ if (keyPressed) {
+ TerrainGridAlphaMapTest.this.left = true;
+ } else {
+ TerrainGridAlphaMapTest.this.left = false;
+ }
+ } else if (name.equals("Rights")) {
+ if (keyPressed) {
+ TerrainGridAlphaMapTest.this.right = true;
+ } else {
+ TerrainGridAlphaMapTest.this.right = false;
+ }
+ } else if (name.equals("Ups")) {
+ if (keyPressed) {
+ TerrainGridAlphaMapTest.this.up = true;
+ } else {
+ TerrainGridAlphaMapTest.this.up = false;
+ }
+ } else if (name.equals("Downs")) {
+ if (keyPressed) {
+ TerrainGridAlphaMapTest.this.down = true;
+ } else {
+ TerrainGridAlphaMapTest.this.down = false;
+ }
+ } else if (name.equals("Jumps")) {
+ TerrainGridAlphaMapTest.this.player3.jump();
+ }
+ }
+ };
+ private final Vector3f walkDirection = new Vector3f();
+
+ @Override
+ public void simpleUpdate(final float tpf) {
+ Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
+ this.walkDirection.set(0, 0, 0);
+ if (this.left) {
+ this.walkDirection.addLocal(camLeft);
+ }
+ if (this.right) {
+ this.walkDirection.addLocal(camLeft.negate());
+ }
+ if (this.up) {
+ this.walkDirection.addLocal(camDir);
+ }
+ if (this.down) {
+ this.walkDirection.addLocal(camDir.negate());
+ }
+
+ if (usePhysics) {
+ this.player3.setWalkDirection(this.walkDirection);
+ this.cam.setLocation(this.player3.getPhysicsLocation());
+ }
+ }
+
+ protected Node createAxisMarker(float arrowSize) {
+
+ Material redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ redMat.getAdditionalRenderState().setWireframe(true);
+ redMat.setColor("Color", ColorRGBA.Red);
+
+ Material greenMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ greenMat.getAdditionalRenderState().setWireframe(true);
+ greenMat.setColor("Color", ColorRGBA.Green);
+
+ Material blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ blueMat.getAdditionalRenderState().setWireframe(true);
+ blueMat.setColor("Color", ColorRGBA.Blue);
+
+ Node axis = new Node();
+
+ // create arrows
+ Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0)));
+ arrowX.setMaterial(redMat);
+ Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0)));
+ arrowY.setMaterial(greenMat);
+ Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize)));
+ arrowZ.setMaterial(blueMat);
+ axis.attachChild(arrowX);
+ axis.attachChild(arrowY);
+ axis.attachChild(arrowZ);
+
+ //axis.setModelBound(new BoundingBox());
+ return axis;
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java b/engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java
new file mode 100644
index 0000000..ec07e7a
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java
@@ -0,0 +1,179 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import java.io.File;
+
+public class TerrainGridSerializationTest extends SimpleApplication {
+
+ private TerrainGrid terrain;
+ private boolean usePhysics = true;
+
+ public static void main(final String[] args) {
+ TerrainGridSerializationTest app = new TerrainGridSerializationTest();
+ app.start();
+ }
+ private CharacterControl player3;
+
+ @Override
+ public void simpleInitApp() {
+ File file = new File("TerrainGridTestData.zip");
+ if (!file.exists()) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
+ } else {
+ assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
+ }
+
+ this.flyCam.setMoveSpeed(100f);
+ ScreenshotAppState state = new ScreenshotAppState();
+ this.stateManager.attach(state);
+
+ this.terrain= (TerrainGrid) assetManager.loadModel("TerrainGrid/TerrainGrid.j3o");
+
+ this.rootNode.attachChild(this.terrain);
+
+ TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ this.terrain.addControl(control);
+
+ final BulletAppState bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+
+ this.getCamera().setLocation(new Vector3f(0, 256, 0));
+
+ this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+ if (usePhysics) {
+ CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
+ player3 = new CharacterControl(capsuleShape, 0.5f);
+ player3.setJumpSpeed(20);
+ player3.setFallSpeed(10);
+ player3.setGravity(10);
+
+ player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
+
+ bulletAppState.getPhysicsSpace().add(player3);
+
+ terrain.addListener(new TerrainGridListener() {
+
+ public void gridMoved(Vector3f newCenter) {
+ }
+
+ public Material tileLoaded(Material material, Vector3f cell) {
+ return material;
+ }
+
+ public void tileAttached(Vector3f cell, TerrainQuad quad) {
+ //workaround for bugged test j3o's
+ while(quad.getControl(RigidBodyControl.class)!=null){
+ quad.removeControl(RigidBodyControl.class);
+ }
+ quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
+ bulletAppState.getPhysicsSpace().add(quad);
+ }
+
+ public void tileDetached(Vector3f cell, TerrainQuad quad) {
+ bulletAppState.getPhysicsSpace().remove(quad);
+ quad.removeControl(RigidBodyControl.class);
+ }
+
+ });
+ }
+
+ this.initKeys();
+ }
+
+ private void initKeys() {
+ // You can map one or several inputs to one named action
+ this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+ this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+ this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+ this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+ this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
+ this.inputManager.addListener(this.actionListener, "Lefts");
+ this.inputManager.addListener(this.actionListener, "Rights");
+ this.inputManager.addListener(this.actionListener, "Ups");
+ this.inputManager.addListener(this.actionListener, "Downs");
+ this.inputManager.addListener(this.actionListener, "Jumps");
+ }
+ private boolean left;
+ private boolean right;
+ private boolean up;
+ private boolean down;
+ private final ActionListener actionListener = new ActionListener() {
+
+ @Override
+ public void onAction(final String name, final boolean keyPressed, final float tpf) {
+ if (name.equals("Lefts")) {
+ if (keyPressed) {
+ TerrainGridSerializationTest.this.left = true;
+ } else {
+ TerrainGridSerializationTest.this.left = false;
+ }
+ } else if (name.equals("Rights")) {
+ if (keyPressed) {
+ TerrainGridSerializationTest.this.right = true;
+ } else {
+ TerrainGridSerializationTest.this.right = false;
+ }
+ } else if (name.equals("Ups")) {
+ if (keyPressed) {
+ TerrainGridSerializationTest.this.up = true;
+ } else {
+ TerrainGridSerializationTest.this.up = false;
+ }
+ } else if (name.equals("Downs")) {
+ if (keyPressed) {
+ TerrainGridSerializationTest.this.down = true;
+ } else {
+ TerrainGridSerializationTest.this.down = false;
+ }
+ } else if (name.equals("Jumps")) {
+ TerrainGridSerializationTest.this.player3.jump();
+ }
+ }
+ };
+ private final Vector3f walkDirection = new Vector3f();
+
+ @Override
+ public void simpleUpdate(final float tpf) {
+ Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
+ this.walkDirection.set(0, 0, 0);
+ if (this.left) {
+ this.walkDirection.addLocal(camLeft);
+ }
+ if (this.right) {
+ this.walkDirection.addLocal(camLeft.negate());
+ }
+ if (this.up) {
+ this.walkDirection.addLocal(camDir);
+ }
+ if (this.down) {
+ this.walkDirection.addLocal(camDir.negate());
+ }
+
+ if (usePhysics) {
+ this.player3.setWalkDirection(this.walkDirection);
+ this.cam.setLocation(this.player3.getPhysicsLocation());
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainGridTest.java b/engine/src/test/jme3test/terrain/TerrainGridTest.java
new file mode 100644
index 0000000..85f8ee4
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainGridTest.java
@@ -0,0 +1,239 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.ImageTileLoader;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.Namer;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.File;
+
+public class TerrainGridTest extends SimpleApplication {
+
+ private Material mat_terrain;
+ private TerrainGrid terrain;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+ private boolean usePhysics = false;
+ private boolean physicsAdded = false;
+
+ public static void main(final String[] args) {
+ TerrainGridTest app = new TerrainGridTest();
+ app.start();
+ }
+ private CharacterControl player3;
+
+ @Override
+ public void simpleInitApp() {
+ File file = new File("TerrainGridTestData.zip");
+ if (!file.exists()) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
+ } else {
+ assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
+ }
+
+ this.flyCam.setMoveSpeed(100f);
+ ScreenshotAppState state = new ScreenshotAppState();
+ this.stateManager.attach(state);
+
+ // TERRAIN TEXTURE material
+ this.mat_terrain = new Material(this.assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
+
+ // Parameters to material:
+ // regionXColorMap: X = 1..4 the texture that should be appliad to state X
+ // regionX: a Vector3f containing the following information:
+ // regionX.x: the start height of the region
+ // regionX.y: the end height of the region
+ // regionX.z: the texture scale for the region
+ // it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
+ // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
+ // slopeTileFactor: the texture scale for slopes
+ // terrainSize: the total size of the terrain (used for scaling the texture)
+ // GRASS texture
+ Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region1ColorMap", grass);
+ this.mat_terrain.setVector3("region1", new Vector3f(88, 200, this.grassScale));
+
+ // DIRT texture
+ Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region2ColorMap", dirt);
+ this.mat_terrain.setVector3("region2", new Vector3f(0, 90, this.dirtScale));
+
+ // ROCK texture
+ Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region3ColorMap", rock);
+ this.mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));
+
+ this.mat_terrain.setTexture("region4ColorMap", rock);
+ this.mat_terrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));
+
+ this.mat_terrain.setTexture("slopeColorMap", rock);
+ this.mat_terrain.setFloat("slopeTileFactor", 32);
+
+ this.mat_terrain.setFloat("terrainSize", 129);
+
+ this.terrain = new TerrainGrid("terrain", 65, 257, new ImageTileLoader(assetManager, new Namer() {
+
+ public String getName(int x, int y) {
+ return "Scenes/TerrainMountains/terrain_" + x + "_" + y + ".png";
+ }
+ }));
+
+ this.terrain.setMaterial(mat_terrain);
+ this.terrain.setLocalTranslation(0, 0, 0);
+ this.terrain.setLocalScale(1f, 1f, 1f);
+ this.rootNode.attachChild(this.terrain);
+
+ TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ this.terrain.addControl(control);
+
+ final BulletAppState bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+
+ this.getCamera().setLocation(new Vector3f(0, 200, 0));
+
+ this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+ rootNode.addLight(light);
+
+ if (usePhysics) {
+ CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
+ player3 = new CharacterControl(capsuleShape, 0.5f);
+ player3.setJumpSpeed(20);
+ player3.setFallSpeed(10);
+ player3.setGravity(10);
+
+ player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
+
+ bulletAppState.getPhysicsSpace().add(player3);
+
+ terrain.addListener(new TerrainGridListener() {
+
+ public void gridMoved(Vector3f newCenter) {
+ }
+
+ public Material tileLoaded(Material material, Vector3f cell) {
+ return material;
+ }
+
+ public void tileAttached(Vector3f cell, TerrainQuad quad) {
+ while(quad.getControl(RigidBodyControl.class)!=null){
+ quad.removeControl(RigidBodyControl.class);
+ }
+ quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
+ bulletAppState.getPhysicsSpace().add(quad);
+ }
+
+ public void tileDetached(Vector3f cell, TerrainQuad quad) {
+ bulletAppState.getPhysicsSpace().remove(quad);
+ quad.removeControl(RigidBodyControl.class);
+ }
+
+ });
+ }
+
+ this.initKeys();
+ }
+
+ private void initKeys() {
+ // You can map one or several inputs to one named action
+ this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+ this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+ this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+ this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+ this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
+ this.inputManager.addListener(this.actionListener, "Lefts");
+ this.inputManager.addListener(this.actionListener, "Rights");
+ this.inputManager.addListener(this.actionListener, "Ups");
+ this.inputManager.addListener(this.actionListener, "Downs");
+ this.inputManager.addListener(this.actionListener, "Jumps");
+ }
+ private boolean left;
+ private boolean right;
+ private boolean up;
+ private boolean down;
+ private final ActionListener actionListener = new ActionListener() {
+
+ @Override
+ public void onAction(final String name, final boolean keyPressed, final float tpf) {
+ if (name.equals("Lefts")) {
+ if (keyPressed) {
+ TerrainGridTest.this.left = true;
+ } else {
+ TerrainGridTest.this.left = false;
+ }
+ } else if (name.equals("Rights")) {
+ if (keyPressed) {
+ TerrainGridTest.this.right = true;
+ } else {
+ TerrainGridTest.this.right = false;
+ }
+ } else if (name.equals("Ups")) {
+ if (keyPressed) {
+ TerrainGridTest.this.up = true;
+ } else {
+ TerrainGridTest.this.up = false;
+ }
+ } else if (name.equals("Downs")) {
+ if (keyPressed) {
+ TerrainGridTest.this.down = true;
+ } else {
+ TerrainGridTest.this.down = false;
+ }
+ } else if (name.equals("Jumps")) {
+ TerrainGridTest.this.player3.jump();
+ }
+ }
+ };
+ private final Vector3f walkDirection = new Vector3f();
+
+ @Override
+ public void simpleUpdate(final float tpf) {
+ Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
+ this.walkDirection.set(0, 0, 0);
+ if (this.left) {
+ this.walkDirection.addLocal(camLeft);
+ }
+ if (this.right) {
+ this.walkDirection.addLocal(camLeft.negate());
+ }
+ if (this.up) {
+ this.walkDirection.addLocal(camDir);
+ }
+ if (this.down) {
+ this.walkDirection.addLocal(camDir.negate());
+ }
+
+ if (usePhysics) {
+ this.player3.setWalkDirection(this.walkDirection);
+ this.cam.setLocation(this.player3.getPhysicsLocation());
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java b/engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java
new file mode 100644
index 0000000..86fbbf9
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java
@@ -0,0 +1,236 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.AssetTileLoader;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.File;
+
+public class TerrainGridTileLoaderTest extends SimpleApplication {
+
+ private Material mat_terrain;
+ private TerrainGrid terrain;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+ private boolean usePhysics = true;
+ private boolean physicsAdded = false;
+
+ public static void main(final String[] args) {
+ TerrainGridTileLoaderTest app = new TerrainGridTileLoaderTest();
+ app.start();
+ }
+ private CharacterControl player3;
+
+ @Override
+ public void simpleInitApp() {
+ File file = new File("TerrainGridTestData.zip");
+ if (!file.exists()) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
+ } else {
+ assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
+ }
+
+ this.flyCam.setMoveSpeed(100f);
+ ScreenshotAppState state = new ScreenshotAppState();
+ this.stateManager.attach(state);
+
+ // TERRAIN TEXTURE material
+ this.mat_terrain = new Material(this.assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
+
+ // Parameters to material:
+ // regionXColorMap: X = 1..4 the texture that should be appliad to state X
+ // regionX: a Vector3f containing the following information:
+ // regionX.x: the start height of the region
+ // regionX.y: the end height of the region
+ // regionX.z: the texture scale for the region
+ // it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
+ // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
+ // slopeTileFactor: the texture scale for slopes
+ // terrainSize: the total size of the terrain (used for scaling the texture)
+ // GRASS texture
+ Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region1ColorMap", grass);
+ this.mat_terrain.setVector3("region1", new Vector3f(88, 200, this.grassScale));
+
+ // DIRT texture
+ Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region2ColorMap", dirt);
+ this.mat_terrain.setVector3("region2", new Vector3f(0, 90, this.dirtScale));
+
+ // ROCK texture
+ Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ this.mat_terrain.setTexture("region3ColorMap", rock);
+ this.mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));
+
+ this.mat_terrain.setTexture("region4ColorMap", rock);
+ this.mat_terrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));
+
+ this.mat_terrain.setTexture("slopeColorMap", rock);
+ this.mat_terrain.setFloat("slopeTileFactor", 32);
+
+ this.mat_terrain.setFloat("terrainSize", 129);
+//quad.getHeightMap(), terrain.getLocalScale()), 0
+ AssetTileLoader grid = new AssetTileLoader(assetManager, "testgrid", "TerrainGrid");
+ this.terrain = new TerrainGrid("terrain", 65, 257, grid);
+
+ this.terrain.setMaterial(this.mat_terrain);
+ this.terrain.setLocalTranslation(0, 0, 0);
+ this.terrain.setLocalScale(2f, 1f, 2f);
+// try {
+// BinaryExporter.getInstance().save(terrain, new File("/Users/normenhansen/Documents/Code/jme3/engine/src/test-data/TerrainGrid/"
+// + "TerrainGrid.j3o"));
+// } catch (IOException ex) {
+// Logger.getLogger(TerrainFractalGridTest.class.getName()).log(Level.SEVERE, null, ex);
+// }
+
+ this.rootNode.attachChild(this.terrain);
+
+ TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ this.terrain.addControl(control);
+
+ final BulletAppState bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+
+ this.getCamera().setLocation(new Vector3f(0, 256, 0));
+
+ this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+ if (usePhysics) {
+ CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
+ player3 = new CharacterControl(capsuleShape, 0.5f);
+ player3.setJumpSpeed(20);
+ player3.setFallSpeed(10);
+ player3.setGravity(10);
+
+ player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
+
+ bulletAppState.getPhysicsSpace().add(player3);
+
+ terrain.addListener(new TerrainGridListener() {
+
+ public void gridMoved(Vector3f newCenter) {
+ }
+
+ public Material tileLoaded(Material material, Vector3f cell) {
+ return material;
+ }
+
+ public void tileAttached(Vector3f cell, TerrainQuad quad) {
+ while(quad.getControl(RigidBodyControl.class)!=null){
+ quad.removeControl(RigidBodyControl.class);
+ }
+ quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
+ bulletAppState.getPhysicsSpace().add(quad);
+ }
+
+ public void tileDetached(Vector3f cell, TerrainQuad quad) {
+ bulletAppState.getPhysicsSpace().remove(quad);
+ quad.removeControl(RigidBodyControl.class);
+ }
+
+ });
+ }
+
+ this.initKeys();
+ }
+
+ private void initKeys() {
+ // You can map one or several inputs to one named action
+ this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+ this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+ this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+ this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+ this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
+ this.inputManager.addListener(this.actionListener, "Lefts");
+ this.inputManager.addListener(this.actionListener, "Rights");
+ this.inputManager.addListener(this.actionListener, "Ups");
+ this.inputManager.addListener(this.actionListener, "Downs");
+ this.inputManager.addListener(this.actionListener, "Jumps");
+ }
+ private boolean left;
+ private boolean right;
+ private boolean up;
+ private boolean down;
+ private final ActionListener actionListener = new ActionListener() {
+
+ @Override
+ public void onAction(final String name, final boolean keyPressed, final float tpf) {
+ if (name.equals("Lefts")) {
+ if (keyPressed) {
+ TerrainGridTileLoaderTest.this.left = true;
+ } else {
+ TerrainGridTileLoaderTest.this.left = false;
+ }
+ } else if (name.equals("Rights")) {
+ if (keyPressed) {
+ TerrainGridTileLoaderTest.this.right = true;
+ } else {
+ TerrainGridTileLoaderTest.this.right = false;
+ }
+ } else if (name.equals("Ups")) {
+ if (keyPressed) {
+ TerrainGridTileLoaderTest.this.up = true;
+ } else {
+ TerrainGridTileLoaderTest.this.up = false;
+ }
+ } else if (name.equals("Downs")) {
+ if (keyPressed) {
+ TerrainGridTileLoaderTest.this.down = true;
+ } else {
+ TerrainGridTileLoaderTest.this.down = false;
+ }
+ } else if (name.equals("Jumps")) {
+ TerrainGridTileLoaderTest.this.player3.jump();
+ }
+ }
+ };
+ private final Vector3f walkDirection = new Vector3f();
+
+ @Override
+ public void simpleUpdate(final float tpf) {
+ Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
+ Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
+ this.walkDirection.set(0, 0, 0);
+ if (this.left) {
+ this.walkDirection.addLocal(camLeft);
+ }
+ if (this.right) {
+ this.walkDirection.addLocal(camLeft.negate());
+ }
+ if (this.up) {
+ this.walkDirection.addLocal(camDir);
+ }
+ if (this.down) {
+ this.walkDirection.addLocal(camDir.negate());
+ }
+
+ if (usePhysics) {
+ this.player3.setWalkDirection(this.walkDirection);
+ this.cam.setLocation(this.player3.getPhysicsLocation());
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainTest.java b/engine/src/test/jme3test/terrain/TerrainTest.java
new file mode 100644
index 0000000..4180c47
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.asset.TextureKey;
+
+/**
+ * Demonstrates how to use terrain.
+ * The base terrain class it uses is TerrainQuad, which is a quad tree of actual
+ * meshes called TerainPatches.
+ * There are a couple options for the terrain in this test:
+ * The first is wireframe mode. Here you can see the underlying trianglestrip structure.
+ * You will notice some off lines; these are degenerate triangles and are part of the
+ * trianglestrip. They are only noticeable in wireframe mode.
+ * Second is Tri-Planar texture mode. Here the textures are rendered on all 3 axes and
+ * then blended together to reduce distortion and stretching.
+ * Third, which you have to modify the code to see, is Entropy LOD calculations.
+ * In the constructor for the TerrainQuad, un-comment the final parameter that is
+ * the LodPerspectiveCalculatorFactory. Then you will see the terrain flicker to start
+ * while it calculates the entropies. Once it is done, it will pick the best LOD value
+ * based on entropy. This method reduces "popping" of terrain greatly when LOD levels
+ * change. It is highly suggested you use it in your app.
+ *
+ * @author bowens
+ */
+public class TerrainTest extends SimpleApplication {
+
+ private TerrainQuad terrain;
+ Material matRock;
+ Material matWire;
+ boolean wireframe = false;
+ boolean triPlanar = false;
+ protected BitmapText hintText;
+ PointLight pl;
+ Geometry lightMdl;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+
+ public static void main(String[] args) {
+ TerrainTest app = new TerrainTest();
+ app.start();
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+
+ loadHintText();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ setupKeys();
+
+ // First, we load up our textures and the heightmap texture for the terrain
+
+ // TERRAIN TEXTURE material
+ matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
+ matRock.setBoolean("useTriPlanarMapping", false);
+
+ // ALPHA map (for splat textures)
+ matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+
+ // HEIGHTMAP image (for the terrain heightmap)
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matRock.setTexture("Tex1", grass);
+ matRock.setFloat("Tex1Scale", grassScale);
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matRock.setTexture("Tex2", dirt);
+ matRock.setFloat("Tex2Scale", dirtScale);
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matRock.setTexture("Tex3", rock);
+ matRock.setFloat("Tex3Scale", rockScale);
+
+ // WIREFRAME material
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+
+ // CREATE HEIGHTMAP
+ AbstractHeightMap heightmap = null;
+ try {
+ //heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3);
+
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);
+ heightmap.load();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ /*
+ * Here we create the actual terrain. The tiles will be 65x65, and the total size of the
+ * terrain will be 513x513. It uses the heightmap we created to generate the height values.
+ */
+ /**
+ * Optimal terrain patch size is 65 (64x64).
+ * The total size is up to you. At 1025 it ran fine for me (200+FPS), however at
+ * size=2049, it got really slow. But that is a jump from 2 million to 8 million triangles...
+ */
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ terrain.addControl(control);
+ terrain.setMaterial(matRock);
+ terrain.setLocalTranslation(0, -100, 0);
+ terrain.setLocalScale(2f, 1f, 2f);
+ rootNode.attachChild(terrain);
+
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+ rootNode.addLight(light);
+
+ cam.setLocation(new Vector3f(0, 10, -10));
+ cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y);
+ }
+
+ public void loadHintText() {
+ hintText = new BitmapText(guiFont, false);
+ hintText.setSize(guiFont.getCharSet().getRenderedSize());
+ hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+ hintText.setText("Hit T to switch to wireframe, P to switch to tri-planar texturing");
+ guiNode.attachChild(hintText);
+ }
+
+ private void setupKeys() {
+ flyCam.setMoveSpeed(50);
+ inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(actionListener, "wireframe");
+ inputManager.addMapping("triPlanar", new KeyTrigger(KeyInput.KEY_P));
+ inputManager.addListener(actionListener, "triPlanar");
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean pressed, float tpf) {
+ if (name.equals("wireframe") && !pressed) {
+ wireframe = !wireframe;
+ if (!wireframe) {
+ terrain.setMaterial(matWire);
+ } else {
+ terrain.setMaterial(matRock);
+ }
+ } else if (name.equals("triPlanar") && !pressed) {
+ triPlanar = !triPlanar;
+ if (triPlanar) {
+ matRock.setBoolean("useTriPlanarMapping", true);
+ // planar textures don't use the mesh's texture coordinates but real world coordinates,
+ // so we need to convert these texture coordinate scales into real world scales so it looks
+ // the same when we switch to/from tr-planar mode
+ matRock.setFloat("Tex1Scale", 1f / (float) (512f / grassScale));
+ matRock.setFloat("Tex2Scale", 1f / (float) (512f / dirtScale));
+ matRock.setFloat("Tex3Scale", 1f / (float) (512f / rockScale));
+ } else {
+ matRock.setBoolean("useTriPlanarMapping", false);
+ matRock.setFloat("Tex1Scale", grassScale);
+ matRock.setFloat("Tex2Scale", dirtScale);
+ matRock.setFloat("Tex3Scale", rockScale);
+ }
+ }
+ }
+ };
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainTestAdvanced.java b/engine/src/test/jme3test/terrain/TerrainTestAdvanced.java
new file mode 100644
index 0000000..92a88d3
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainTestAdvanced.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.scene.Node;
+import com.jme3.scene.debug.Arrow;
+
+/**
+ * Uses the terrain's lighting texture with normal maps and lights.
+ *
+ * @author bowens
+ */
+public class TerrainTestAdvanced extends SimpleApplication {
+
+ private TerrainQuad terrain;
+ Material matTerrain;
+ Material matWire;
+ boolean wireframe = false;
+ boolean triPlanar = false;
+ boolean wardiso = false;
+ boolean minnaert = false;
+ protected BitmapText hintText;
+ PointLight pl;
+ Geometry lightMdl;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+
+ public static void main(String[] args) {
+ TerrainTestAdvanced app = new TerrainTestAdvanced();
+ app.start();
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+
+ loadHintText();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ setupKeys();
+
+ // First, we load up our textures and the heightmap texture for the terrain
+
+ // TERRAIN TEXTURE material
+ matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matTerrain.setBoolean("useTriPlanarMapping", false);
+ matTerrain.setFloat("Shininess", 0.0f);
+
+ // ALPHA map (for splat textures)
+ matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png"));
+ matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png"));
+
+ // HEIGHTMAP image (for the terrain heightmap)
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ //matTerrain.setTexture("DiffuseMap_1", grass);
+ //matTerrain.setFloat("DiffuseMap_1_scale", grassScale);
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap", dirt);
+ matTerrain.setFloat("DiffuseMap_0_scale", dirtScale);
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ //matTerrain.setTexture("DiffuseMap_2", rock);
+ //matTerrain.setFloat("DiffuseMap_2_scale", rockScale);
+
+ // BRICK texture
+ Texture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");
+ brick.setWrap(WrapMode.Repeat);
+ //matTerrain.setTexture("DiffuseMap_3", brick);
+ //matTerrain.setFloat("DiffuseMap_3_scale", rockScale);
+
+ // RIVER ROCK texture
+ Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg");
+ riverRock.setWrap(WrapMode.Repeat);
+ //matTerrain.setTexture("DiffuseMap_4", riverRock);
+ //matTerrain.setFloat("DiffuseMap_4_scale", rockScale);
+
+
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("NormalMap", normalMap0);
+ //matTerrain.setTexture("NormalMap_1", normalMap2);
+ //matTerrain.setTexture("NormalMap_2", normalMap2);
+ //matTerrain.setTexture("NormalMap_4", normalMap2);
+
+ // WIREFRAME material
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+
+ //createSky();
+
+ // CREATE HEIGHTMAP
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.5f);
+ heightmap.load();
+ heightmap.smooth(0.9f, 1);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ /*
+ * Here we create the actual terrain. The tiles will be 65x65, and the total size of the
+ * terrain will be 513x513. It uses the heightmap we created to generate the height values.
+ */
+ /**
+ * Optimal terrain patch size is 65 (64x64).
+ * The total size is up to you. At 1025 it ran fine for me (200+FPS), however at
+ * size=2049, it got really slow. But that is a jump from 2 million to 8 million triangles...
+ */
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
+ TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ terrain.addControl(control);
+ terrain.setMaterial(matTerrain);
+ terrain.setModelBound(new BoundingBox());
+ terrain.updateModelBound();
+ terrain.setLocalTranslation(0, -100, 0);
+ terrain.setLocalScale(1f, 1f, 1f);
+ rootNode.attachChild(terrain);
+
+ Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
+ //terrain.generateDebugTangents(debugMat);
+
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalize());
+ rootNode.addLight(light);
+
+ cam.setLocation(new Vector3f(0, 10, -10));
+ cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y);
+ flyCam.setMoveSpeed(400);
+
+ rootNode.attachChild(createAxisMarker(20));
+ }
+
+ public void loadHintText() {
+ hintText = new BitmapText(guiFont, false);
+ hintText.setSize(guiFont.getCharSet().getRenderedSize());
+ hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+ hintText.setText("Hit T to switch to wireframe, P to switch to tri-planar texturing");
+ guiNode.attachChild(hintText);
+ }
+
+ private void setupKeys() {
+ flyCam.setMoveSpeed(50);
+ inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(actionListener, "wireframe");
+ inputManager.addMapping("triPlanar", new KeyTrigger(KeyInput.KEY_P));
+ inputManager.addListener(actionListener, "triPlanar");
+ inputManager.addMapping("WardIso", new KeyTrigger(KeyInput.KEY_9));
+ inputManager.addListener(actionListener, "WardIso");
+ inputManager.addMapping("Minnaert", new KeyTrigger(KeyInput.KEY_0));
+ inputManager.addListener(actionListener, "Minnaert");
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean pressed, float tpf) {
+ if (name.equals("wireframe") && !pressed) {
+ wireframe = !wireframe;
+ if (!wireframe) {
+ terrain.setMaterial(matWire);
+ } else {
+ terrain.setMaterial(matTerrain);
+ }
+ } else if (name.equals("triPlanar") && !pressed) {
+ triPlanar = !triPlanar;
+ if (triPlanar) {
+ matTerrain.setBoolean("useTriPlanarMapping", true);
+ // planar textures don't use the mesh's texture coordinates but real world coordinates,
+ // so we need to convert these texture coordinate scales into real world scales so it looks
+ // the same when we switch to/from tr-planar mode
+ matTerrain.setFloat("DiffuseMap_0_scale", 1f / (float) (512f / grassScale));
+ matTerrain.setFloat("DiffuseMap_1_scale", 1f / (float) (512f / dirtScale));
+ matTerrain.setFloat("DiffuseMap_2_scale", 1f / (float) (512f / rockScale));
+ matTerrain.setFloat("DiffuseMap_3_scale", 1f / (float) (512f / rockScale));
+ matTerrain.setFloat("DiffuseMap_4_scale", 1f / (float) (512f / rockScale));
+ } else {
+ matTerrain.setBoolean("useTriPlanarMapping", false);
+ matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
+ matTerrain.setFloat("DiffuseMap_1_scale", dirtScale);
+ matTerrain.setFloat("DiffuseMap_2_scale", rockScale);
+ matTerrain.setFloat("DiffuseMap_3_scale", rockScale);
+ matTerrain.setFloat("DiffuseMap_4_scale", rockScale);
+ }
+ }
+ }
+ };
+
+ private void createSky() {
+ Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
+ Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
+ Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
+ Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
+ Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
+ Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
+
+ Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
+ rootNode.attachChild(sky);
+ }
+
+ protected Node createAxisMarker(float arrowSize) {
+
+ Material redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ redMat.getAdditionalRenderState().setWireframe(true);
+ redMat.setColor("Color", ColorRGBA.Red);
+
+ Material greenMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ greenMat.getAdditionalRenderState().setWireframe(true);
+ greenMat.setColor("Color", ColorRGBA.Green);
+
+ Material blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ blueMat.getAdditionalRenderState().setWireframe(true);
+ blueMat.setColor("Color", ColorRGBA.Blue);
+
+ Node axis = new Node();
+
+ // create arrows
+ Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0)));
+ arrowX.setMaterial(redMat);
+ Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0)));
+ arrowY.setMaterial(greenMat);
+ Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize)));
+ arrowZ.setMaterial(blueMat);
+ axis.attachChild(arrowX);
+ axis.attachChild(arrowY);
+ axis.attachChild(arrowZ);
+
+ //axis.setModelBound(new BoundingBox());
+ return axis;
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainTestCollision.java b/engine/src/test/jme3test/terrain/TerrainTestCollision.java
new file mode 100644
index 0000000..9bcd48a
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainTestCollision.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.terrain;
+
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import jme3tools.converters.ImageToAwt;
+
+/**
+ * Creates a terrain object and a collision node to go with it. Then
+ * drops several balls from the sky that collide with the terrain
+ * and roll around.
+ * Left click to place a sphere on the ground where the crosshairs intersect the terrain.
+ * Hit keys 1 or 2 to raise/lower the terrain at that spot.
+ *
+ * @author Brent Owens
+ */
+public class TerrainTestCollision extends SimpleApplication {
+
+ TerrainQuad terrain;
+ Node terrainPhysicsNode;
+ Material matRock;
+ Material matWire;
+ boolean wireframe = false;
+ protected BitmapText hintText;
+ PointLight pl;
+ Geometry lightMdl;
+ Geometry collisionMarker;
+ private BulletAppState bulletAppState;
+ Geometry collisionSphere;
+ Geometry collisionBox;
+ Geometry selectedCollisionObject;
+
+ public static void main(String[] args) {
+ TerrainTestCollision app = new TerrainTestCollision();
+ app.start();
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+ loadHintText();
+ initCrossHairs();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+ stateManager.attach(bulletAppState);
+ setupKeys();
+ matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
+ matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matRock.setTexture("Tex1", grass);
+ matRock.setFloat("Tex1Scale", 64f);
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matRock.setTexture("Tex2", dirt);
+ matRock.setFloat("Tex2Scale", 32f);
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matRock.setTexture("Tex3", rock);
+ matRock.setFloat("Tex3Scale", 128f);
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
+ heightmap.load();
+
+ } catch (Exception e) {
+ }
+
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ terrain.addControl(control);
+ terrain.setMaterial(matRock);
+ terrain.setLocalScale(new Vector3f(2, 2, 2));
+ terrain.setLocked(false); // unlock it so we can edit the height
+ rootNode.attachChild(terrain);
+
+
+ /**
+ * Create PhysicsRigidBodyControl for collision
+ */
+ terrain.addControl(new RigidBodyControl(0));
+ bulletAppState.getPhysicsSpace().addAll(terrain);
+
+
+ // Add 5 physics spheres to the world, with random sizes and positions
+ // let them drop from the sky
+ for (int i = 0; i < 5; i++) {
+ float r = (float) (8 * Math.random());
+ Geometry sphere = new Geometry("cannonball", new Sphere(10, 10, r));
+ sphere.setMaterial(matWire);
+ float x = (float) (20 * Math.random()) - 40; // random position
+ float y = (float) (20 * Math.random()) - 40; // random position
+ float z = (float) (20 * Math.random()) - 40; // random position
+ sphere.setLocalTranslation(new Vector3f(x, 100 + y, z));
+ sphere.addControl(new RigidBodyControl(new SphereCollisionShape(r), 2));
+ rootNode.attachChild(sphere);
+ bulletAppState.getPhysicsSpace().add(sphere);
+ }
+
+ collisionBox = new Geometry("collisionBox", new Box(2, 2, 2));
+ collisionBox.setModelBound(new BoundingBox());
+ collisionBox.setLocalTranslation(new Vector3f(20, 95, 30));
+ collisionBox.setMaterial(matWire);
+ rootNode.attachChild(collisionBox);
+ selectedCollisionObject = collisionBox;
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(1, -0.5f, -0.1f).normalizeLocal());
+ dl.setColor(new ColorRGBA(0.50f, 0.40f, 0.50f, 1.0f));
+ rootNode.addLight(dl);
+
+ cam.setLocation(new Vector3f(0, 25, -10));
+ cam.lookAtDirection(new Vector3f(0, -1, 0).normalizeLocal(), Vector3f.UNIT_Y);
+ }
+
+ public void loadHintText() {
+ hintText = new BitmapText(guiFont, false);
+ hintText.setSize(guiFont.getCharSet().getRenderedSize());
+ hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+ //hintText.setText("Hit T to switch to wireframe");
+ hintText.setText("");
+ guiNode.attachChild(hintText);
+ }
+
+ protected void initCrossHairs() {
+ //guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+
+ private void setupKeys() {
+ flyCam.setMoveSpeed(50);
+ inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(actionListener, "wireframe");
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Forwards", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("Backs", new KeyTrigger(KeyInput.KEY_I));
+ inputManager.addListener(actionListener, "Lefts");
+ inputManager.addListener(actionListener, "Rights");
+ inputManager.addListener(actionListener, "Ups");
+ inputManager.addListener(actionListener, "Downs");
+ inputManager.addListener(actionListener, "Forwards");
+ inputManager.addListener(actionListener, "Backs");
+ inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(actionListener, "shoot");
+ inputManager.addMapping("cameraDown", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
+ inputManager.addListener(actionListener, "cameraDown");
+ }
+
+ @Override
+ public void update() {
+ super.update();
+ }
+
+ private void createCollisionMarker() {
+ Sphere s = new Sphere(6, 6, 1);
+ collisionMarker = new Geometry("collisionMarker");
+ collisionMarker.setMesh(s);
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", ColorRGBA.Orange);
+ collisionMarker.setMaterial(mat);
+ rootNode.attachChild(collisionMarker);
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String binding, boolean keyPressed, float tpf) {
+ if (binding.equals("wireframe") && !keyPressed) {
+ wireframe = !wireframe;
+ if (!wireframe) {
+ terrain.setMaterial(matWire);
+ } else {
+ terrain.setMaterial(matRock);
+ }
+ } else if (binding.equals("shoot") && !keyPressed) {
+
+ Vector3f origin = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.0f);
+ Vector3f direction = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.3f);
+ direction.subtractLocal(origin).normalizeLocal();
+
+
+ Ray ray = new Ray(origin, direction);
+ CollisionResults results = new CollisionResults();
+ int numCollisions = terrain.collideWith(ray, results);
+ if (numCollisions > 0) {
+ CollisionResult hit = results.getClosestCollision();
+ if (collisionMarker == null) {
+ createCollisionMarker();
+ }
+ Vector2f loc = new Vector2f(hit.getContactPoint().x, hit.getContactPoint().z);
+ float height = terrain.getHeight(loc);
+ System.out.println("collide " + hit.getContactPoint() + ", height: " + height + ", distance: " + hit.getDistance());
+ collisionMarker.setLocalTranslation(new Vector3f(hit.getContactPoint().x, height, hit.getContactPoint().z));
+ }
+ } else if (binding.equals("cameraDown") && !keyPressed) {
+ getCamera().lookAtDirection(new Vector3f(0, -1, 0), Vector3f.UNIT_Y);
+ } else if (binding.equals("Lefts") && !keyPressed) {
+ Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone();
+ selectedCollisionObject.move(-0.5f, 0, 0);
+ testCollision(oldLoc);
+ } else if (binding.equals("Rights") && !keyPressed) {
+ Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone();
+ selectedCollisionObject.move(0.5f, 0, 0);
+ testCollision(oldLoc);
+ } else if (binding.equals("Forwards") && !keyPressed) {
+ Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone();
+ selectedCollisionObject.move(0, 0, 0.5f);
+ testCollision(oldLoc);
+ } else if (binding.equals("Backs") && !keyPressed) {
+ Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone();
+ selectedCollisionObject.move(0, 0, -0.5f);
+ testCollision(oldLoc);
+ } else if (binding.equals("Ups") && !keyPressed) {
+ Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone();
+ selectedCollisionObject.move(0, 0.5f, 0);
+ testCollision(oldLoc);
+ } else if (binding.equals("Downs") && !keyPressed) {
+ Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone();
+ selectedCollisionObject.move(0, -0.5f, 0);
+ testCollision(oldLoc);
+ }
+
+ }
+ };
+
+ private void testCollision(Vector3f oldLoc) {
+ if (terrain.collideWith(selectedCollisionObject.getWorldBound(), new CollisionResults()) > 0) {
+ selectedCollisionObject.setLocalTranslation(oldLoc);
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java b/engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java
new file mode 100644
index 0000000..f813f08
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.debug.Arrow;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.FilteredBasis;
+import com.jme3.terrain.noise.filter.IterativeFilter;
+import com.jme3.terrain.noise.filter.OptimizedErode;
+import com.jme3.terrain.noise.filter.PerturbFilter;
+import com.jme3.terrain.noise.filter.SmoothFilter;
+import com.jme3.terrain.noise.fractal.FractalSum;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Brent Owens
+ */
+public class TerrainTestModifyHeight extends SimpleApplication {
+
+ private TerrainQuad terrain;
+ Material matTerrain;
+ Material matWire;
+ boolean wireframe = true;
+ boolean triPlanar = false;
+ boolean wardiso = false;
+ boolean minnaert = false;
+ protected BitmapText hintText;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+
+ private boolean raiseTerrain = false;
+ private boolean lowerTerrain = false;
+
+ private Geometry marker;
+ private Geometry markerNormal;
+
+ public static void main(String[] args) {
+ TerrainTestModifyHeight app = new TerrainTestModifyHeight();
+ app.start();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf){
+ Vector3f intersection = getWorldIntersection();
+ updateHintText(intersection);
+
+ if (raiseTerrain){
+
+ if (intersection != null) {
+ adjustHeight(intersection, 64, tpf * 60);
+ }
+ }else if (lowerTerrain){
+ if (intersection != null) {
+ adjustHeight(intersection, 64, -tpf * 60);
+ }
+ }
+
+ if (terrain != null && intersection != null) {
+ float h = terrain.getHeight(new Vector2f(intersection.x, intersection.z));
+ Vector3f tl = terrain.getWorldTranslation();
+ marker.setLocalTranslation(tl.add(new Vector3f(intersection.x, h, intersection.z)) );
+ markerNormal.setLocalTranslation(tl.add(new Vector3f(intersection.x, h, intersection.z)) );
+
+ Vector3f normal = terrain.getNormal(new Vector2f(intersection.x, intersection.z));
+ ((Arrow)markerNormal.getMesh()).setArrowExtent(normal);
+ }
+ }
+
+ @Override
+ public void simpleInitApp() {
+ loadHintText();
+ initCrossHairs();
+ setupKeys();
+
+ createMarker();
+
+ // WIREFRAME material
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+
+ createTerrain();
+ //createTerrainGrid();
+
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+ rootNode.addLight(light);
+
+ AmbientLight ambLight = new AmbientLight();
+ ambLight.setColor(new ColorRGBA(1f, 1f, 0.8f, 0.2f));
+ rootNode.addLight(ambLight);
+
+ cam.setLocation(new Vector3f(0, 256, 0));
+ cam.lookAtDirection(new Vector3f(0, -1f, 0).normalizeLocal(), Vector3f.UNIT_X);
+ }
+
+ public void loadHintText() {
+ hintText = new BitmapText(guiFont, false);
+ hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+ hintText.setText("Hit 1 to raise terrain, hit 2 to lower terrain");
+ guiNode.attachChild(hintText);
+ }
+
+ public void updateHintText(Vector3f target) {
+ int x = (int) getCamera().getLocation().x;
+ int y = (int) getCamera().getLocation().y;
+ int z = (int) getCamera().getLocation().z;
+ String targetText = "";
+ if (target!= null)
+ targetText = " intersect: "+target.toString();
+ hintText.setText("Press left mouse button to raise terrain, press right mouse button to lower terrain. " + x + "," + y + "," + z+targetText);
+ }
+
+ protected void initCrossHairs() {
+ BitmapText ch = new BitmapText(guiFont, false);
+ ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+ ch.setText("+"); // crosshairs
+ ch.setLocalTranslation( // center
+ settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+ settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+ guiNode.attachChild(ch);
+ }
+
+ private void setupKeys() {
+ flyCam.setMoveSpeed(100);
+ inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(actionListener, "wireframe");
+ inputManager.addMapping("Raise", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(actionListener, "Raise");
+ inputManager.addMapping("Lower", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
+ inputManager.addListener(actionListener, "Lower");
+ }
+ private ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean pressed, float tpf) {
+ if (name.equals("wireframe") && !pressed) {
+ wireframe = !wireframe;
+ if (!wireframe) {
+ terrain.setMaterial(matWire);
+ } else {
+ terrain.setMaterial(matTerrain);
+ }
+ } else if (name.equals("Raise")) {
+ raiseTerrain = pressed;
+ } else if (name.equals("Lower")) {
+ lowerTerrain = pressed;
+ }
+ }
+ };
+
+ private void adjustHeight(Vector3f loc, float radius, float height) {
+
+ // offset it by radius because in the loop we iterate through 2 radii
+ int radiusStepsX = (int) (radius / terrain.getLocalScale().x);
+ int radiusStepsZ = (int) (radius / terrain.getLocalScale().z);
+
+ float xStepAmount = terrain.getLocalScale().x;
+ float zStepAmount = terrain.getLocalScale().z;
+ long start = System.currentTimeMillis();
+ List<Vector2f> locs = new ArrayList<Vector2f>();
+ List<Float> heights = new ArrayList<Float>();
+
+ for (int z = -radiusStepsZ; z < radiusStepsZ; z++) {
+ for (int x = -radiusStepsX; x < radiusStepsX; x++) {
+
+ float locX = loc.x + (x * xStepAmount);
+ float locZ = loc.z + (z * zStepAmount);
+
+ if (isInRadius(locX - loc.x, locZ - loc.z, radius)) {
+ // see if it is in the radius of the tool
+ float h = calculateHeight(radius, height, locX - loc.x, locZ - loc.z);
+ locs.add(new Vector2f(locX, locZ));
+ heights.add(h);
+ }
+ }
+ }
+
+ terrain.adjustHeight(locs, heights);
+ //System.out.println("Modified "+locs.size()+" points, took: " + (System.currentTimeMillis() - start)+" ms");
+ terrain.updateModelBound();
+ }
+
+ private boolean isInRadius(float x, float y, float radius) {
+ Vector2f point = new Vector2f(x, y);
+ // return true if the distance is less than equal to the radius
+ return point.length() <= radius;
+ }
+
+ private float calculateHeight(float radius, float heightFactor, float x, float z) {
+ // find percentage for each 'unit' in radius
+ Vector2f point = new Vector2f(x, z);
+ float val = point.length() / radius;
+ val = 1 - val;
+ if (val <= 0) {
+ val = 0;
+ }
+ return heightFactor * val;
+ }
+
+ private Vector3f getWorldIntersection() {
+ Vector3f origin = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.0f);
+ Vector3f direction = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.3f);
+ direction.subtractLocal(origin).normalizeLocal();
+
+ Ray ray = new Ray(origin, direction);
+ CollisionResults results = new CollisionResults();
+ int numCollisions = terrain.collideWith(ray, results);
+ if (numCollisions > 0) {
+ CollisionResult hit = results.getClosestCollision();
+ return hit.getContactPoint();
+ }
+ return null;
+ }
+
+ private void createTerrain() {
+ // First, we load up our textures and the heightmap texture for the terrain
+
+ // TERRAIN TEXTURE material
+ matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matTerrain.setBoolean("useTriPlanarMapping", false);
+ matTerrain.setBoolean("WardIso", true);
+ matTerrain.setFloat("Shininess", 0);
+
+ // ALPHA map (for splat textures)
+ matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap", grass);
+ matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_1", dirt);
+ matTerrain.setFloat("DiffuseMap_1_scale", dirtScale);
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_2", rock);
+ matTerrain.setFloat("DiffuseMap_2_scale", rockScale);
+
+ // HEIGHTMAP image (for the terrain heightmap)
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.5f);
+ heightmap.load();
+ heightmap.smooth(0.9f, 1);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // CREATE THE TERRAIN
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ terrain.addControl(control);
+ terrain.setMaterial(matTerrain);
+ terrain.setLocalTranslation(0, -100, 0);
+ terrain.setLocalScale(2.5f, 0.5f, 2.5f);
+ rootNode.attachChild(terrain);
+ }
+
+ private void createTerrainGrid() {
+
+ // TERRAIN TEXTURE material
+ matTerrain = new Material(this.assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
+
+ // Parameters to material:
+ // regionXColorMap: X = 1..4 the texture that should be appliad to state X
+ // regionX: a Vector3f containing the following information:
+ // regionX.x: the start height of the region
+ // regionX.y: the end height of the region
+ // regionX.z: the texture scale for the region
+ // it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
+ // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
+ // slopeTileFactor: the texture scale for slopes
+ // terrainSize: the total size of the terrain (used for scaling the texture)
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("region1ColorMap", grass);
+ matTerrain.setVector3("region1", new Vector3f(88, 200, this.grassScale));
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("region2ColorMap", dirt);
+ matTerrain.setVector3("region2", new Vector3f(0, 90, this.dirtScale));
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("region3ColorMap", rock);
+ matTerrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));
+
+ matTerrain.setTexture("region4ColorMap", rock);
+ matTerrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));
+
+ matTerrain.setTexture("slopeColorMap", rock);
+ matTerrain.setFloat("slopeTileFactor", 32);
+
+ matTerrain.setFloat("terrainSize", 513);
+
+ FractalSum base = new FractalSum();
+ base.setRoughness(0.7f);
+ base.setFrequency(1.0f);
+ base.setAmplitude(1.0f);
+ base.setLacunarity(2.12f);
+ base.setOctaves(8);
+ base.setScale(0.02125f);
+ base.addModulator(new NoiseModulator() {
+ @Override
+ public float value(float... in) {
+ return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
+ }
+ });
+
+ FilteredBasis ground = new FilteredBasis(base);
+
+ PerturbFilter perturb = new PerturbFilter();
+ perturb.setMagnitude(0.119f);
+
+ OptimizedErode therm = new OptimizedErode();
+ therm.setRadius(5);
+ therm.setTalus(0.011f);
+
+ SmoothFilter smooth = new SmoothFilter();
+ smooth.setRadius(1);
+ smooth.setEffect(0.7f);
+
+ IterativeFilter iterate = new IterativeFilter();
+ iterate.addPreFilter(perturb);
+ iterate.addPostFilter(smooth);
+ iterate.setFilter(therm);
+ iterate.setIterations(1);
+
+ ground.addPreFilter(iterate);
+
+ this.terrain = new TerrainGrid("terrain", 65, 257, new FractalTileLoader(ground, 256f));
+
+
+ terrain.setMaterial(matTerrain);
+ terrain.setLocalTranslation(0, 0, 0);
+ terrain.setLocalScale(2f, 1f, 2f);
+
+ rootNode.attachChild(this.terrain);
+
+ TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
+ this.terrain.addControl(control);
+ }
+
+ private void createMarker() {
+ // collision marker
+ Sphere sphere = new Sphere(8, 8, 0.5f);
+ marker = new Geometry("Marker");
+ marker.setMesh(sphere);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", new ColorRGBA(251f/255f, 130f/255f, 0f, 0.6f));
+ mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+
+ marker.setMaterial(mat);
+ rootNode.attachChild(marker);
+
+
+ // surface normal marker
+ Arrow arrow = new Arrow(new Vector3f(0,1,0));
+ markerNormal = new Geometry("MarkerNormal");
+ markerNormal.setMesh(arrow);
+ markerNormal.setMaterial(mat);
+ rootNode.attachChild(markerNormal);
+ }
+}
diff --git a/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java b/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java
new file mode 100644
index 0000000..8ba9404
--- /dev/null
+++ b/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.export.Savable;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Saves and loads terrain.
+ *
+ * @author Brent Owens
+ */
+public class TerrainTestReadWrite extends SimpleApplication {
+
+ private Terrain terrain;
+ protected BitmapText hintText;
+ private float grassScale = 64;
+ private float dirtScale = 16;
+ private float rockScale = 128;
+ private Material matTerrain;
+ private Material matWire;
+
+ public static void main(String[] args) {
+ TerrainTestReadWrite app = new TerrainTestReadWrite();
+ app.start();
+ //testHeightmapBuilding();
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+
+ loadHintText();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+
+ createControls();
+ createMap();
+ }
+
+ private void createMap() {
+ matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matTerrain.setBoolean("useTriPlanarMapping", false);
+ matTerrain.setBoolean("WardIso", true);
+
+ // ALPHA map (for splat textures)
+ matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+
+ // HEIGHTMAP image (for the terrain heightmap)
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+
+ // GRASS texture
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap", grass);
+ matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
+
+
+ // DIRT texture
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_1", dirt);
+ matTerrain.setFloat("DiffuseMap_1_scale", dirtScale);
+
+ // ROCK texture
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("DiffuseMap_2", rock);
+ matTerrain.setFloat("DiffuseMap_2_scale", rockScale);
+
+
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matTerrain.setTexture("NormalMap", normalMap0);
+ matTerrain.setTexture("NormalMap_1", normalMap2);
+ matTerrain.setTexture("NormalMap_2", normalMap2);
+
+ matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ matWire.getAdditionalRenderState().setWireframe(true);
+ matWire.setColor("Color", ColorRGBA.Green);
+
+
+ // CREATE HEIGHTMAP
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);
+ heightmap.load();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (new File("terrainsave.jme").exists()) {
+ loadTerrain();
+ } else {
+ // create the terrain as normal, and give it a control for LOD management
+ TerrainQuad terrainQuad = new TerrainQuad("terrain", 65, 129, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
+ TerrainLodControl control = new TerrainLodControl(terrainQuad, getCamera());
+ control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+ terrainQuad.addControl(control);
+ terrainQuad.setMaterial(matTerrain);
+ terrainQuad.setLocalTranslation(0, -100, 0);
+ terrainQuad.setLocalScale(4f, 0.25f, 4f);
+ rootNode.attachChild(terrainQuad);
+
+ this.terrain = terrainQuad;
+ }
+
+ DirectionalLight light = new DirectionalLight();
+ light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+ rootNode.addLight(light);
+ }
+
+ /**
+ * Create the save and load actions and add them to the input listener
+ */
+ private void createControls() {
+ flyCam.setMoveSpeed(50);
+ cam.setLocation(new Vector3f(0, 100, 0));
+
+ inputManager.addMapping("save", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(saveActionListener, "save");
+
+ inputManager.addMapping("load", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addListener(loadActionListener, "load");
+
+ inputManager.addMapping("clone", new KeyTrigger(KeyInput.KEY_C));
+ inputManager.addListener(cloneActionListener, "clone");
+ }
+
+ public void loadHintText() {
+ hintText = new BitmapText(guiFont, false);
+ hintText.setSize(guiFont.getCharSet().getRenderedSize());
+ hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+ hintText.setText("Hit T to save, and Y to load");
+ guiNode.attachChild(hintText);
+ }
+ private ActionListener saveActionListener = new ActionListener() {
+
+ public void onAction(String name, boolean pressed, float tpf) {
+ if (name.equals("save") && !pressed) {
+
+ FileOutputStream fos = null;
+ try {
+ long start = System.currentTimeMillis();
+ fos = new FileOutputStream(new File("terrainsave.jme"));
+
+ // we just use the exporter and pass in the terrain
+ BinaryExporter.getInstance().save((Savable)terrain, new BufferedOutputStream(fos));
+
+ fos.flush();
+ float duration = (System.currentTimeMillis() - start) / 1000.0f;
+ System.out.println("Save took " + duration + " seconds");
+ } catch (IOException ex) {
+ Logger.getLogger(TerrainTestReadWrite.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ try {
+ if (fos != null) {
+ fos.close();
+ }
+ } catch (IOException e) {
+ Logger.getLogger(TerrainTestReadWrite.class.getName()).log(Level.SEVERE, null, e);
+ }
+ }
+ }
+ }
+ };
+
+ private void loadTerrain() {
+ FileInputStream fis = null;
+ try {
+ long start = System.currentTimeMillis();
+ // remove the existing terrain and detach it from the root node.
+ if (terrain != null) {
+ Node existingTerrain = (Node)terrain;
+ existingTerrain.removeFromParent();
+ existingTerrain.removeControl(TerrainLodControl.class);
+ existingTerrain.detachAllChildren();
+ terrain = null;
+ }
+
+ // import the saved terrain, and attach it back to the root node
+ File f = new File("terrainsave.jme");
+ fis = new FileInputStream(f);
+ BinaryImporter imp = BinaryImporter.getInstance();
+ imp.setAssetManager(assetManager);
+ terrain = (TerrainQuad) imp.load(new BufferedInputStream(fis));
+ rootNode.attachChild((Node)terrain);
+
+ float duration = (System.currentTimeMillis() - start) / 1000.0f;
+ System.out.println("Load took " + duration + " seconds");
+
+ // now we have to add back the camera to the LOD control
+ TerrainLodControl lodControl = ((Node)terrain).getControl(TerrainLodControl.class);
+ if (lodControl != null)
+ lodControl.setCamera(getCamera());
+
+ } catch (IOException ex) {
+ Logger.getLogger(TerrainTestReadWrite.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ } catch (IOException ex) {
+ Logger.getLogger(TerrainTestReadWrite.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+ private ActionListener loadActionListener = new ActionListener() {
+
+ public void onAction(String name, boolean pressed, float tpf) {
+ if (name.equals("load") && !pressed) {
+ loadTerrain();
+ }
+ }
+ };
+ private ActionListener cloneActionListener = new ActionListener() {
+
+ public void onAction(String name, boolean pressed, float tpf) {
+ if (name.equals("clone") && !pressed) {
+
+ Terrain clone = (Terrain) ((Node)terrain).clone();
+ ((Node)terrain).removeFromParent();
+ terrain = clone;
+ getRootNode().attachChild((Node)terrain);
+ }
+ }
+ };
+
+ // no junit tests, so this has to be hand-tested:
+ private static void testHeightmapBuilding() {
+ int s = 9;
+ int b = 3;
+ float[] hm = new float[s * s];
+ for (int i = 0; i < s; i++) {
+ for (int j = 0; j < s; j++) {
+ hm[(i * s) + j] = i * j;
+ }
+ }
+
+ for (int i = 0; i < s; i++) {
+ for (int j = 0; j < s; j++) {
+ System.out.print(hm[i * s + j] + " ");
+ }
+ System.out.println("");
+ }
+
+ TerrainQuad terrain = new TerrainQuad("terrain", b, s, hm);
+ float[] hm2 = terrain.getHeightMap();
+ boolean failed = false;
+ for (int i = 0; i < s * s; i++) {
+ if (hm[i] != hm2[i]) {
+ failed = true;
+ }
+ }
+
+ System.out.println("");
+ if (failed) {
+ System.out.println("Terrain heightmap building FAILED!!!");
+ for (int i = 0; i < s; i++) {
+ for (int j = 0; j < s; j++) {
+ System.out.print(hm2[i * s + j] + " ");
+ }
+ System.out.println("");
+ }
+ } else {
+ System.out.println("Terrain heightmap building PASSED");
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/texture/TestSkyLoading.java b/engine/src/test/jme3test/texture/TestSkyLoading.java
new file mode 100644
index 0000000..7101f1b
--- /dev/null
+++ b/engine/src/test/jme3test/texture/TestSkyLoading.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.texture;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.Texture;
+import com.jme3.util.SkyFactory;
+
+public class TestSkyLoading extends SimpleApplication {
+
+ public static void main(String[] args){
+ TestSkyLoading app = new TestSkyLoading();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
+ Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
+ Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
+ Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
+ Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
+ Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
+
+ Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
+ rootNode.attachChild(sky);
+ }
+
+}
diff --git a/engine/src/test/jme3test/texture/TestTexture3D.java b/engine/src/test/jme3test/texture/TestTexture3D.java
new file mode 100644
index 0000000..de93f8c
--- /dev/null
+++ b/engine/src/test/jme3test/texture/TestTexture3D.java
@@ -0,0 +1,103 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.texture;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+
+public class TestTexture3D extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestTexture3D app = new TestTexture3D();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ //mouseInput.setCursorVisible(true);
+ flyCam.setMoveSpeed(10);
+ //creating a sphere
+ Sphere sphere = new Sphere(32, 32, 1);
+ //getting the boundingbox
+ sphere.updateBound();
+ BoundingBox bb = (BoundingBox) sphere.getBound();
+ Vector3f min = bb.getMin(null);
+ float[] ext = new float[]{bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2};
+ //we need to change the UV coordinates (the sphere is assumet to be inside the 3D image box)
+ sphere.clearBuffer(Type.TexCoord);
+ VertexBuffer vb = sphere.getBuffer(Type.Position);
+ FloatBuffer fb = (FloatBuffer) vb.getData();
+ float[] uvCoordinates = BufferUtils.getFloatArray(fb);
+ //now transform the coordinates so that they are in the range of <0; 1>
+ for (int i = 0; i < uvCoordinates.length; i += 3) {
+ uvCoordinates[i] = (uvCoordinates[i] - min.x) / ext[0];
+ uvCoordinates[i + 1] = (uvCoordinates[i + 1] - min.y) / ext[1];
+ uvCoordinates[i + 2] = (uvCoordinates[i + 2] - min.z) / ext[2];
+ }
+ //apply new texture coordinates
+ VertexBuffer uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
+ uvCoordsBuffer.setupData(Usage.Static, 3, com.jme3.scene.VertexBuffer.Format.Float,
+ BufferUtils.createFloatBuffer(uvCoordinates));
+ sphere.setBuffer(uvCoordsBuffer);
+ //create geometry, and apply material and our 3D texture
+ Geometry g = new Geometry("sphere", sphere);
+ Material material = new Material(assetManager, "jme3test/texture/tex3D.j3md");
+ try {
+ Texture texture = this.getTexture();
+ material.setTexture("Texture", texture);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ g.setMaterial(material);
+ rootNode.attachChild(g);
+ //add some light so that it is visible
+ PointLight light = new PointLight();
+ light.setColor(ColorRGBA.White);
+ light.setPosition(new Vector3f(5, 5, 5));
+ light.setRadius(20);
+ rootNode.addLight(light);
+ light = new PointLight();
+ light.setColor(ColorRGBA.White);
+ light.setPosition(new Vector3f(-5, -5, -5));
+ light.setRadius(20);
+ rootNode.addLight(light);
+ }
+
+ /**
+ * This method creates a RGB8 texture with the sizes of 10x10x10 pixels.
+ */
+ private Texture getTexture() throws IOException {
+ ArrayList<ByteBuffer> data = new ArrayList<ByteBuffer>(1);
+ ByteBuffer bb = BufferUtils.createByteBuffer(10 * 10 * 10 * 3);//all data must be inside one buffer
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 10 * 10; ++j) {
+ bb.put((byte) (255f*i/10f));
+ bb.put((byte) (255f*i/10f));
+ bb.put((byte) (255f));
+ }
+ }
+ bb.rewind();
+ data.add(bb);
+ return new Texture3D(new Image(Format.RGB8, 10, 10, 10, data));
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/TestTexture3DLoading.java b/engine/src/test/jme3test/texture/TestTexture3DLoading.java
new file mode 100644
index 0000000..bc53362
--- /dev/null
+++ b/engine/src/test/jme3test/texture/TestTexture3DLoading.java
@@ -0,0 +1,54 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.texture;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+import com.jme3.texture.Texture;
+
+public class TestTexture3DLoading extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestTexture3DLoading app = new TestTexture3DLoading();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ viewPort.setBackgroundColor(ColorRGBA.DarkGray);
+ flyCam.setEnabled(false);
+
+
+ Quad q = new Quad(10, 10);
+
+ Geometry geom = new Geometry("Quad", q);
+ Material material = new Material(assetManager, "jme3test/texture/tex3DThumb.j3md");
+ TextureKey key = new TextureKey("Textures/3D/flame.dds");
+ key.setGenerateMips(true);
+ key.setAsTexture3D(true);
+
+ Texture t = assetManager.loadTexture(key);
+
+ int rows = 4;//4 * 4
+
+ q.scaleTextureCoordinates(new Vector2f(rows, rows));
+
+ //The image only have 8 pictures and we have 16 thumbs, the data will be interpolated by the GPU
+ material.setFloat("InvDepth", 1f / 16f);
+ material.setInt("Rows", rows);
+ material.setTexture("Texture", t);
+ geom.setMaterial(material);
+
+ rootNode.attachChild(geom);
+
+ cam.setLocation(new Vector3f(4.7444625f, 5.160054f, 13.1939f));
+ }
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/TestTextureArray.java b/engine/src/test/jme3test/texture/TestTextureArray.java
new file mode 100644
index 0000000..823fd78
--- /dev/null
+++ b/engine/src/test/jme3test/texture/TestTextureArray.java
@@ -0,0 +1,86 @@
+package jme3test.texture;
+
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Caps;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.TextureArray;
+import com.jme3.util.BufferUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestTextureArray extends SimpleApplication
+{
+
+ @Override
+ public void simpleInitApp()
+ {
+ Material mat = new Material(assetManager, "jme3test/texture/UnshadedArray.j3md");
+
+ for (Caps caps : renderManager.getRenderer().getCaps()) {
+ System.out.println(caps.name());
+ }
+ if(!renderManager.getRenderer().getCaps().contains(Caps.TextureArray)){
+ throw new UnsupportedOperationException("Your hardware does not support TextureArray");
+ }
+
+
+ Texture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond.jpg");
+ Texture tex2 = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
+ List<Image> images = new ArrayList<Image>();
+ images.add(tex1.getImage());
+ images.add(tex2.getImage());
+ TextureArray tex3 = new TextureArray(images);
+ mat.setTexture("ColorMap", tex3);
+
+ Mesh m = new Mesh();
+ Vector3f[] vertices = new Vector3f[8];
+ vertices[0] = new Vector3f(0, 0, 0);
+ vertices[1] = new Vector3f(3, 0, 0);
+ vertices[2] = new Vector3f(0, 3, 0);
+ vertices[3] = new Vector3f(3, 3, 0);
+
+ vertices[4] = new Vector3f(3, 0, 0);
+ vertices[5] = new Vector3f(6, 0, 0);
+ vertices[6] = new Vector3f(3, 3, 0);
+ vertices[7] = new Vector3f(6, 3, 0);
+
+ Vector3f[] texCoord = new Vector3f[8];
+ texCoord[0] = new Vector3f(0, 0, 0);
+ texCoord[1] = new Vector3f(1, 0, 0);
+ texCoord[2] = new Vector3f(0, 1, 0);
+ texCoord[3] = new Vector3f(1, 1, 0);
+
+ texCoord[4] = new Vector3f(0, 0, 1);
+ texCoord[5] = new Vector3f(1, 0, 1);
+ texCoord[6] = new Vector3f(0, 1, 1);
+ texCoord[7] = new Vector3f(1, 1, 1);
+
+ int[] indexes = { 2, 0, 1, 1, 3, 2 , 6, 4, 5, 5, 7, 6};
+
+ m.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
+ m.setBuffer(Type.TexCoord, 3, BufferUtils.createFloatBuffer(texCoord));
+ m.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes));
+ m.updateBound();
+
+ Geometry geom = new Geometry("Mesh", m);
+ geom.setMaterial(mat);
+ rootNode.attachChild(geom);
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args)
+ {
+ TestTextureArray app = new TestTextureArray();
+ app.start();
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/UnshadedArray.frag b/engine/src/test/jme3test/texture/UnshadedArray.frag
new file mode 100644
index 0000000..655ff9b
--- /dev/null
+++ b/engine/src/test/jme3test/texture/UnshadedArray.frag
@@ -0,0 +1,50 @@
+uniform vec4 m_Color;
+
+#if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
+ #define NEED_TEXCOORD1
+#endif
+
+#ifdef HAS_COLORMAP
+ uniform sampler2DArray m_ColorMap;
+#endif
+
+#ifdef NEED_TEXCOORD1
+ varying vec3 texCoord1;
+#endif
+
+#ifdef HAS_LIGHTMAP
+ uniform sampler2D m_LightMap;
+ #ifdef SEPERATE_TEXCOORD
+ varying vec3 texCoord2;
+ #endif
+#endif
+
+#ifdef HAS_VERTEXCOLOR
+ varying vec4 vertColor;
+#endif
+
+void main(){
+ vec4 color = vec4(1.0);
+
+ #ifdef HAS_COLORMAP
+ color *= texture2DArray(m_ColorMap, texCoord1);
+ #endif
+
+ #ifdef HAS_VERTEXCOLOR
+ color *= vertColor;
+ #endif
+
+ #ifdef HAS_COLOR
+ color *= m_Color;
+ #endif
+
+ #ifdef HAS_LIGHTMAP
+ #ifdef SEPARATE_TEXCOORD
+ color.rgb *= texture2D(m_LightMap, texCoord2).rgb;
+ #else
+ color.rgb *= texture2D(m_LightMap, texCoord1).rgb;
+ #endif
+ #endif
+
+ gl_FragColor = color;
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/UnshadedArray.j3md b/engine/src/test/jme3test/texture/UnshadedArray.j3md
new file mode 100644
index 0000000..19af9bf
--- /dev/null
+++ b/engine/src/test/jme3test/texture/UnshadedArray.j3md
@@ -0,0 +1,70 @@
+MaterialDef Unshaded {
+
+ MaterialParameters {
+ TextureArray ColorMap
+ Texture2D LightMap
+ Color Color (Color)
+ Boolean VertexColor
+ Boolean SeparateTexCoord
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+ VertexShader GLSL100: jme3test/texture/UnshadedArray.vert
+ FragmentShader GLSL100: jme3test/texture/UnshadedArray.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ SEPARATE_TEXCOORD : SeparateTexCoord
+ HAS_COLORMAP : ColorMap
+ HAS_LIGHTMAP : LightMap
+ HAS_VERTEXCOLOR : VertexColor
+ HAS_COLOR : Color
+ }
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ }
+
+ RenderState {
+
+ }
+
+ }
+
+
+ Technique Glow {
+
+ VertexShader GLSL100: Cjme3test/texture/UnshadedArray.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ HAS_COLORMAP // Must be passed so that Unshaded.vert exports texCoord.
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/UnshadedArray.vert b/engine/src/test/jme3test/texture/UnshadedArray.vert
new file mode 100644
index 0000000..9d1153c
--- /dev/null
+++ b/engine/src/test/jme3test/texture/UnshadedArray.vert
@@ -0,0 +1,38 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+attribute vec3 inPosition;
+
+#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
+ #define NEED_TEXCOORD1
+#endif
+
+#ifdef NEED_TEXCOORD1
+ attribute vec3 inTexCoord;
+ varying vec3 texCoord1;
+#endif
+
+#ifdef SEPARATE_TEXCOORD
+ attribute vec3 inTexCoord2;
+ varying vec3 texCoord2;
+#endif
+
+#ifdef HAS_VERTEXCOLOR
+ attribute vec4 inColor;
+ varying vec4 vertColor;
+#endif
+
+void main(){
+ #ifdef NEED_TEXCOORD1
+ texCoord1 = inTexCoord;
+ #endif
+
+ #ifdef SEPARATE_TEXCOORD
+ texCoord2 = inTexCoord2;
+ #endif
+
+ #ifdef HAS_VERTEXCOLOR
+ vertColor = inColor;
+ #endif
+
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+}
+
diff --git a/engine/src/test/jme3test/texture/tex3D.frag b/engine/src/test/jme3test/texture/tex3D.frag
new file mode 100644
index 0000000..55862ac
--- /dev/null
+++ b/engine/src/test/jme3test/texture/tex3D.frag
@@ -0,0 +1,7 @@
+uniform sampler3D m_Texture;
+
+varying vec3 texCoord;
+
+void main(){
+ gl_FragColor= texture3D(m_Texture,texCoord);
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/tex3D.j3md b/engine/src/test/jme3test/texture/tex3D.j3md
new file mode 100644
index 0000000..1ba2605
--- /dev/null
+++ b/engine/src/test/jme3test/texture/tex3D.j3md
@@ -0,0 +1,16 @@
+MaterialDef My MaterialDef {
+
+ MaterialParameters {
+ Texture3D Texture
+ }
+
+ Technique {
+ VertexShader GLSL100: jme3test/texture/tex3D.vert
+ FragmentShader GLSL100: jme3test/texture/tex3D.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/texture/tex3D.vert b/engine/src/test/jme3test/texture/tex3D.vert
new file mode 100644
index 0000000..f91b7b3
--- /dev/null
+++ b/engine/src/test/jme3test/texture/tex3D.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inTexCoord;
+attribute vec3 inPosition;
+
+varying vec3 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
+ texCoord=inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/tex3DThumb.frag b/engine/src/test/jme3test/texture/tex3DThumb.frag
new file mode 100644
index 0000000..f6eb25a
--- /dev/null
+++ b/engine/src/test/jme3test/texture/tex3DThumb.frag
@@ -0,0 +1,14 @@
+uniform sampler3D m_Texture;
+uniform int m_Rows;
+uniform float m_InvDepth;
+
+varying vec2 texCoord;
+
+void main(){
+float depthx=floor(texCoord.x);
+ float depthy=(m_Rows-1.0) - floor(texCoord.y);
+ //vec3 texC=vec3(texCoord.x,texCoord.y ,0.7);//
+
+ vec3 texC=vec3(fract(texCoord.x),fract(texCoord.y),(depthy*m_Rows+depthx)*m_InvDepth);//
+ gl_FragColor= texture3D(m_Texture,texC);
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/texture/tex3DThumb.j3md b/engine/src/test/jme3test/texture/tex3DThumb.j3md
new file mode 100644
index 0000000..a42bd38
--- /dev/null
+++ b/engine/src/test/jme3test/texture/tex3DThumb.j3md
@@ -0,0 +1,18 @@
+MaterialDef Tex3DThumb {
+
+ MaterialParameters {
+ Texture3D Texture
+ Int Rows;
+ Float InvDepth;
+ }
+
+ Technique {
+ VertexShader GLSL100: jme3test/texture/tex3DThumb.vert
+ FragmentShader GLSL100: jme3test/texture/tex3DThumb.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+}
diff --git a/engine/src/test/jme3test/texture/tex3DThumb.vert b/engine/src/test/jme3test/texture/tex3DThumb.vert
new file mode 100644
index 0000000..6d27bc0
--- /dev/null
+++ b/engine/src/test/jme3test/texture/tex3DThumb.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec2 inTexCoord;
+attribute vec3 inPosition;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
+ texCoord=inTexCoord;
+} \ No newline at end of file
diff --git a/engine/src/test/jme3test/tools/TestOctree.java b/engine/src/test/jme3test/tools/TestOctree.java
new file mode 100644
index 0000000..01eac3b
--- /dev/null
+++ b/engine/src/test/jme3test/tools/TestOctree.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.tools;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bounding.BoundingBox;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireBox;
+import com.jme3.scene.plugins.ogre.MeshLoader;
+import com.jme3.scene.plugins.ogre.OgreMeshKey;
+import com.jme3.texture.FrameBuffer;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import jme3tools.optimize.FastOctnode;
+import jme3tools.optimize.Octree;
+
+
+public class TestOctree extends SimpleApplication implements SceneProcessor {
+
+ private Octree tree;
+ private FastOctnode fastRoot;
+ private Geometry[] globalGeoms;
+ private BoundingBox octBox;
+
+ private Set<Geometry> renderSet = new HashSet<Geometry>(300);
+ private Material mat, mat2;
+ private WireBox box = new WireBox(1,1,1);
+
+ public static void main(String[] args){
+ TestOctree app = new TestOctree();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+// this.flyCam.setMoveSpeed(2000);
+// this.cam.setFrustumFar(10000);
+ MeshLoader.AUTO_INTERLEAVE = false;
+
+// mat = new Material(assetManager, "Common/MatDefs/Misc/WireColor.j3md");
+// mat.setColor("Color", ColorRGBA.White);
+
+// mat2 = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+
+ assetManager.registerLocator("quake3level.zip", "com.jme3.asset.plugins.ZipLocator");
+ MaterialList matList = (MaterialList) assetManager.loadAsset("Scene.material");
+ OgreMeshKey key = new OgreMeshKey("main.meshxml", matList);
+ Spatial scene = assetManager.loadModel(key);
+
+// Spatial scene = assetManager.loadModel("Models/Teapot/teapot.obj");
+// scene.scale(3);
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setColor(ColorRGBA.White);
+ dl.setDirection(new Vector3f(-1, -1, -1).normalize());
+ rootNode.addLight(dl);
+
+ DirectionalLight dl2 = new DirectionalLight();
+ dl2.setColor(ColorRGBA.White);
+ dl2.setDirection(new Vector3f(1, -1, 1).normalize());
+ rootNode.addLight(dl2);
+
+ // generate octree
+// tree = new Octree(scene, 20000);
+ tree = new Octree(scene, 50);
+ tree.construct();
+
+ ArrayList<Geometry> globalGeomList = new ArrayList<Geometry>();
+ tree.createFastOctnodes(globalGeomList);
+ tree.generateFastOctnodeLinks();
+
+ for (Geometry geom : globalGeomList){
+ geom.addLight(dl);
+ geom.addLight(dl2);
+ geom.updateGeometricState();
+ }
+
+ globalGeoms = globalGeomList.toArray(new Geometry[0]);
+ fastRoot = tree.getFastRoot();
+ octBox = tree.getBound();
+
+ viewPort.addProcessor(this);
+ }
+
+ public void initialize(RenderManager rm, ViewPort vp) {
+ }
+
+ public void reshape(ViewPort vp, int w, int h) {
+ }
+
+ public boolean isInitialized() {
+ return true;
+ }
+
+ public void preFrame(float tpf) {
+ }
+
+ public void postQueue(RenderQueue rq) {
+ renderSet.clear();
+ //tree.generateRenderSet(renderSet, cam);
+ fastRoot.generateRenderSet(globalGeoms, renderSet, cam, octBox, true);
+// System.out.println("Geoms: "+renderSet.size());
+ int tris = 0;
+
+ for (Geometry geom : renderSet){
+ tris += geom.getTriangleCount();
+// geom.setMaterial(mat2);
+ rq.addToQueue(geom, geom.getQueueBucket());
+ }
+
+// Matrix4f transform = new Matrix4f();
+// transform.setScale(0.2f, 0.2f, 0.2f);
+// System.out.println("Tris: "+tris);
+
+// tree.renderBounds(rq, transform, box, mat);
+
+// renderManager.flushQueue(viewPort);
+ }
+
+ public void postFrame(FrameBuffer out) {
+ }
+
+ public void cleanup() {
+ }
+}
diff --git a/engine/src/test/jme3test/tools/TestSaveGame.java b/engine/src/test/jme3test/tools/TestSaveGame.java
new file mode 100644
index 0000000..fc7d1b4
--- /dev/null
+++ b/engine/src/test/jme3test/tools/TestSaveGame.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.tools;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import jme3tools.savegame.SaveGame;
+
+public class TestSaveGame extends SimpleApplication {
+
+ public static void main(String[] args) {
+
+ TestSaveGame app = new TestSaveGame();
+ app.start();
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ }
+
+ public void simpleInitApp() {
+
+ //node that is used to store player data
+ Node myPlayer = new Node();
+ myPlayer.setName("PlayerNode");
+ myPlayer.setUserData("name", "Mario");
+ myPlayer.setUserData("health", 100.0f);
+ myPlayer.setUserData("points", 0);
+
+ //the actual model would be attached to this node
+ Spatial model = (Spatial) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ myPlayer.attachChild(model);
+
+ //before saving the game, the model should be detached so its not saved along with the node
+ myPlayer.detachAllChildren();
+ SaveGame.saveGame("mycompany/mygame", "savegame_001", myPlayer);
+
+ //later the game is loaded again
+ Node player = (Node) SaveGame.loadGame("mycompany/mygame", "savegame_001");
+ player.attachChild(model);
+ rootNode.attachChild(player);
+
+ //and the data is available
+ System.out.println("Name: " + player.getUserData("name"));
+ System.out.println("Health: " + player.getUserData("health"));
+ System.out.println("Points: " + player.getUserData("points"));
+
+ AmbientLight al = new AmbientLight();
+ rootNode.addLight(al);
+
+ //note you can also implement your own classes that implement the Savable interface.
+ }
+}
diff --git a/engine/src/test/jme3test/tools/TestTextureAtlas.java b/engine/src/test/jme3test/tools/TestTextureAtlas.java
new file mode 100644
index 0000000..d749e47
--- /dev/null
+++ b/engine/src/test/jme3test/tools/TestTextureAtlas.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.tools;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import jme3tools.optimize.TextureAtlas;
+
+public class TestTextureAtlas extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestTextureAtlas app = new TestTextureAtlas();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(50);
+ Node scene = new Node("Scene");
+ Spatial obj1 = assetManager.loadModel("Models/Ferrari/Car.scene");
+ obj1.setLocalTranslation(-4, 0, 0);
+ Spatial obj2 = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ obj2.setLocalTranslation(-2, 0, 0);
+ Spatial obj3 = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+ obj3.setLocalTranslation(-0, 0, 0);
+ Spatial obj4 = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
+ obj4.setLocalTranslation(2, 0, 0);
+ Spatial obj5 = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
+ obj5.setLocalTranslation(4, 0, 0);
+ scene.attachChild(obj1);
+ scene.attachChild(obj2);
+ scene.attachChild(obj3);
+ scene.attachChild(obj4);
+ scene.attachChild(obj5);
+
+ Geometry geom = TextureAtlas.makeAtlasBatch(scene, assetManager, 2048);
+
+ AmbientLight al = new AmbientLight();
+ rootNode.addLight(al);
+
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(0.69077975f, -0.6277887f, -0.35875428f).normalizeLocal());
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ rootNode.addLight(sun);
+
+ rootNode.attachChild(geom);
+
+ //quad to display material
+ Geometry box = new Geometry("displayquad", new Quad(4, 4));
+ box.setMaterial(geom.getMaterial());
+ box.setLocalTranslation(0, 1, 3);
+ rootNode.attachChild(box);
+ }
+}
diff --git a/engine/src/test/jme3test/water/TestPostWater.java b/engine/src/test/jme3test/water/TestPostWater.java
new file mode 100644
index 0000000..9a89f98
--- /dev/null
+++ b/engine/src/test/jme3test/water/TestPostWater.java
@@ -0,0 +1,298 @@
+package jme3test.water;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.LowPassFilter;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.BloomFilter;
+import com.jme3.post.filters.DepthOfFieldFilter;
+import com.jme3.post.filters.LightScatteringFilter;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.SkyFactory;
+import com.jme3.water.WaterFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * test
+ * @author normenhansen
+ */
+public class TestPostWater extends SimpleApplication {
+
+ private Vector3f lightDir = new Vector3f(-4.9236743f, -1.27054665f, 5.896916f);
+ private WaterFilter water;
+ TerrainQuad terrain;
+ Material matRock;
+ AudioNode waves;
+ LowPassFilter underWaterAudioFilter = new LowPassFilter(0.5f, 0.1f);
+ LowPassFilter underWaterReverbFilter = new LowPassFilter(0.5f, 0.1f);
+ LowPassFilter aboveWaterAudioFilter = new LowPassFilter(1, 1);
+
+ public static void main(String[] args) {
+ TestPostWater app = new TestPostWater();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+
+ setDisplayFps(false);
+ setDisplayStatView(false);
+
+ Node mainScene = new Node("Main Scene");
+ rootNode.attachChild(mainScene);
+
+ createTerrain(mainScene);
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(lightDir);
+ sun.setColor(ColorRGBA.White.clone().multLocal(1.7f));
+ mainScene.addLight(sun);
+
+ DirectionalLight l = new DirectionalLight();
+ l.setDirection(Vector3f.UNIT_Y.mult(-1));
+ l.setColor(ColorRGBA.White.clone().multLocal(0.3f));
+// mainScene.addLight(l);
+
+ flyCam.setMoveSpeed(50);
+
+ //cam.setLocation(new Vector3f(-700, 100, 300));
+ //cam.setRotation(new Quaternion().fromAngleAxis(0.5f, Vector3f.UNIT_Z));
+ cam.setLocation(new Vector3f(-327.21957f, 61.6459f, 126.884346f));
+ cam.setRotation(new Quaternion(0.052168474f, 0.9443102f, -0.18395276f, 0.2678024f));
+
+
+ cam.setRotation(new Quaternion().fromAngles(new float[]{FastMath.PI * 0.06f, FastMath.PI * 0.65f, 0}));
+
+
+ Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false);
+ sky.setLocalScale(350);
+
+ mainScene.attachChild(sky);
+ cam.setFrustumFar(4000);
+ //cam.setFrustumNear(100);
+
+
+
+ //private FilterPostProcessor fpp;
+
+
+ water = new WaterFilter(rootNode, lightDir);
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+
+ fpp.addFilter(water);
+ BloomFilter bloom=new BloomFilter();
+ //bloom.getE
+ bloom.setExposurePower(55);
+ bloom.setBloomIntensity(1.0f);
+ fpp.addFilter(bloom);
+ LightScatteringFilter lsf = new LightScatteringFilter(lightDir.mult(-300));
+ lsf.setLightDensity(1.0f);
+ fpp.addFilter(lsf);
+ DepthOfFieldFilter dof=new DepthOfFieldFilter();
+ dof.setFocusDistance(0);
+ dof.setFocusRange(100);
+ fpp.addFilter(dof);
+//
+
+ // fpp.addFilter(new TranslucentBucketFilter());
+ //
+
+ // fpp.setNumSamples(4);
+
+
+ water.setWaveScale(0.003f);
+ water.setMaxAmplitude(2f);
+ water.setFoamExistence(new Vector3f(1f, 4, 0.5f));
+ water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam2.jpg"));
+ //water.setNormalScale(0.5f);
+
+ //water.setRefractionConstant(0.25f);
+ water.setRefractionStrength(0.2f);
+ //water.setFoamHardness(0.6f);
+
+ water.setWaterHeight(initialWaterHeight);
+ uw=cam.getLocation().y<waterHeight;
+
+ waves = new AudioNode(audioRenderer, assetManager, "Sound/Environment/Ocean Waves.ogg", false);
+ waves.setLooping(true);
+ waves.setReverbEnabled(true);
+ if(uw){
+ waves.setDryFilter(new LowPassFilter(0.5f, 0.1f));
+ }else{
+ waves.setDryFilter(aboveWaterAudioFilter);
+ }
+ audioRenderer.playSource(waves);
+ //
+ viewPort.addProcessor(fpp);
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (isPressed) {
+ if (name.equals("foam1")) {
+ water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam.jpg"));
+ }
+ if (name.equals("foam2")) {
+ water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam2.jpg"));
+ }
+ if (name.equals("foam3")) {
+ water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam3.jpg"));
+ }
+ }
+ }
+ }, "foam1", "foam2", "foam3");
+ inputManager.addMapping("foam1", new KeyTrigger(keyInput.KEY_1));
+ inputManager.addMapping("foam2", new KeyTrigger(keyInput.KEY_2));
+ inputManager.addMapping("foam3", new KeyTrigger(keyInput.KEY_3));
+// createBox();
+ // createFire();
+ }
+ Geometry box;
+
+ private void createBox() {
+ //creating a transluscent box
+ box = new Geometry("box", new Box(new Vector3f(0, 0, 0), 50, 50, 50));
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setColor("Color", new ColorRGBA(1.0f, 0, 0, 0.3f));
+ mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ //mat.getAdditionalRenderState().setDepthWrite(false);
+ //mat.getAdditionalRenderState().setDepthTest(false);
+ box.setMaterial(mat);
+ box.setQueueBucket(Bucket.Translucent);
+
+
+ //creating a post view port
+// ViewPort post=renderManager.createPostView("transpPost", cam);
+// post.setClearFlags(false, true, true);
+
+
+ box.setLocalTranslation(-600, 0, 300);
+
+ //attaching the box to the post viewport
+ //Don't forget to updateGeometricState() the box in the simpleUpdate
+ // post.attachScene(box);
+
+ rootNode.attachChild(box);
+ }
+
+ private void createFire() {
+ /** Uses Texture from jme3-test-data library! */
+ ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
+ Material mat_red = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+ mat_red.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+
+ fire.setMaterial(mat_red);
+ fire.setImagesX(2);
+ fire.setImagesY(2); // 2x2 texture animation
+ fire.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); // red
+ fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+ fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
+ fire.setStartSize(10f);
+ fire.setEndSize(1f);
+ fire.setGravity(0, 0, 0);
+ fire.setLowLife(0.5f);
+ fire.setHighLife(1.5f);
+ fire.getParticleInfluencer().setVelocityVariation(0.3f);
+ fire.setLocalTranslation(-350, 40, 430);
+
+ fire.setQueueBucket(Bucket.Transparent);
+ rootNode.attachChild(fire);
+ }
+
+ private void createTerrain(Node rootNode) {
+ matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+ matRock.setBoolean("useTriPlanarMapping", false);
+ matRock.setBoolean("WardIso", true);
+ matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+ Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap", grass);
+ matRock.setFloat("DiffuseMap_0_scale", 64);
+ Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ dirt.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_1", dirt);
+ matRock.setFloat("DiffuseMap_1_scale", 16);
+ Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ rock.setWrap(WrapMode.Repeat);
+ matRock.setTexture("DiffuseMap_2", rock);
+ matRock.setFloat("DiffuseMap_2_scale", 128);
+ Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
+ normalMap0.setWrap(WrapMode.Repeat);
+ Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
+ normalMap1.setWrap(WrapMode.Repeat);
+ Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
+ normalMap2.setWrap(WrapMode.Repeat);
+ matRock.setTexture("NormalMap", normalMap0);
+ matRock.setTexture("NormalMap_1", normalMap2);
+ matRock.setTexture("NormalMap_2", normalMap2);
+
+ AbstractHeightMap heightmap = null;
+ try {
+ heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
+ heightmap.load();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+ List<Camera> cameras = new ArrayList<Camera>();
+ cameras.add(getCamera());
+ terrain.setMaterial(matRock);
+ terrain.setLocalScale(new Vector3f(5, 5, 5));
+ terrain.setLocalTranslation(new Vector3f(0, -30, 0));
+ terrain.setLocked(false); // unlock it so we can edit the height
+
+ terrain.setShadowMode(ShadowMode.Receive);
+ rootNode.attachChild(terrain);
+
+ }
+ //This part is to emulate tides, slightly varrying the height of the water plane
+ private float time = 0.0f;
+ private float waterHeight = 0.0f;
+ private float initialWaterHeight = 0.8f;
+private boolean uw=false;
+ @Override
+ public void simpleUpdate(float tpf) {
+ super.simpleUpdate(tpf);
+ // box.updateGeometricState();
+ time += tpf;
+ waterHeight = (float) Math.cos(((time * 0.6f) % FastMath.TWO_PI)) * 1.5f;
+ water.setWaterHeight(initialWaterHeight + waterHeight);
+ if(water.isUnderWater() && !uw){
+
+ waves.setDryFilter(new LowPassFilter(0.5f, 0.1f));
+ uw=true;
+ }
+ if(!water.isUnderWater() && uw){
+ uw=false;
+ //waves.setReverbEnabled(false);
+ waves.setDryFilter(new LowPassFilter(1, 1f));
+ //waves.setDryFilter(new LowPassFilter(1,1f));
+
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/water/TestPostWaterLake.java b/engine/src/test/jme3test/water/TestPostWaterLake.java
new file mode 100644
index 0000000..c12501e
--- /dev/null
+++ b/engine/src/test/jme3test/water/TestPostWaterLake.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.water;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.scene.Spatial;
+import com.jme3.util.SkyFactory;
+import com.jme3.water.WaterFilter;
+import java.io.File;
+
+public class TestPostWaterLake extends SimpleApplication {
+
+ // set default for applets
+ private static boolean useHttp = true;
+
+ public static void main(String[] args) {
+
+ TestPostWaterLake app = new TestPostWaterLake();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ this.flyCam.setMoveSpeed(10);
+ cam.setLocation(new Vector3f(-27.0f, 1.0f, 75.0f));
+ // cam.setRotation(new Quaternion(0.03f, 0.9f, 0f, 0.4f));
+
+ // load sky
+ rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+ File file = new File("wildhouse.zip");
+
+ if (file.exists()) {
+ useHttp = false;
+ }
+ // create the geometry and attach it
+ // load the level from zip or http zip
+ if (useHttp) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", HttpZipLocator.class.getName());
+ } else {
+ assetManager.registerLocator("wildhouse.zip", ZipLocator.class.getName());
+ }
+ Spatial scene = assetManager.loadModel("main.scene");
+ rootNode.attachChild(scene);
+
+ DirectionalLight sun = new DirectionalLight();
+ Vector3f lightDir = new Vector3f(-0.37352666f, -0.50444174f, -0.7784704f);
+ sun.setDirection(lightDir);
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ scene.addLight(sun);
+
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ final WaterFilter water = new WaterFilter(rootNode, lightDir);
+ water.setWaterHeight(-20);
+ water.setUseFoam(false);
+ water.setUseRipples(false);
+ water.setDeepWaterColor(ColorRGBA.Brown);
+ water.setWaterColor(ColorRGBA.Brown.mult(2.0f));
+ water.setWaterTransparency(0.2f);
+ water.setMaxAmplitude(0.3f);
+ water.setWaveScale(0.008f);
+ water.setSpeed(0.7f);
+ water.setShoreHardness(1.0f);
+ water.setRefractionConstant(0.2f);
+ water.setShininess(0.3f);
+ water.setSunScale(1.0f);
+ water.setColorExtinction(new Vector3f(10.0f, 20.0f, 30.0f));
+ fpp.addFilter(water);
+ viewPort.addProcessor(fpp);
+
+ inputManager.addListener(new ActionListener() {
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if(isPressed){
+ if(water.isUseHQShoreline()){
+ water.setUseHQShoreline(false);
+ }else{
+ water.setUseHQShoreline(true);
+ }
+ }
+ }
+ }, "HQ");
+
+ inputManager.addMapping("HQ", new KeyTrigger(keyInput.KEY_SPACE));
+ }
+}
diff --git a/engine/src/test/jme3test/water/TestSceneWater.java b/engine/src/test/jme3test/water/TestSceneWater.java
new file mode 100644
index 0000000..8a96877
--- /dev/null
+++ b/engine/src/test/jme3test/water/TestSceneWater.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.water;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.SkyFactory;
+import com.jme3.water.SimpleWaterProcessor;
+import java.io.File;
+
+public class TestSceneWater extends SimpleApplication {
+
+ // set default for applets
+ private static boolean useHttp = true;
+
+ public static void main(String[] args) {
+
+ TestSceneWater app = new TestSceneWater();
+ app.start();
+ }
+
+ public void simpleInitApp() {
+ this.flyCam.setMoveSpeed(10);
+ Node mainScene=new Node();
+ cam.setLocation(new Vector3f(-27.0f, 1.0f, 75.0f));
+ cam.setRotation(new Quaternion(0.03f, 0.9f, 0f, 0.4f));
+
+ // load sky
+ mainScene.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+
+
+ File file = new File("wildhouse.zip");
+ if (file.exists()) {
+ useHttp = false;
+ }
+ // create the geometry and attach it
+ // load the level from zip or http zip
+ if (useHttp) {
+ assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", HttpZipLocator.class.getName());
+ } else {
+ assetManager.registerLocator("wildhouse.zip", ZipLocator.class.getName());
+ }
+ Spatial scene = assetManager.loadModel("main.scene");
+
+ DirectionalLight sun = new DirectionalLight();
+ Vector3f lightDir=new Vector3f(-0.37352666f, -0.50444174f, -0.7784704f);
+ sun.setDirection(lightDir);
+ sun.setColor(ColorRGBA.White.clone().multLocal(2));
+ scene.addLight(sun);
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ //add lightPos Geometry
+ Sphere lite=new Sphere(8, 8, 3.0f);
+ Geometry lightSphere=new Geometry("lightsphere", lite);
+ lightSphere.setMaterial(mat);
+ Vector3f lightPos=lightDir.multLocal(-400);
+ lightSphere.setLocalTranslation(lightPos);
+ rootNode.attachChild(lightSphere);
+
+
+ SimpleWaterProcessor waterProcessor = new SimpleWaterProcessor(assetManager);
+ waterProcessor.setReflectionScene(mainScene);
+ waterProcessor.setDebug(false);
+ waterProcessor.setLightPosition(lightPos);
+ waterProcessor.setRefractionClippingOffset(1.0f);
+
+
+ //setting the water plane
+ Vector3f waterLocation=new Vector3f(0,-20,0);
+ waterProcessor.setPlane(new Plane(Vector3f.UNIT_Y, waterLocation.dot(Vector3f.UNIT_Y)));
+ WaterUI waterUi=new WaterUI(inputManager, waterProcessor);
+ waterProcessor.setWaterColor(ColorRGBA.Brown);
+ waterProcessor.setDebug(true);
+ //lower render size for higher performance
+// waterProcessor.setRenderSize(128,128);
+ //raise depth to see through water
+// waterProcessor.setWaterDepth(20);
+ //lower the distortion scale if the waves appear too strong
+// waterProcessor.setDistortionScale(0.1f);
+ //lower the speed of the waves if they are too fast
+// waterProcessor.setWaveSpeed(0.01f);
+
+ Quad quad = new Quad(400,400);
+
+ //the texture coordinates define the general size of the waves
+ quad.scaleTextureCoordinates(new Vector2f(6f,6f));
+
+ Geometry water=new Geometry("water", quad);
+ water.setShadowMode(ShadowMode.Receive);
+ water.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
+ water.setMaterial(waterProcessor.getMaterial());
+ water.setLocalTranslation(-200, -20, 250);
+
+ rootNode.attachChild(water);
+
+ viewPort.addProcessor(waterProcessor);
+
+ mainScene.attachChild(scene);
+ rootNode.attachChild(mainScene);
+ }
+}
diff --git a/engine/src/test/jme3test/water/TestSimpleWater.java b/engine/src/test/jme3test/water/TestSimpleWater.java
new file mode 100644
index 0000000..de32923
--- /dev/null
+++ b/engine/src/test/jme3test/water/TestSimpleWater.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.water;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.util.SkyFactory;
+import com.jme3.water.SimpleWaterProcessor;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class TestSimpleWater extends SimpleApplication implements ActionListener {
+
+ Material mat;
+ Spatial waterPlane;
+ Geometry lightSphere;
+ SimpleWaterProcessor waterProcessor;
+ Node sceneNode;
+ boolean useWater = true;
+ private Vector3f lightPos = new Vector3f(33,12,-29);
+
+
+ public static void main(String[] args) {
+ TestSimpleWater app = new TestSimpleWater();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ initInput();
+ initScene();
+
+ //create processor
+ waterProcessor = new SimpleWaterProcessor(assetManager);
+ waterProcessor.setReflectionScene(sceneNode);
+ waterProcessor.setDebug(true);
+ viewPort.addProcessor(waterProcessor);
+
+ waterProcessor.setLightPosition(lightPos);
+
+ //create water quad
+ //waterPlane = waterProcessor.createWaterGeometry(100, 100);
+ waterPlane=(Spatial) assetManager.loadAsset("Models/WaterTest/WaterTest.mesh.xml");
+ waterPlane.setMaterial(waterProcessor.getMaterial());
+ waterPlane.setLocalScale(40);
+ waterPlane.setLocalTranslation(-5, 0, 5);
+
+ rootNode.attachChild(waterPlane);
+ }
+
+ private void initScene() {
+ //init cam location
+ cam.setLocation(new Vector3f(0, 10, 10));
+ cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+ //init scene
+ sceneNode = new Node("Scene");
+ mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Box b = new Box(1, 1, 1);
+ Geometry geom = new Geometry("Box", b);
+ geom.setMaterial(mat);
+ sceneNode.attachChild(geom);
+
+ // load sky
+ sceneNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+ rootNode.attachChild(sceneNode);
+
+ //add lightPos Geometry
+ Sphere lite=new Sphere(8, 8, 3.0f);
+ lightSphere=new Geometry("lightsphere", lite);
+ lightSphere.setMaterial(mat);
+ lightSphere.setLocalTranslation(lightPos);
+ rootNode.attachChild(lightSphere);
+ }
+
+ protected void initInput() {
+ flyCam.setMoveSpeed(3);
+ //init input
+ inputManager.addMapping("use_water", new KeyTrigger(KeyInput.KEY_O));
+ inputManager.addListener(this, "use_water");
+ inputManager.addMapping("lightup", new KeyTrigger(KeyInput.KEY_T));
+ inputManager.addListener(this, "lightup");
+ inputManager.addMapping("lightdown", new KeyTrigger(KeyInput.KEY_G));
+ inputManager.addListener(this, "lightdown");
+ inputManager.addMapping("lightleft", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addListener(this, "lightleft");
+ inputManager.addMapping("lightright", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addListener(this, "lightright");
+ inputManager.addMapping("lightforward", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addListener(this, "lightforward");
+ inputManager.addMapping("lightback", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addListener(this, "lightback");
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ fpsText.setText("Light Position: "+lightPos.toString()+" Change Light position with [U], [H], [J], [K] and [T], [G] Turn off water with [O]");
+ lightSphere.setLocalTranslation(lightPos);
+ waterProcessor.setLightPosition(lightPos);
+ }
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (name.equals("use_water") && value) {
+ if (!useWater) {
+ useWater = true;
+ waterPlane.setMaterial(waterProcessor.getMaterial());
+ } else {
+ useWater = false;
+ waterPlane.setMaterial(mat);
+ }
+ } else if (name.equals("lightup") && value) {
+ lightPos.y++;
+ } else if (name.equals("lightdown") && value) {
+ lightPos.y--;
+ } else if (name.equals("lightleft") && value) {
+ lightPos.x--;
+ } else if (name.equals("lightright") && value) {
+ lightPos.x++;
+ } else if (name.equals("lightforward") && value) {
+ lightPos.z--;
+ } else if (name.equals("lightback") && value) {
+ lightPos.z++;
+ }
+ }
+}
diff --git a/engine/src/test/jme3test/water/WaterUI.java b/engine/src/test/jme3test/water/WaterUI.java
new file mode 100644
index 0000000..855b53f
--- /dev/null
+++ b/engine/src/test/jme3test/water/WaterUI.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.water;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.water.SimpleWaterProcessor;
+
+/**
+ *
+ * @author nehon
+ */
+public class WaterUI {
+ private SimpleWaterProcessor processor;
+ public WaterUI(InputManager inputManager, SimpleWaterProcessor proc) {
+ processor=proc;
+
+
+ System.out.println("----------------- SSAO UI Debugger --------------------");
+ System.out.println("-- Water transparency : press Y to increase, H to decrease");
+ System.out.println("-- Water depth : press U to increase, J to decrease");
+// System.out.println("-- AO scale : press I to increase, K to decrease");
+// System.out.println("-- AO bias : press O to increase, P to decrease");
+// System.out.println("-- Toggle AO on/off : press space bar");
+// System.out.println("-- Use only AO : press Num pad 0");
+// System.out.println("-- Output config declaration : press P");
+ System.out.println("-------------------------------------------------------");
+
+ inputManager.addMapping("transparencyUp", new KeyTrigger(KeyInput.KEY_Y));
+ inputManager.addMapping("transparencyDown", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("depthUp", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("depthDown", new KeyTrigger(KeyInput.KEY_J));
+// inputManager.addMapping("scaleUp", new KeyTrigger(KeyInput.KEY_I));
+// inputManager.addMapping("scaleDown", new KeyTrigger(KeyInput.KEY_K));
+// inputManager.addMapping("biasUp", new KeyTrigger(KeyInput.KEY_O));
+// inputManager.addMapping("biasDown", new KeyTrigger(KeyInput.KEY_L));
+// inputManager.addMapping("outputConfig", new KeyTrigger(KeyInput.KEY_P));
+// inputManager.addMapping("toggleUseAO", new KeyTrigger(KeyInput.KEY_SPACE));
+// inputManager.addMapping("toggleUseOnlyAo", new KeyTrigger(KeyInput.KEY_NUMPAD0));
+
+// ActionListener acl = new ActionListener() {
+//
+// public void onAction(String name, boolean keyPressed, float tpf) {
+//
+// if (name.equals("toggleUseAO") && keyPressed) {
+// ssaoConfig.setUseAo(!ssaoConfig.isUseAo());
+// System.out.println("use AO : "+ssaoConfig.isUseAo());
+// }
+// if (name.equals("toggleUseOnlyAo") && keyPressed) {
+// ssaoConfig.setUseOnlyAo(!ssaoConfig.isUseOnlyAo());
+// System.out.println("use Only AO : "+ssaoConfig.isUseOnlyAo());
+//
+// }
+// if (name.equals("outputConfig") && keyPressed) {
+// System.out.println("new SSAOConfig("+ssaoConfig.getSampleRadius()+"f,"+ssaoConfig.getIntensity()+"f,"+ssaoConfig.getScale()+"f,"+ssaoConfig.getBias()+"f,"+ssaoConfig.isUseOnlyAo()+","+ssaoConfig.isUseAo()+");");
+// }
+//
+// }
+// };
+
+ AnalogListener anl = new AnalogListener() {
+
+ public void onAnalog(String name, float value, float tpf) {
+ if (name.equals("transparencyUp")) {
+ processor.setWaterTransparency(processor.getWaterTransparency()+0.001f);
+ System.out.println("Water transparency : "+processor.getWaterTransparency());
+ }
+ if (name.equals("transparencyDown")) {
+ processor.setWaterTransparency(processor.getWaterTransparency()-0.001f);
+ System.out.println("Water transparency : "+processor.getWaterTransparency());
+ }
+ if (name.equals("depthUp")) {
+ processor.setWaterDepth(processor.getWaterDepth()+0.001f);
+ System.out.println("Water depth : "+processor.getWaterDepth());
+ }
+ if (name.equals("depthDown")) {
+ processor.setWaterDepth(processor.getWaterDepth()-0.001f);
+ System.out.println("Water depth : "+processor.getWaterDepth());
+ }
+
+ }
+ };
+ // inputManager.addListener(acl,"toggleUseAO","toggleUseOnlyAo","outputConfig");
+ inputManager.addListener(anl, "transparencyUp","transparencyDown","depthUp","depthDown");
+
+ }
+
+
+
+}
diff --git a/engine/src/tools/jme3tools/converters/Converter.java b/engine/src/tools/jme3tools/converters/Converter.java
new file mode 100644
index 0000000..8e8df56
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/Converter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters;
+
+import java.util.Map;
+
+public interface Converter<T> {
+ public T convert(T input, Map<String, String> params);
+}
diff --git a/engine/src/tools/jme3tools/converters/FolderConverter.java b/engine/src/tools/jme3tools/converters/FolderConverter.java
new file mode 100644
index 0000000..8dc9922
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/FolderConverter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.system.JmeSystem;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+public class FolderConverter {
+
+ private static AssetManager assetManager;
+ private static File sourceRoot;
+ private static JarOutputStream jarOut;
+ private static long time;
+
+ private static void process(File file) throws IOException{
+ String name = file.getName().replaceAll("[\\/\\.]", "_");
+ JarEntry entry = new JarEntry(name);
+ entry.setTime(time);
+
+ jarOut.putNextEntry(entry);
+ }
+
+ public static void main(String[] args) throws IOException{
+ if (args.length == 0){
+ System.out.println("Usage: java -jar FolderConverter <input folder>");
+ System.out.println();
+ System.out.println(" Converts files from input to output");
+ System.exit(1);
+ }
+
+ sourceRoot = new File(args[0]);
+
+ File jarFile = new File(sourceRoot.getParent(), sourceRoot.getName()+".jar");
+ FileOutputStream out = new FileOutputStream(jarFile);
+ jarOut = new JarOutputStream(out);
+
+ assetManager = JmeSystem.newAssetManager();
+ assetManager.registerLocator(sourceRoot.toString(),
+ "com.jme3.asset.plugins.FileSystemLocator");
+ for (File f : sourceRoot.listFiles()){
+ process(f);
+ }
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/RGB565.java b/engine/src/tools/jme3tools/converters/RGB565.java
new file mode 100644
index 0000000..c0e43f5
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/RGB565.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters;
+
+/**
+ *
+ * @author Kirill
+ */
+public class RGB565 {
+
+ public static short ARGB8_to_RGB565(int argb){
+ int a = (argb & 0xFF000000) >> 24;
+ int r = (argb & 0x00FF0000) >> 16;
+ int g = (argb & 0x0000FF00) >> 8;
+ int b = (argb & 0x000000FF);
+
+ r = r >> 3;
+ g = g >> 2;
+ b = b >> 3;
+
+ return (short) (b | (g << 5) | (r << (5 + 6)));
+ }
+
+ public static int RGB565_to_ARGB8(short rgb565){
+ int a = 0xff;
+ int r = (rgb565 & 0xf800) >> 11;
+ int g = (rgb565 & 0x07e0) >> 5;
+ int b = (rgb565 & 0x001f);
+
+ r = r << 3;
+ g = g << 2;
+ b = b << 3;
+
+ return (a << 24) | (r << 16) | (g << 8) | (b);
+ }
+
+
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/FloatToFixed.java b/engine/src/tools/jme3tools/converters/model/FloatToFixed.java
new file mode 100644
index 0000000..0d0647a
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/FloatToFixed.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import java.nio.*;
+
+public class FloatToFixed {
+
+ private static final float shortSize = Short.MAX_VALUE - Short.MIN_VALUE;
+ private static final float shortOff = (Short.MAX_VALUE + Short.MIN_VALUE) * 0.5f;
+
+ private static final float byteSize = Byte.MAX_VALUE - Byte.MIN_VALUE;
+ private static final float byteOff = (Byte.MAX_VALUE + Byte.MIN_VALUE) * 0.5f;
+
+ public static void convertToFixed(Geometry geom, Format posFmt, Format nmFmt, Format tcFmt){
+ geom.updateModelBound();
+ BoundingBox bbox = (BoundingBox) geom.getModelBound();
+ Mesh mesh = geom.getMesh();
+
+ VertexBuffer positions = mesh.getBuffer(Type.Position);
+ VertexBuffer normals = mesh.getBuffer(Type.Normal);
+ VertexBuffer texcoords = mesh.getBuffer(Type.TexCoord);
+ VertexBuffer indices = mesh.getBuffer(Type.Index);
+
+ // positions
+ FloatBuffer fb = (FloatBuffer) positions.getData();
+ if (posFmt != Format.Float){
+ Buffer newBuf = VertexBuffer.createBuffer(posFmt, positions.getNumComponents(),
+ mesh.getVertexCount());
+ Transform t = convertPositions(fb, bbox, newBuf);
+ t.combineWithParent(geom.getLocalTransform());
+ geom.setLocalTransform(t);
+
+ VertexBuffer newPosVb = new VertexBuffer(Type.Position);
+ newPosVb.setupData(positions.getUsage(),
+ positions.getNumComponents(),
+ posFmt,
+ newBuf);
+ mesh.clearBuffer(Type.Position);
+ mesh.setBuffer(newPosVb);
+ }
+
+ // normals, automatically convert to signed byte
+ fb = (FloatBuffer) normals.getData();
+
+ ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity());
+ convertNormals(fb, bb);
+
+ normals = new VertexBuffer(Type.Normal);
+ normals.setupData(Usage.Static, 3, Format.Byte, bb);
+ normals.setNormalized(true);
+ mesh.clearBuffer(Type.Normal);
+ mesh.setBuffer(normals);
+
+ // texcoords
+ fb = (FloatBuffer) texcoords.getData();
+ if (tcFmt != Format.Float){
+ Buffer newBuf = VertexBuffer.createBuffer(tcFmt,
+ texcoords.getNumComponents(),
+ mesh.getVertexCount());
+ convertTexCoords2D(fb, newBuf);
+
+ VertexBuffer newTcVb = new VertexBuffer(Type.TexCoord);
+ newTcVb.setupData(texcoords.getUsage(),
+ texcoords.getNumComponents(),
+ tcFmt,
+ newBuf);
+ mesh.clearBuffer(Type.TexCoord);
+ mesh.setBuffer(newTcVb);
+ }
+ }
+
+ public static void compressIndexBuffer(Mesh mesh){
+ int vertCount = mesh.getVertexCount();
+ VertexBuffer vb = mesh.getBuffer(Type.Index);
+ Format targetFmt;
+ if (vb.getFormat() == Format.UnsignedInt && vertCount <= 0xffff){
+ if (vertCount <= 256)
+ targetFmt = Format.UnsignedByte;
+ else
+ targetFmt = Format.UnsignedShort;
+ }else if (vb.getFormat() == Format.UnsignedShort && vertCount <= 0xff){
+ targetFmt = Format.UnsignedByte;
+ }else{
+ return;
+ }
+
+ IndexBuffer src = mesh.getIndexBuffer();
+ Buffer newBuf = VertexBuffer.createBuffer(targetFmt, vb.getNumComponents(), src.size());
+
+ VertexBuffer newVb = new VertexBuffer(Type.Index);
+ newVb.setupData(vb.getUsage(), vb.getNumComponents(), targetFmt, newBuf);
+ mesh.clearBuffer(Type.Index);
+ mesh.setBuffer(newVb);
+
+ IndexBuffer dst = mesh.getIndexBuffer();
+ for (int i = 0; i < src.size(); i++){
+ dst.put(i, src.get(i));
+ }
+ }
+
+ private static void convertToFixed(FloatBuffer input, IntBuffer output){
+ if (output.capacity() < input.capacity())
+ throw new RuntimeException("Output must be at least as large as input!");
+
+ input.clear();
+ output.clear();
+ for (int i = 0; i < input.capacity(); i++){
+ output.put( (int) (input.get() * (float)(1<<16)) );
+ }
+ output.flip();
+ }
+
+ private static void convertToFloat(IntBuffer input, FloatBuffer output){
+ if (output.capacity() < input.capacity())
+ throw new RuntimeException("Output must be at least as large as input!");
+
+ input.clear();
+ output.clear();
+ for (int i = 0; i < input.capacity(); i++){
+ output.put( ((float)input.get() / (float)(1<<16)) );
+ }
+ output.flip();
+ }
+
+ private static void convertToUByte(FloatBuffer input, ByteBuffer output){
+ if (output.capacity() < input.capacity())
+ throw new RuntimeException("Output must be at least as large as input!");
+
+ input.clear();
+ output.clear();
+ for (int i = 0; i < input.capacity(); i++){
+ output.put( (byte) (input.get() * 255f) );
+ }
+ output.flip();
+ }
+
+
+ public static VertexBuffer convertToUByte(VertexBuffer vb){
+ FloatBuffer fb = (FloatBuffer) vb.getData();
+ ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity());
+ convertToUByte(fb, bb);
+
+ VertexBuffer newVb = new VertexBuffer(vb.getBufferType());
+ newVb.setupData(vb.getUsage(),
+ vb.getNumComponents(),
+ Format.UnsignedByte,
+ bb);
+ newVb.setNormalized(true);
+ return newVb;
+ }
+
+ public static VertexBuffer convertToFixed(VertexBuffer vb){
+ if (vb.getFormat() == Format.Int)
+ return vb;
+
+ FloatBuffer fb = (FloatBuffer) vb.getData();
+ IntBuffer ib = BufferUtils.createIntBuffer(fb.capacity());
+ convertToFixed(fb, ib);
+
+ VertexBuffer newVb = new VertexBuffer(vb.getBufferType());
+ newVb.setupData(vb.getUsage(),
+ vb.getNumComponents(),
+ Format.Int,
+ ib);
+ return newVb;
+ }
+
+ public static VertexBuffer convertToFloat(VertexBuffer vb){
+ if (vb.getFormat() == Format.Float)
+ return vb;
+
+ IntBuffer ib = (IntBuffer) vb.getData();
+ FloatBuffer fb = BufferUtils.createFloatBuffer(ib.capacity());
+ convertToFloat(ib, fb);
+
+ VertexBuffer newVb = new VertexBuffer(vb.getBufferType());
+ newVb.setupData(vb.getUsage(),
+ vb.getNumComponents(),
+ Format.Float,
+ fb);
+ return newVb;
+ }
+
+ private static void convertNormals(FloatBuffer input, ByteBuffer output){
+ if (output.capacity() < input.capacity())
+ throw new RuntimeException("Output must be at least as large as input!");
+
+ input.clear();
+ output.clear();
+ Vector3f temp = new Vector3f();
+ int vertexCount = input.capacity() / 3;
+ for (int i = 0; i < vertexCount; i++){
+ BufferUtils.populateFromBuffer(temp, input, i);
+
+ // offset and scale vector into -128 ... 127
+ temp.multLocal(127).addLocal(0.5f, 0.5f, 0.5f);
+
+ // quantize
+ byte v1 = (byte) temp.getX();
+ byte v2 = (byte) temp.getY();
+ byte v3 = (byte) temp.getZ();
+
+ // store
+ output.put(v1).put(v2).put(v3);
+ }
+ }
+
+ private static void convertTexCoords2D(FloatBuffer input, Buffer output){
+ if (output.capacity() < input.capacity())
+ throw new RuntimeException("Output must be at least as large as input!");
+
+ input.clear();
+ output.clear();
+ Vector2f temp = new Vector2f();
+ int vertexCount = input.capacity() / 2;
+
+ ShortBuffer sb = null;
+ IntBuffer ib = null;
+
+ if (output instanceof ShortBuffer)
+ sb = (ShortBuffer) output;
+ else if (output instanceof IntBuffer)
+ ib = (IntBuffer) output;
+ else
+ throw new UnsupportedOperationException();
+
+ for (int i = 0; i < vertexCount; i++){
+ BufferUtils.populateFromBuffer(temp, input, i);
+
+ if (sb != null){
+ sb.put( (short) (temp.getX()*Short.MAX_VALUE) );
+ sb.put( (short) (temp.getY()*Short.MAX_VALUE) );
+ }else{
+ int v1 = (int) (temp.getX() * ((float)(1 << 16)));
+ int v2 = (int) (temp.getY() * ((float)(1 << 16)));
+ ib.put(v1).put(v2);
+ }
+ }
+ }
+
+ private static Transform convertPositions(FloatBuffer input, BoundingBox bbox, Buffer output){
+ if (output.capacity() < input.capacity())
+ throw new RuntimeException("Output must be at least as large as input!");
+
+ Vector3f offset = bbox.getCenter().negate();
+ Vector3f size = new Vector3f(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
+ size.multLocal(2);
+
+ ShortBuffer sb = null;
+ ByteBuffer bb = null;
+ float dataTypeSize;
+ float dataTypeOffset;
+ if (output instanceof ShortBuffer){
+ sb = (ShortBuffer) output;
+ dataTypeOffset = shortOff;
+ dataTypeSize = shortSize;
+ }else{
+ bb = (ByteBuffer) output;
+ dataTypeOffset = byteOff;
+ dataTypeSize = byteSize;
+ }
+ Vector3f scale = new Vector3f();
+ scale.set(dataTypeSize, dataTypeSize, dataTypeSize).divideLocal(size);
+
+ Vector3f invScale = new Vector3f();
+ invScale.set(size).divideLocal(dataTypeSize);
+
+ offset.multLocal(scale);
+ offset.addLocal(dataTypeOffset, dataTypeOffset, dataTypeOffset);
+
+ // offset = (-modelOffset * shortSize)/modelSize + shortOff
+ // scale = shortSize / modelSize
+
+ input.clear();
+ output.clear();
+ Vector3f temp = new Vector3f();
+ int vertexCount = input.capacity() / 3;
+ for (int i = 0; i < vertexCount; i++){
+ BufferUtils.populateFromBuffer(temp, input, i);
+
+ // offset and scale vector into -32768 ... 32767
+ // or into -128 ... 127 if using bytes
+ temp.multLocal(scale);
+ temp.addLocal(offset);
+
+ // quantize and store
+ if (sb != null){
+ short v1 = (short) temp.getX();
+ short v2 = (short) temp.getY();
+ short v3 = (short) temp.getZ();
+ sb.put(v1).put(v2).put(v3);
+ }else{
+ byte v1 = (byte) temp.getX();
+ byte v2 = (byte) temp.getY();
+ byte v3 = (byte) temp.getZ();
+ bb.put(v1).put(v2).put(v3);
+ }
+ }
+
+ Transform transform = new Transform();
+ transform.setTranslation(offset.negate().multLocal(invScale));
+ transform.setScale(invScale);
+ return transform;
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/ModelConverter.java b/engine/src/tools/jme3tools/converters/model/ModelConverter.java
new file mode 100644
index 0000000..2539574
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/ModelConverter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model;
+
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.nio.Buffer;
+import java.util.Arrays;
+import java.util.Comparator;
+import jme3tools.converters.model.strip.PrimitiveGroup;
+import jme3tools.converters.model.strip.TriStrip;
+
+public class ModelConverter {
+
+ private static final class PrimComparator
+ implements Comparator<PrimitiveGroup> {
+
+ public int compare(PrimitiveGroup g1, PrimitiveGroup g2) {
+ if (g1.type < g2.type)
+ return -1;
+ else if (g1.type > g2.type)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+ private static final PrimComparator primComp = new PrimComparator();
+
+ public static void generateStrips(Mesh mesh, boolean stitch, boolean listOnly, int cacheSize, int minStripSize){
+ TriStrip ts = new TriStrip();
+ ts.setStitchStrips(stitch);
+ ts.setCacheSize(cacheSize);
+ ts.setListsOnly(listOnly);
+ ts.setMinStripSize(minStripSize);
+
+ IndexBuffer ib = mesh.getIndexBuffer();
+ int[] indices = new int[ib.size()];
+ for (int i = 0; i < indices.length; i++)
+ indices[i] = ib.get(i);
+
+ PrimitiveGroup[] groups = ts.generateStrips(indices);
+ Arrays.sort(groups, primComp);
+
+ int numElements = 0;
+ for (PrimitiveGroup group : groups)
+ numElements += group.numIndices;
+
+ VertexBuffer original = mesh.getBuffer(Type.Index);
+ Buffer buf = VertexBuffer.createBuffer(original.getFormat(),
+ original.getNumComponents(),
+ numElements);
+ original.updateData(buf);
+ ib = mesh.getIndexBuffer();
+
+ int curIndex = 0;
+ int[] modeStart = new int[]{ -1, -1, -1 };
+ int[] elementLengths = new int[groups.length];
+ for (int i = 0; i < groups.length; i++){
+ PrimitiveGroup group = groups[i];
+ elementLengths[i] = group.numIndices;
+
+ if (modeStart[group.type] == -1){
+ modeStart[group.type] = i;
+ }
+
+ int[] trimmedIndices = group.getTrimmedIndices();
+ for (int j = 0; j < trimmedIndices.length; j++){
+ ib.put(curIndex + j, trimmedIndices[j]);
+ }
+
+ curIndex += group.numIndices;
+ }
+
+ if (modeStart[0] == -1 && modeStart[1] == 0 && modeStart[2] == -1 &&
+ elementLengths.length == 1){
+ original.compact(elementLengths[0]);
+ mesh.setMode(Mode.TriangleStrip);
+ }else{
+ mesh.setElementLengths(elementLengths);
+ mesh.setModeStart(modeStart);
+ mesh.setMode(Mode.Hybrid);
+ }
+
+ mesh.updateCounts();
+ }
+
+ public static void optimize(Mesh mesh, boolean toFixed){
+ // update any data that need updating
+ mesh.updateBound();
+ mesh.updateCounts();
+
+ // set all buffers into STATIC_DRAW mode
+ mesh.setStatic();
+
+ if (mesh.getBuffer(Type.Index) != null){
+ // compress index buffer from UShort to UByte (if possible)
+ FloatToFixed.compressIndexBuffer(mesh);
+
+ // generate triangle strips stitched with degenerate tris
+ generateStrips(mesh, false, false, 16, 0);
+ }
+
+ IntMap<VertexBuffer> bufs = mesh.getBuffers();
+ for (Entry<VertexBuffer> entry : bufs){
+ VertexBuffer vb = entry.getValue();
+ if (vb == null || vb.getBufferType() == Type.Index)
+ continue;
+
+ if (vb.getFormat() == Format.Float){
+ if (vb.getBufferType() == Type.Color){
+ // convert the color buffer to UByte
+ vb = FloatToFixed.convertToUByte(vb);
+ vb.setNormalized(true);
+ }else if (toFixed){
+ // convert normals, positions, and texcoords
+ // to fixed-point (16.16)
+ vb = FloatToFixed.convertToFixed(vb);
+// vb = FloatToFixed.convertToFloat(vb);
+ }
+ mesh.clearBuffer(vb.getBufferType());
+ mesh.setBuffer(vb);
+ }
+ }
+ mesh.setInterleaved();
+ }
+
+ private static void optimizeScene(Spatial source, boolean toFixed){
+ if (source instanceof Geometry){
+ Geometry geom = (Geometry) source;
+ Mesh mesh = geom.getMesh();
+ optimize(mesh, toFixed);
+ }else if (source instanceof Node){
+ Node node = (Node) source;
+ for (int i = node.getQuantity() - 1; i >= 0; i--){
+ Spatial child = node.getChild(i);
+ optimizeScene(child, toFixed);
+ }
+ }
+ }
+
+ public static void optimize(Spatial source, boolean toFixed){
+ optimizeScene(source, toFixed);
+ source.updateLogicalState(0);
+ source.updateGeometricState();
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/EdgeInfo.java b/engine/src/tools/jme3tools/converters/model/strip/EdgeInfo.java
new file mode 100644
index 0000000..02b7350
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/EdgeInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+/**
+ *
+ */
+class EdgeInfo {
+
+ FaceInfo m_face0, m_face1;
+ int m_v0, m_v1;
+ EdgeInfo m_nextV0, m_nextV1;
+
+ public EdgeInfo(int v0, int v1) {
+ m_v0 = v0;
+ m_v1 = v1;
+ m_face0 = null;
+ m_face1 = null;
+ m_nextV0 = null;
+ m_nextV1 = null;
+
+ }
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/EdgeInfoVec.java b/engine/src/tools/jme3tools/converters/model/strip/EdgeInfoVec.java
new file mode 100644
index 0000000..f289d78
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/EdgeInfoVec.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+import java.util.ArrayList;
+
+class EdgeInfoVec extends ArrayList<EdgeInfo> {
+
+ private static final long serialVersionUID = 1L;
+
+ public EdgeInfoVec() {
+ super();
+ }
+
+ public EdgeInfo at(int index) {
+ return get(index);
+ }
+
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/FaceInfo.java b/engine/src/tools/jme3tools/converters/model/strip/FaceInfo.java
new file mode 100644
index 0000000..e917ec7
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/FaceInfo.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+
+class FaceInfo {
+
+ int m_v0, m_v1, m_v2;
+ int m_stripId; // real strip Id
+ int m_testStripId; // strip Id in an experiment
+ int m_experimentId; // in what experiment was it given an experiment Id?
+
+ public FaceInfo(int v0, int v1, int v2){
+ m_v0 = v0; m_v1 = v1; m_v2 = v2;
+ m_stripId = -1;
+ m_testStripId = -1;
+ m_experimentId = -1;
+ }
+
+ public void set(FaceInfo o) {
+ m_v0 = o.m_v0;
+ m_v1 = o.m_v1;
+ m_v2 = o.m_v2;
+
+ m_stripId = o.m_stripId;
+ m_testStripId = o.m_testStripId;
+ m_experimentId = o.m_experimentId;
+ }
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/FaceInfoVec.java b/engine/src/tools/jme3tools/converters/model/strip/FaceInfoVec.java
new file mode 100644
index 0000000..8319280
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/FaceInfoVec.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+import java.util.ArrayList;
+
+class FaceInfoVec extends ArrayList<FaceInfo> {
+
+
+ private static final long serialVersionUID = 1L;
+
+ public FaceInfoVec() {
+ super();
+ }
+
+ public FaceInfo at(int index) {
+ return get(index);
+ }
+
+ public void reserve(int i) {
+ super.ensureCapacity(i);
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/IntVec.java b/engine/src/tools/jme3tools/converters/model/strip/IntVec.java
new file mode 100644
index 0000000..7e7eecb
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/IntVec.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+
+
+public class IntVec {
+
+ private int[] data;
+ private int count = 0;
+
+ public IntVec() {
+ data = new int[16];
+ }
+
+ public IntVec(int startSize) {
+ data = new int[startSize];
+ }
+
+ public int size() {
+ return count;
+ }
+
+ public int get(int i) {
+ return data[i];
+ }
+
+ public void add(int val) {
+ if ( count == data.length ) {
+ int[] ndata = new int[count*2];
+ System.arraycopy(data,0,ndata,0,count);
+ data = ndata;
+ }
+ data[count] = val;
+ count++;
+ }
+
+ public void clear() {
+ count = 0;
+ }
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/PrimitiveGroup.java b/engine/src/tools/jme3tools/converters/model/strip/PrimitiveGroup.java
new file mode 100644
index 0000000..51697ee
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/PrimitiveGroup.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+/**
+ *
+ */
+public class PrimitiveGroup {
+
+ public static final int PT_LIST = 0;
+ public static final int PT_STRIP = 1;
+ public static final int PT_FAN = 2;
+
+ public int type;
+ public int[] indices;
+ public int numIndices;
+
+ public PrimitiveGroup() {
+ type = PT_STRIP;
+ }
+
+ public String getTypeString() {
+ switch(type) {
+ case PT_LIST : return "list";
+ case PT_STRIP: return "strip";
+ case PT_FAN: return "fan";
+ default: return "????";
+ }
+ }
+
+ public String toString() {
+ return getTypeString() + " : " + numIndices;
+ }
+
+ public String getFullInfo() {
+ if ( type != PT_STRIP )
+ return toString();
+
+ int[] stripLengths = new int[numIndices];
+
+ int prev = -1;
+ int length = -1;
+ for ( int i =0; i < numIndices; i++) {
+ if (indices[i] == prev) {
+ stripLengths[length]++;
+ length = -1;
+ prev = -1;
+ } else {
+ prev = indices[i];
+ length++;
+ }
+ }
+ stripLengths[length]++;
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("Strip:").append(numIndices).append("\n");
+ for ( int i =0; i < stripLengths.length; i++) {
+ if ( stripLengths[i] > 0) {
+ sb.append(i).append("->").append(stripLengths[i]).append("\n");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @return
+ */
+ public int[] getTrimmedIndices() {
+ if ( indices.length == numIndices )
+ return indices;
+ int[] nind = new int[numIndices];
+ System.arraycopy(indices,0,nind,0,numIndices);
+ return nind;
+ }
+
+}
+
diff --git a/engine/src/tools/jme3tools/converters/model/strip/StripInfo.java b/engine/src/tools/jme3tools/converters/model/strip/StripInfo.java
new file mode 100644
index 0000000..a9b1098
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/StripInfo.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+/**
+ *
+ */
+class StripInfo {
+
+ StripStartInfo m_startInfo;
+ FaceInfoVec m_faces = new FaceInfoVec();
+ int m_stripId;
+ int m_experimentId;
+
+ boolean visited;
+
+ int m_numDegenerates;
+
+
+ public StripInfo(StripStartInfo startInfo,int stripId, int experimentId) {
+
+ m_startInfo = startInfo;
+ m_stripId = stripId;
+ m_experimentId = experimentId;
+ visited = false;
+ m_numDegenerates = 0;
+ }
+
+ boolean isExperiment() {
+ return m_experimentId >= 0;
+ }
+
+ boolean isInStrip(FaceInfo faceInfo) {
+ if(faceInfo == null)
+ return false;
+
+ return (m_experimentId >= 0 ? faceInfo.m_testStripId == m_stripId : faceInfo.m_stripId == m_stripId);
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// IsMarked()
+//
+// If either the faceInfo has a real strip index because it is
+// already assign to a committed strip OR it is assigned in an
+// experiment and the experiment index is the one we are building
+// for, then it is marked and unavailable
+ boolean isMarked(FaceInfo faceInfo){
+ return (faceInfo.m_stripId >= 0) || (isExperiment() && faceInfo.m_experimentId == m_experimentId);
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// MarkTriangle()
+//
+// Marks the face with the current strip ID
+//
+ void markTriangle(FaceInfo faceInfo){
+ if (isExperiment()){
+ faceInfo.m_experimentId = m_experimentId;
+ faceInfo.m_testStripId = m_stripId;
+ }
+ else{
+ faceInfo.m_experimentId = -1;
+ faceInfo.m_stripId = m_stripId;
+ }
+ }
+
+
+ boolean unique(FaceInfoVec faceVec, FaceInfo face)
+ {
+ boolean bv0, bv1, bv2; //bools to indicate whether a vertex is in the faceVec or not
+ bv0 = bv1 = bv2 = false;
+
+ for(int i = 0; i < faceVec.size(); i++)
+ {
+ if(!bv0)
+ {
+ if( (faceVec.at(i).m_v0 == face.m_v0) ||
+ (faceVec.at(i).m_v1 == face.m_v0) ||
+ (faceVec.at(i).m_v2 == face.m_v0) )
+ bv0 = true;
+ }
+
+ if(!bv1)
+ {
+ if( (faceVec.at(i).m_v0 == face.m_v1) ||
+ (faceVec.at(i).m_v1 == face.m_v1) ||
+ (faceVec.at(i).m_v2 == face.m_v1) )
+ bv1 = true;
+ }
+
+ if(!bv2)
+ {
+ if( (faceVec.at(i).m_v0 == face.m_v2) ||
+ (faceVec.at(i).m_v1 == face.m_v2) ||
+ (faceVec.at(i).m_v2 == face.m_v2) )
+ bv2 = true;
+ }
+
+ //the face is not unique, all it's vertices exist in the face vector
+ if(bv0 && bv1 && bv2)
+ return false;
+ }
+
+ //if we get out here, it's unique
+ return true;
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Build()
+//
+// Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
+//
+ void build(EdgeInfoVec edgeInfos, FaceInfoVec faceInfos)
+ {
+ // used in building the strips forward and backward
+ IntVec scratchIndices = new IntVec();
+
+ // build forward... start with the initial face
+ FaceInfoVec forwardFaces = new FaceInfoVec();
+ FaceInfoVec backwardFaces = new FaceInfoVec();
+ forwardFaces.add(m_startInfo.m_startFace);
+
+ markTriangle(m_startInfo.m_startFace);
+
+ int v0 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge.m_v0 : m_startInfo.m_startEdge.m_v1);
+ int v1 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge.m_v1 : m_startInfo.m_startEdge.m_v0);
+
+ // easiest way to get v2 is to use this function which requires the
+ // other indices to already be in the list.
+ scratchIndices.add(v0);
+ scratchIndices.add(v1);
+ int v2 = Stripifier.getNextIndex(scratchIndices, m_startInfo.m_startFace);
+ scratchIndices.add(v2);
+
+ //
+ // build the forward list
+ //
+ int nv0 = v1;
+ int nv1 = v2;
+
+ FaceInfo nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
+ while (nextFace != null && !isMarked(nextFace))
+ {
+ //check to see if this next face is going to cause us to die soon
+ int testnv0 = nv1;
+ int testnv1 = Stripifier.getNextIndex(scratchIndices, nextFace);
+
+ FaceInfo nextNextFace = Stripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);
+
+ if( (nextNextFace == null) || (isMarked(nextNextFace)) )
+ {
+ //uh, oh, we're following a dead end, try swapping
+ FaceInfo testNextFace = Stripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
+
+ if( ((testNextFace != null) && !isMarked(testNextFace)) )
+ {
+ //we only swap if it buys us something
+
+ //add a "fake" degenerate face
+ FaceInfo tempFace = new FaceInfo(nv0, nv1, nv0);
+
+ forwardFaces.add(tempFace);
+ markTriangle(tempFace);
+
+ scratchIndices.add(nv0);
+ testnv0 = nv0;
+
+ ++m_numDegenerates;
+ }
+
+ }
+
+ // add this to the strip
+ forwardFaces.add(nextFace);
+
+ markTriangle(nextFace);
+
+ // add the index
+ //nv0 = nv1;
+ //nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
+ scratchIndices.add(testnv1);
+
+ // and get the next face
+ nv0 = testnv0;
+ nv1 = testnv1;
+
+ nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
+
+ }
+
+ // tempAllFaces is going to be forwardFaces + backwardFaces
+ // it's used for Unique()
+ FaceInfoVec tempAllFaces = new FaceInfoVec();
+ for(int i = 0; i < forwardFaces.size(); i++)
+ tempAllFaces.add(forwardFaces.at(i));
+
+ //
+ // reset the indices for building the strip backwards and do so
+ //
+ scratchIndices.clear();
+ scratchIndices.add(v2);
+ scratchIndices.add(v1);
+ scratchIndices.add(v0);
+ nv0 = v1;
+ nv1 = v0;
+ nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
+ while (nextFace != null && !isMarked(nextFace))
+ {
+ //this tests to see if a face is "unique", meaning that its vertices aren't already in the list
+ // so, strips which "wrap-around" are not allowed
+ if(!unique(tempAllFaces, nextFace))
+ break;
+
+ //check to see if this next face is going to cause us to die soon
+ int testnv0 = nv1;
+ int testnv1 = Stripifier.getNextIndex(scratchIndices, nextFace);
+
+ FaceInfo nextNextFace = Stripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);
+
+ if( (nextNextFace == null) || (isMarked(nextNextFace)) )
+ {
+ //uh, oh, we're following a dead end, try swapping
+ FaceInfo testNextFace = Stripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
+ if( ((testNextFace != null) && !isMarked(testNextFace)) )
+ {
+ //we only swap if it buys us something
+
+ //add a "fake" degenerate face
+ FaceInfo tempFace = new FaceInfo(nv0, nv1, nv0);
+
+ backwardFaces.add(tempFace);
+ markTriangle(tempFace);
+ scratchIndices.add(nv0);
+ testnv0 = nv0;
+
+ ++m_numDegenerates;
+ }
+
+ }
+
+ // add this to the strip
+ backwardFaces.add(nextFace);
+
+ //this is just so Unique() will work
+ tempAllFaces.add(nextFace);
+
+ markTriangle(nextFace);
+
+ // add the index
+ //nv0 = nv1;
+ //nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
+ scratchIndices.add(testnv1);
+
+ // and get the next face
+ nv0 = testnv0;
+ nv1 = testnv1;
+ nextFace = Stripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
+ }
+
+ // Combine the forward and backwards stripification lists and put into our own face vector
+ combine(forwardFaces, backwardFaces);
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Combine()
+//
+// Combines the two input face vectors and puts the result into m_faces
+//
+ void combine(FaceInfoVec forward, FaceInfoVec backward){
+
+ // add backward faces
+ int numFaces = backward.size();
+ for (int i = numFaces - 1; i >= 0; i--)
+ m_faces.add(backward.at(i));
+
+ // add forward faces
+ numFaces = forward.size();
+ for (int i = 0; i < numFaces; i++)
+ m_faces.add(forward.at(i));
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// SharesEdge()
+//
+// Returns true if the input face and the current strip share an edge
+//
+ boolean sharesEdge(FaceInfo faceInfo, EdgeInfoVec edgeInfos)
+ {
+ //check v0.v1 edge
+ EdgeInfo currEdge = Stripifier.findEdgeInfo(edgeInfos, faceInfo.m_v0, faceInfo.m_v1);
+
+ if(isInStrip(currEdge.m_face0) || isInStrip(currEdge.m_face1))
+ return true;
+
+ //check v1.v2 edge
+ currEdge = Stripifier.findEdgeInfo(edgeInfos, faceInfo.m_v1, faceInfo.m_v2);
+
+ if(isInStrip(currEdge.m_face0) || isInStrip(currEdge.m_face1))
+ return true;
+
+ //check v2.v0 edge
+ currEdge = Stripifier.findEdgeInfo(edgeInfos, faceInfo.m_v2, faceInfo.m_v0);
+
+ if(isInStrip(currEdge.m_face0) || isInStrip(currEdge.m_face1))
+ return true;
+
+ return false;
+
+ }
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/StripInfoVec.java b/engine/src/tools/jme3tools/converters/model/strip/StripInfoVec.java
new file mode 100644
index 0000000..58165f9
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/StripInfoVec.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+import java.util.ArrayList;
+
+
+class StripInfoVec extends ArrayList<StripInfo> {
+
+
+ private static final long serialVersionUID = 1L;
+
+ public StripInfoVec() {
+ super();
+ }
+
+ public StripInfo at(int index) {
+ return get(index);
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/StripStartInfo.java b/engine/src/tools/jme3tools/converters/model/strip/StripStartInfo.java
new file mode 100644
index 0000000..69f5157
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/StripStartInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+class StripStartInfo {
+
+
+ FaceInfo m_startFace;
+ EdgeInfo m_startEdge;
+ boolean m_toV1;
+
+
+ public StripStartInfo(FaceInfo startFace, EdgeInfo startEdge, boolean toV1){
+ m_startFace = startFace;
+ m_startEdge = startEdge;
+ m_toV1 = toV1;
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/Stripifier.java b/engine/src/tools/jme3tools/converters/model/strip/Stripifier.java
new file mode 100644
index 0000000..c8630fe
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/Stripifier.java
@@ -0,0 +1,1365 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+import java.util.HashSet;
+import java.util.logging.Logger;
+
+/**
+ *
+ */
+class Stripifier {
+ private static final Logger logger = Logger.getLogger(Stripifier.class
+ .getName());
+
+ public static int CACHE_INEFFICIENCY = 6;
+
+ IntVec indices = new IntVec();
+
+ int cacheSize;
+
+ int minStripLength;
+
+ float meshJump;
+
+ boolean bFirstTimeResetPoint;
+
+ Stripifier() {
+ super();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // FindEdgeInfo()
+ //
+ // find the edge info for these two indices
+ //
+ static EdgeInfo findEdgeInfo(EdgeInfoVec edgeInfos, int v0, int v1) {
+
+ // we can get to it through either array
+ // because the edge infos have a v0 and v1
+ // and there is no order except how it was
+ // first created.
+ EdgeInfo infoIter = edgeInfos.at(v0);
+ while (infoIter != null) {
+ if (infoIter.m_v0 == v0) {
+ if (infoIter.m_v1 == v1)
+ return infoIter;
+
+ infoIter = infoIter.m_nextV0;
+ } else {
+ if (infoIter.m_v0 == v1)
+ return infoIter;
+
+ infoIter = infoIter.m_nextV1;
+ }
+ }
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // FindOtherFace
+ //
+ // find the other face sharing these vertices
+ // exactly like the edge info above
+ //
+ static FaceInfo findOtherFace(EdgeInfoVec edgeInfos, int v0, int v1,
+ FaceInfo faceInfo) {
+ EdgeInfo edgeInfo = findEdgeInfo(edgeInfos, v0, v1);
+
+ if ((edgeInfo == null) || (v0 == v1)) {
+ //we've hit a degenerate
+ return null;
+ }
+
+ return (edgeInfo.m_face0 == faceInfo ? edgeInfo.m_face1
+ : edgeInfo.m_face0);
+ }
+
+ static boolean alreadyExists(FaceInfo faceInfo, FaceInfoVec faceInfos) {
+ for (int i = 0; i < faceInfos.size(); ++i) {
+ FaceInfo o = faceInfos.at(i);
+ if ((o.m_v0 == faceInfo.m_v0) && (o.m_v1 == faceInfo.m_v1)
+ && (o.m_v2 == faceInfo.m_v2))
+ return true;
+ }
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // BuildStripifyInfo()
+ //
+ // Builds the list of all face and edge infos
+ //
+ void buildStripifyInfo(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos,
+ int maxIndex) {
+ // reserve space for the face infos, but do not resize them.
+ int numIndices = indices.size();
+ faceInfos.reserve(numIndices / 3);
+
+ // we actually resize the edge infos, so we must initialize to null
+ for (int i = 0; i < maxIndex + 1; i++)
+ edgeInfos.add(null);
+
+ // iterate through the triangles of the triangle list
+ int numTriangles = numIndices / 3;
+ int index = 0;
+ boolean[] bFaceUpdated = new boolean[3];
+
+ for (int i = 0; i < numTriangles; i++) {
+ boolean bMightAlreadyExist = true;
+ bFaceUpdated[0] = false;
+ bFaceUpdated[1] = false;
+ bFaceUpdated[2] = false;
+
+ // grab the indices
+ int v0 = indices.get(index++);
+ int v1 = indices.get(index++);
+ int v2 = indices.get(index++);
+
+ //we disregard degenerates
+ if (isDegenerate(v0, v1, v2))
+ continue;
+
+ // create the face info and add it to the list of faces, but only
+ // if this exact face doesn't already
+ // exist in the list
+ FaceInfo faceInfo = new FaceInfo(v0, v1, v2);
+
+ // grab the edge infos, creating them if they do not already exist
+ EdgeInfo edgeInfo01 = findEdgeInfo(edgeInfos, v0, v1);
+ if (edgeInfo01 == null) {
+ //since one of it's edges isn't in the edge data structure, it
+ // can't already exist in the face structure
+ bMightAlreadyExist = false;
+
+ // create the info
+ edgeInfo01 = new EdgeInfo(v0, v1);
+
+ // update the linked list on both
+ edgeInfo01.m_nextV0 = edgeInfos.at(v0);
+ edgeInfo01.m_nextV1 = edgeInfos.at(v1);
+ edgeInfos.set(v0, edgeInfo01);
+ edgeInfos.set(v1, edgeInfo01);
+
+ // set face 0
+ edgeInfo01.m_face0 = faceInfo;
+ } else {
+ if (edgeInfo01.m_face1 != null) {
+ logger.info("BuildStripifyInfo: > 2 triangles on an edge"
+ + v0 + "," + v1 + "... uncertain consequences\n");
+ } else {
+ edgeInfo01.m_face1 = faceInfo;
+ bFaceUpdated[0] = true;
+ }
+ }
+
+ // grab the edge infos, creating them if they do not already exist
+ EdgeInfo edgeInfo12 = findEdgeInfo(edgeInfos, v1, v2);
+ if (edgeInfo12 == null) {
+ bMightAlreadyExist = false;
+
+ // create the info
+ edgeInfo12 = new EdgeInfo(v1, v2);
+
+ // update the linked list on both
+ edgeInfo12.m_nextV0 = edgeInfos.at(v1);
+ edgeInfo12.m_nextV1 = edgeInfos.at(v2);
+ edgeInfos.set(v1, edgeInfo12);
+ edgeInfos.set(v2, edgeInfo12);
+
+ // set face 0
+ edgeInfo12.m_face0 = faceInfo;
+ } else {
+ if (edgeInfo12.m_face1 != null) {
+ logger.info("BuildStripifyInfo: > 2 triangles on an edge"
+ + v1
+ + ","
+ + v2
+ + "... uncertain consequences\n");
+ } else {
+ edgeInfo12.m_face1 = faceInfo;
+ bFaceUpdated[1] = true;
+ }
+ }
+
+ // grab the edge infos, creating them if they do not already exist
+ EdgeInfo edgeInfo20 = findEdgeInfo(edgeInfos, v2, v0);
+ if (edgeInfo20 == null) {
+ bMightAlreadyExist = false;
+
+ // create the info
+ edgeInfo20 = new EdgeInfo(v2, v0);
+
+ // update the linked list on both
+ edgeInfo20.m_nextV0 = edgeInfos.at(v2);
+ edgeInfo20.m_nextV1 = edgeInfos.at(v0);
+ edgeInfos.set(v2, edgeInfo20);
+ edgeInfos.set(v0, edgeInfo20);
+
+ // set face 0
+ edgeInfo20.m_face0 = faceInfo;
+ } else {
+ if (edgeInfo20.m_face1 != null) {
+ logger.info("BuildStripifyInfo: > 2 triangles on an edge"
+ + v2
+ + ","
+ + v0
+ + "... uncertain consequences\n");
+ } else {
+ edgeInfo20.m_face1 = faceInfo;
+ bFaceUpdated[2] = true;
+ }
+ }
+
+ if (bMightAlreadyExist) {
+ if (!alreadyExists(faceInfo, faceInfos))
+ faceInfos.add(faceInfo);
+ else {
+
+ //cleanup pointers that point to this deleted face
+ if (bFaceUpdated[0])
+ edgeInfo01.m_face1 = null;
+ if (bFaceUpdated[1])
+ edgeInfo12.m_face1 = null;
+ if (bFaceUpdated[2])
+ edgeInfo20.m_face1 = null;
+ }
+ } else {
+ faceInfos.add(faceInfo);
+ }
+
+ }
+ }
+
+ static boolean isDegenerate(FaceInfo face) {
+ if (face.m_v0 == face.m_v1)
+ return true;
+ else if (face.m_v0 == face.m_v2)
+ return true;
+ else if (face.m_v1 == face.m_v2)
+ return true;
+ else
+ return false;
+ }
+
+ static boolean isDegenerate(int v0, int v1, int v2) {
+ if (v0 == v1)
+ return true;
+ else if (v0 == v2)
+ return true;
+ else if (v1 == v2)
+ return true;
+ else
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // GetNextIndex()
+ //
+ // Returns vertex of the input face which is "next" in the input index list
+ //
+ static int getNextIndex(IntVec indices, FaceInfo face) {
+
+ int numIndices = indices.size();
+
+ int v0 = indices.get(numIndices - 2);
+ int v1 = indices.get(numIndices - 1);
+
+ int fv0 = face.m_v0;
+ int fv1 = face.m_v1;
+ int fv2 = face.m_v2;
+
+ if (fv0 != v0 && fv0 != v1) {
+ if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1)) {
+ logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n");
+ logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n");
+ }
+ return fv0;
+ }
+ if (fv1 != v0 && fv1 != v1) {
+ if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1)) {
+ logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n");
+ logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n");
+ }
+ return fv1;
+ }
+ if (fv2 != v0 && fv2 != v1) {
+ if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1)) {
+ logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n");
+ logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n");
+ }
+ return fv2;
+ }
+
+ // shouldn't get here, but let's try and fail gracefully
+ if ((fv0 == fv1) || (fv0 == fv2))
+ return fv0;
+ else if ((fv1 == fv0) || (fv1 == fv2))
+ return fv1;
+ else if ((fv2 == fv0) || (fv2 == fv1))
+ return fv2;
+ else
+ return -1;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // FindStartPoint()
+ //
+ // Finds a good starting point, namely one which has only one neighbor
+ //
+ static int findStartPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) {
+ int bestCtr = -1;
+ int bestIndex = -1;
+
+ for (int i = 0; i < faceInfos.size(); i++) {
+ int ctr = 0;
+
+ if (findOtherFace(edgeInfos, faceInfos.at(i).m_v0,
+ faceInfos.at(i).m_v1, faceInfos.at(i)) == null)
+ ctr++;
+ if (findOtherFace(edgeInfos, faceInfos.at(i).m_v1,
+ faceInfos.at(i).m_v2, faceInfos.at(i)) == null)
+ ctr++;
+ if (findOtherFace(edgeInfos, faceInfos.at(i).m_v2,
+ faceInfos.at(i).m_v0, faceInfos.at(i)) == null)
+ ctr++;
+ if (ctr > bestCtr) {
+ bestCtr = ctr;
+ bestIndex = i;
+ //return i;
+ }
+ }
+ //return -1;
+
+ if (bestCtr == 0)
+ return -1;
+
+ return bestIndex;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // FindGoodResetPoint()
+ //
+ // A good reset point is one near other commited areas so that
+ // we know that when we've made the longest strips its because
+ // we're stripifying in the same general orientation.
+ //
+ FaceInfo findGoodResetPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) {
+ // we hop into different areas of the mesh to try to get
+ // other large open spans done. Areas of small strips can
+ // just be left to triangle lists added at the end.
+ FaceInfo result = null;
+
+ if (result == null) {
+ int numFaces = faceInfos.size();
+ int startPoint;
+ if (bFirstTimeResetPoint) {
+ //first time, find a face with few neighbors (look for an edge
+ // of the mesh)
+ startPoint = findStartPoint(faceInfos, edgeInfos);
+ bFirstTimeResetPoint = false;
+ } else
+ startPoint = (int) (((float) numFaces - 1) * meshJump);
+
+ if (startPoint == -1) {
+ startPoint = (int) (((float) numFaces - 1) * meshJump);
+
+ //meshJump += 0.1f;
+ //if (meshJump > 1.0f)
+ // meshJump = .05f;
+ }
+
+ int i = startPoint;
+ do {
+
+ // if this guy isn't visited, try him
+ if (faceInfos.at(i).m_stripId < 0) {
+ result = faceInfos.at(i);
+ break;
+ }
+
+ // update the index and clamp to 0-(numFaces-1)
+ if (++i >= numFaces)
+ i = 0;
+
+ } while (i != startPoint);
+
+ // update the meshJump
+ meshJump += 0.1f;
+ if (meshJump > 1.0f)
+ meshJump = .05f;
+ }
+
+ // return the best face we found
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // GetUniqueVertexInB()
+ //
+ // Returns the vertex unique to faceB
+ //
+ static int getUniqueVertexInB(FaceInfo faceA, FaceInfo faceB) {
+
+ int facev0 = faceB.m_v0;
+ if (facev0 != faceA.m_v0 && facev0 != faceA.m_v1
+ && facev0 != faceA.m_v2)
+ return facev0;
+
+ int facev1 = faceB.m_v1;
+ if (facev1 != faceA.m_v0 && facev1 != faceA.m_v1
+ && facev1 != faceA.m_v2)
+ return facev1;
+
+ int facev2 = faceB.m_v2;
+ if (facev2 != faceA.m_v0 && facev2 != faceA.m_v1
+ && facev2 != faceA.m_v2)
+ return facev2;
+
+ // nothing is different
+ return -1;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // GetSharedVertices()
+ //
+ // Returns the (at most) two vertices shared between the two faces
+ //
+ static void getSharedVertices(FaceInfo faceA, FaceInfo faceB, int[] vertex) {
+ vertex[0] = -1;
+ vertex[1] = -1;
+
+ int facev0 = faceB.m_v0;
+ if (facev0 == faceA.m_v0 || facev0 == faceA.m_v1
+ || facev0 == faceA.m_v2) {
+ if (vertex[0] == -1)
+ vertex[0] = facev0;
+ else {
+ vertex[1] = facev0;
+ return;
+ }
+ }
+
+ int facev1 = faceB.m_v1;
+ if (facev1 == faceA.m_v0 || facev1 == faceA.m_v1
+ || facev1 == faceA.m_v2) {
+ if (vertex[0] == -1)
+ vertex[0] = facev1;
+ else {
+ vertex[1] = facev1;
+ return;
+ }
+ }
+
+ int facev2 = faceB.m_v2;
+ if (facev2 == faceA.m_v0 || facev2 == faceA.m_v1
+ || facev2 == faceA.m_v2) {
+ if (vertex[0] == -1)
+ vertex[0] = facev2;
+ else {
+ vertex[1] = facev2;
+ return;
+ }
+ }
+
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // CommitStrips()
+ //
+ // "Commits" the input strips by setting their m_experimentId to -1 and
+ // adding to the allStrips
+ // vector
+ //
+ static void commitStrips(StripInfoVec allStrips, StripInfoVec strips) {
+ // Iterate through strips
+ int numStrips = strips.size();
+ for (int i = 0; i < numStrips; i++) {
+
+ // Tell the strip that it is now real
+ StripInfo strip = strips.at(i);
+ strip.m_experimentId = -1;
+
+ // add to the list of real strips
+ allStrips.add(strip);
+
+ // Iterate through the faces of the strip
+ // Tell the faces of the strip that they belong to a real strip now
+ FaceInfoVec faces = strips.at(i).m_faces;
+ int numFaces = faces.size();
+
+ for (int j = 0; j < numFaces; j++) {
+ strip.markTriangle(faces.at(j));
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // NextIsCW()
+ //
+ // Returns true if the next face should be ordered in CW fashion
+ //
+ static boolean nextIsCW(int numIndices) {
+ return ((numIndices % 2) == 0);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // UpdateCacheFace()
+ //
+ // Updates the input vertex cache with this face's vertices
+ //
+ static void updateCacheFace(VertexCache vcache, FaceInfo face) {
+ if (!vcache.inCache(face.m_v0))
+ vcache.addEntry(face.m_v0);
+
+ if (!vcache.inCache(face.m_v1))
+ vcache.addEntry(face.m_v1);
+
+ if (!vcache.inCache(face.m_v2))
+ vcache.addEntry(face.m_v2);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // UpdateCacheStrip()
+ //
+ // Updates the input vertex cache with this strip's vertices
+ //
+ static void updateCacheStrip(VertexCache vcache, StripInfo strip) {
+ for (int i = 0; i < strip.m_faces.size(); ++i) {
+ if (!vcache.inCache(strip.m_faces.at(i).m_v0))
+ vcache.addEntry(strip.m_faces.at(i).m_v0);
+
+ if (!vcache.inCache(strip.m_faces.at(i).m_v1))
+ vcache.addEntry(strip.m_faces.at(i).m_v1);
+
+ if (!vcache.inCache(strip.m_faces.at(i).m_v2))
+ vcache.addEntry(strip.m_faces.at(i).m_v2);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // CalcNumHitsStrip()
+ //
+ // returns the number of cache hits per face in the strip
+ //
+ static float calcNumHitsStrip(VertexCache vcache, StripInfo strip) {
+ int numHits = 0;
+ int numFaces = 0;
+
+ for (int i = 0; i < strip.m_faces.size(); i++) {
+ if (vcache.inCache(strip.m_faces.at(i).m_v0))
+ ++numHits;
+
+ if (vcache.inCache(strip.m_faces.at(i).m_v1))
+ ++numHits;
+
+ if (vcache.inCache(strip.m_faces.at(i).m_v2))
+ ++numHits;
+
+ numFaces++;
+ }
+
+ return ((float) numHits / (float) numFaces);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // AvgStripSize()
+ //
+ // Finds the average strip size of the input vector of strips
+ //
+ static float avgStripSize(StripInfoVec strips) {
+ int sizeAccum = 0;
+ int numStrips = strips.size();
+ for (int i = 0; i < numStrips; i++) {
+ StripInfo strip = strips.at(i);
+ sizeAccum += strip.m_faces.size();
+ sizeAccum -= strip.m_numDegenerates;
+ }
+ return ((float) sizeAccum) / ((float) numStrips);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // CalcNumHitsFace()
+ //
+ // returns the number of cache hits in the face
+ //
+ static int calcNumHitsFace(VertexCache vcache, FaceInfo face) {
+ int numHits = 0;
+
+ if (vcache.inCache(face.m_v0))
+ numHits++;
+
+ if (vcache.inCache(face.m_v1))
+ numHits++;
+
+ if (vcache.inCache(face.m_v2))
+ numHits++;
+
+ return numHits;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // NumNeighbors()
+ //
+ // Returns the number of neighbors that this face has
+ //
+ static int numNeighbors(FaceInfo face, EdgeInfoVec edgeInfoVec) {
+ int numNeighbors = 0;
+
+ if (findOtherFace(edgeInfoVec, face.m_v0, face.m_v1, face) != null) {
+ numNeighbors++;
+ }
+
+ if (findOtherFace(edgeInfoVec, face.m_v1, face.m_v2, face) != null) {
+ numNeighbors++;
+ }
+
+ if (findOtherFace(edgeInfoVec, face.m_v2, face.m_v0, face) != null) {
+ numNeighbors++;
+ }
+
+ return numNeighbors;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // IsCW()
+ //
+ // Returns true if the face is ordered in CW fashion
+ //
+ static boolean isCW(FaceInfo faceInfo, int v0, int v1) {
+ if (faceInfo.m_v0 == v0)
+ return (faceInfo.m_v1 == v1);
+ else if (faceInfo.m_v1 == v0)
+ return (faceInfo.m_v2 == v1);
+ else
+ return (faceInfo.m_v0 == v1);
+
+ }
+
+ static boolean faceContainsIndex(FaceInfo face, int index) {
+ return ((face.m_v0 == index) || (face.m_v1 == index) || (face.m_v2 == index));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // FindTraversal()
+ //
+ // Finds the next face to start the next strip on.
+ //
+ static boolean findTraversal(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos,
+ StripInfo strip, StripStartInfo startInfo) {
+
+ // if the strip was v0.v1 on the edge, then v1 will be a vertex in the
+ // next edge.
+ int v = (strip.m_startInfo.m_toV1 ? strip.m_startInfo.m_startEdge.m_v1
+ : strip.m_startInfo.m_startEdge.m_v0);
+
+ FaceInfo untouchedFace = null;
+ EdgeInfo edgeIter = edgeInfos.at(v);
+ while (edgeIter != null) {
+ FaceInfo face0 = edgeIter.m_face0;
+ FaceInfo face1 = edgeIter.m_face1;
+ if ((face0 != null && !strip.isInStrip(face0)) && face1 != null
+ && !strip.isMarked(face1)) {
+ untouchedFace = face1;
+ break;
+ }
+ if ((face1 != null && !strip.isInStrip(face1)) && face0 != null
+ && !strip.isMarked(face0)) {
+ untouchedFace = face0;
+ break;
+ }
+
+ // find the next edgeIter
+ edgeIter = (edgeIter.m_v0 == v ? edgeIter.m_nextV0
+ : edgeIter.m_nextV1);
+ }
+
+ startInfo.m_startFace = untouchedFace;
+ startInfo.m_startEdge = edgeIter;
+ if (edgeIter != null) {
+ if (strip.sharesEdge(startInfo.m_startFace, edgeInfos))
+ startInfo.m_toV1 = (edgeIter.m_v0 == v); //note! used to be
+ // m_v1
+ else
+ startInfo.m_toV1 = (edgeIter.m_v1 == v);
+ }
+ return (startInfo.m_startFace != null);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // RemoveSmallStrips()
+ //
+ // allStrips is the whole strip vector...all small strips will be deleted
+ // from this list, to avoid leaking mem
+ // allBigStrips is an out parameter which will contain all strips above
+ // minStripLength
+ // faceList is an out parameter which will contain all faces which were
+ // removed from the striplist
+ //
+ void removeSmallStrips(StripInfoVec allStrips, StripInfoVec allBigStrips,
+ FaceInfoVec faceList) {
+ faceList.clear();
+ allBigStrips.clear(); //make sure these are empty
+ FaceInfoVec tempFaceList = new FaceInfoVec();
+
+ for (int i = 0; i < allStrips.size(); i++) {
+ if (allStrips.at(i).m_faces.size() < minStripLength) {
+ //strip is too small, add faces to faceList
+ for (int j = 0; j < allStrips.at(i).m_faces.size(); j++)
+ tempFaceList.add(allStrips.at(i).m_faces.at(j));
+
+ } else {
+ allBigStrips.add(allStrips.at(i));
+ }
+ }
+
+ boolean[] bVisitedList = new boolean[tempFaceList.size()];
+
+ VertexCache vcache = new VertexCache(cacheSize);
+
+ int bestNumHits = -1;
+ int numHits;
+ int bestIndex = -9999;
+
+ while (true) {
+ bestNumHits = -1;
+
+ //find best face to add next, given the current cache
+ for (int i = 0; i < tempFaceList.size(); i++) {
+ if (bVisitedList[i])
+ continue;
+
+ numHits = calcNumHitsFace(vcache, tempFaceList.at(i));
+ if (numHits > bestNumHits) {
+ bestNumHits = numHits;
+ bestIndex = i;
+ }
+ }
+
+ if (bestNumHits == -1.0f)
+ break;
+ bVisitedList[bestIndex] = true;
+ updateCacheFace(vcache, tempFaceList.at(bestIndex));
+ faceList.add(tempFaceList.at(bestIndex));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // CreateStrips()
+ //
+ // Generates actual strips from the list-in-strip-order.
+ //
+ int createStrips(StripInfoVec allStrips, IntVec stripIndices,
+ boolean bStitchStrips) {
+ int numSeparateStrips = 0;
+
+ FaceInfo tLastFace = new FaceInfo(0, 0, 0);
+ int nStripCount = allStrips.size();
+
+ //we infer the cw/ccw ordering depending on the number of indices
+ //this is screwed up by the fact that we insert -1s to denote changing
+ // strips
+ //this is to account for that
+ int accountForNegatives = 0;
+
+ for (int i = 0; i < nStripCount; i++) {
+ StripInfo strip = allStrips.at(i);
+ int nStripFaceCount = strip.m_faces.size();
+
+ // Handle the first face in the strip
+ {
+ FaceInfo tFirstFace = new FaceInfo(strip.m_faces.at(0).m_v0,
+ strip.m_faces.at(0).m_v1, strip.m_faces.at(0).m_v2);
+
+ // If there is a second face, reorder vertices such that the
+ // unique vertex is first
+ if (nStripFaceCount > 1) {
+ int nUnique = getUniqueVertexInB(strip.m_faces.at(1),
+ tFirstFace);
+ if (nUnique == tFirstFace.m_v1) {
+ int tmp = tFirstFace.m_v0;
+ tFirstFace.m_v0 = tFirstFace.m_v1;
+ tFirstFace.m_v1 = tmp;
+ } else if (nUnique == tFirstFace.m_v2) {
+ int tmp = tFirstFace.m_v0;
+ tFirstFace.m_v0 = tFirstFace.m_v2;
+ tFirstFace.m_v2 = tmp;
+ }
+
+ // If there is a third face, reorder vertices such that the
+ // shared vertex is last
+ if (nStripFaceCount > 2) {
+ if (isDegenerate(strip.m_faces.at(1))) {
+ int pivot = strip.m_faces.at(1).m_v1;
+ if (tFirstFace.m_v1 == pivot) {
+ int tmp = tFirstFace.m_v1;
+ tFirstFace.m_v1 = tFirstFace.m_v2;
+ tFirstFace.m_v2 = tmp;
+ }
+ } else {
+ int[] nShared = new int[2];
+ getSharedVertices(strip.m_faces.at(2), tFirstFace,
+ nShared);
+ if ((nShared[0] == tFirstFace.m_v1)
+ && (nShared[1] == -1)) {
+ int tmp = tFirstFace.m_v1;
+ tFirstFace.m_v1 = tFirstFace.m_v2;
+ tFirstFace.m_v2 = tmp;
+ }
+ }
+ }
+ }
+
+ if ((i == 0) || !bStitchStrips) {
+ if (!isCW(strip.m_faces.at(0), tFirstFace.m_v0,
+ tFirstFace.m_v1))
+ stripIndices.add(tFirstFace.m_v0);
+ } else {
+ // Double tap the first in the new strip
+ stripIndices.add(tFirstFace.m_v0);
+
+ // Check CW/CCW ordering
+ if (nextIsCW(stripIndices.size() - accountForNegatives) != isCW(
+ strip.m_faces.at(0), tFirstFace.m_v0,
+ tFirstFace.m_v1)) {
+ stripIndices.add(tFirstFace.m_v0);
+ }
+ }
+
+ stripIndices.add(tFirstFace.m_v0);
+ stripIndices.add(tFirstFace.m_v1);
+ stripIndices.add(tFirstFace.m_v2);
+
+ // Update last face info
+ tLastFace.set(tFirstFace);
+ }
+
+ for (int j = 1; j < nStripFaceCount; j++) {
+ int nUnique = getUniqueVertexInB(tLastFace, strip.m_faces.at(j));
+ if (nUnique != -1) {
+ stripIndices.add(nUnique);
+
+ // Update last face info
+ tLastFace.m_v0 = tLastFace.m_v1;
+ tLastFace.m_v1 = tLastFace.m_v2;
+ tLastFace.m_v2 = nUnique;
+ } else {
+ //we've hit a degenerate
+ stripIndices.add(strip.m_faces.at(j).m_v2);
+ tLastFace.m_v0 = strip.m_faces.at(j).m_v0; //tLastFace.m_v1;
+ tLastFace.m_v1 = strip.m_faces.at(j).m_v1; //tLastFace.m_v2;
+ tLastFace.m_v2 = strip.m_faces.at(j).m_v2; //tLastFace.m_v1;
+
+ }
+ }
+
+ // Double tap between strips.
+ if (bStitchStrips) {
+ if (i != nStripCount - 1)
+ stripIndices.add(tLastFace.m_v2);
+ } else {
+ //-1 index indicates next strip
+ stripIndices.add(-1);
+ accountForNegatives++;
+ numSeparateStrips++;
+ }
+
+ // Update last face info
+ tLastFace.m_v0 = tLastFace.m_v1;
+ tLastFace.m_v1 = tLastFace.m_v2;
+ tLastFace.m_v2 = tLastFace.m_v2;
+ }
+
+ if (bStitchStrips)
+ numSeparateStrips = 1;
+ return numSeparateStrips;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // FindAllStrips()
+ //
+ // Does the stripification, puts output strips into vector allStrips
+ //
+ // Works by setting runnning a number of experiments in different areas of
+ // the mesh, and
+ // accepting the one which results in the longest strips. It then accepts
+ // this, and moves
+ // on to a different area of the mesh. We try to jump around the mesh some,
+ // to ensure that
+ // large open spans of strips get generated.
+ //
+ void findAllStrips(StripInfoVec allStrips, FaceInfoVec allFaceInfos,
+ EdgeInfoVec allEdgeInfos, int numSamples) {
+ // the experiments
+ int experimentId = 0;
+ int stripId = 0;
+ boolean done = false;
+
+ int loopCtr = 0;
+
+ while (!done) {
+ loopCtr++;
+
+ //
+ // PHASE 1: Set up numSamples * numEdges experiments
+ //
+ StripInfoVec[] experiments = new StripInfoVec[numSamples * 6];
+ for (int i = 0; i < experiments.length; i++)
+ experiments[i] = new StripInfoVec();
+
+ int experimentIndex = 0;
+ HashSet<FaceInfo> resetPoints = new HashSet<FaceInfo>(); /* NvFaceInfo */
+ for (int i = 0; i < numSamples; i++) {
+ // Try to find another good reset point.
+ // If there are none to be found, we are done
+ FaceInfo nextFace = findGoodResetPoint(allFaceInfos,
+ allEdgeInfos);
+ if (nextFace == null) {
+ done = true;
+ break;
+ }
+ // If we have already evaluated starting at this face in this
+ // slew of experiments, then skip going any further
+ else if (resetPoints.contains(nextFace)) {
+ continue;
+ }
+
+ // trying it now...
+ resetPoints.add(nextFace);
+
+ // otherwise, we shall now try experiments for starting on the
+ // 01,12, and 20 edges
+
+ // build the strip off of this face's 0-1 edge
+ EdgeInfo edge01 = findEdgeInfo(allEdgeInfos, nextFace.m_v0,
+ nextFace.m_v1);
+ StripInfo strip01 = new StripInfo(new StripStartInfo(nextFace,
+ edge01, true), stripId++, experimentId++);
+ experiments[experimentIndex++].add(strip01);
+
+ // build the strip off of this face's 1-0 edge
+ EdgeInfo edge10 = findEdgeInfo(allEdgeInfos, nextFace.m_v0,
+ nextFace.m_v1);
+ StripInfo strip10 = new StripInfo(new StripStartInfo(nextFace,
+ edge10, false), stripId++, experimentId++);
+ experiments[experimentIndex++].add(strip10);
+
+ // build the strip off of this face's 1-2 edge
+ EdgeInfo edge12 = findEdgeInfo(allEdgeInfos, nextFace.m_v1,
+ nextFace.m_v2);
+ StripInfo strip12 = new StripInfo(new StripStartInfo(nextFace,
+ edge12, true), stripId++, experimentId++);
+ experiments[experimentIndex++].add(strip12);
+
+ // build the strip off of this face's 2-1 edge
+ EdgeInfo edge21 = findEdgeInfo(allEdgeInfos, nextFace.m_v1,
+ nextFace.m_v2);
+ StripInfo strip21 = new StripInfo(new StripStartInfo(nextFace,
+ edge21, false), stripId++, experimentId++);
+ experiments[experimentIndex++].add(strip21);
+
+ // build the strip off of this face's 2-0 edge
+ EdgeInfo edge20 = findEdgeInfo(allEdgeInfos, nextFace.m_v2,
+ nextFace.m_v0);
+ StripInfo strip20 = new StripInfo(new StripStartInfo(nextFace,
+ edge20, true), stripId++, experimentId++);
+ experiments[experimentIndex++].add(strip20);
+
+ // build the strip off of this face's 0-2 edge
+ EdgeInfo edge02 = findEdgeInfo(allEdgeInfos, nextFace.m_v2,
+ nextFace.m_v0);
+ StripInfo strip02 = new StripInfo(new StripStartInfo(nextFace,
+ edge02, false), stripId++, experimentId++);
+ experiments[experimentIndex++].add(strip02);
+ }
+
+ //
+ // PHASE 2: Iterate through that we setup in the last phase
+ // and really build each of the strips and strips that follow to
+ // see how
+ // far we get
+ //
+ int numExperiments = experimentIndex;
+ for (int i = 0; i < numExperiments; i++) {
+
+ // get the strip set
+
+ // build the first strip of the list
+ experiments[i].at(0).build(allEdgeInfos, allFaceInfos);
+ int experimentId2 = experiments[i].at(0).m_experimentId;
+
+ StripInfo stripIter = experiments[i].at(0);
+ StripStartInfo startInfo = new StripStartInfo(null, null, false);
+ while (findTraversal(allFaceInfos, allEdgeInfos, stripIter,
+ startInfo)) {
+
+ // create the new strip info
+ //TODO startInfo clone ?
+ stripIter = new StripInfo(startInfo, stripId++,
+ experimentId2);
+
+ // build the next strip
+ stripIter.build(allEdgeInfos, allFaceInfos);
+
+ // add it to the list
+ experiments[i].add(stripIter);
+ }
+ }
+
+ //
+ // Phase 3: Find the experiment that has the most promise
+ //
+ int bestIndex = 0;
+ double bestValue = 0;
+ for (int i = 0; i < numExperiments; i++) {
+ float avgStripSizeWeight = 1.0f;
+ //float numTrisWeight = 0.0f;
+ float numStripsWeight = 0.0f;
+ float avgStripSize = avgStripSize(experiments[i]);
+ float numStrips = experiments[i].size();
+ float value = avgStripSize * avgStripSizeWeight
+ + (numStrips * numStripsWeight);
+ //float value = 1.f / numStrips;
+ //float value = numStrips * avgStripSize;
+
+ if (value > bestValue) {
+ bestValue = value;
+ bestIndex = i;
+ }
+ }
+
+ //
+ // Phase 4: commit the best experiment of the bunch
+ //
+ commitStrips(allStrips, experiments[bestIndex]);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // SplitUpStripsAndOptimize()
+ //
+ // Splits the input vector of strips (allBigStrips) into smaller, cache
+ // friendly pieces, then
+ // reorders these pieces to maximize cache hits
+ // The final strips are output through outStrips
+ //
+ void splitUpStripsAndOptimize(StripInfoVec allStrips,
+ StripInfoVec outStrips, EdgeInfoVec edgeInfos,
+ FaceInfoVec outFaceList) {
+ int threshold = cacheSize;
+ StripInfoVec tempStrips = new StripInfoVec();
+ int j;
+
+ //split up strips into threshold-sized pieces
+ for (int i = 0; i < allStrips.size(); i++) {
+ StripInfo currentStrip;
+ StripStartInfo startInfo = new StripStartInfo(null, null, false);
+
+ int actualStripSize = 0;
+ for (j = 0; j < allStrips.at(i).m_faces.size(); ++j) {
+ if (!isDegenerate(allStrips.at(i).m_faces.at(j)))
+ actualStripSize++;
+ }
+
+ if (actualStripSize /* allStrips.at(i).m_faces.size() */
+ > threshold) {
+
+ int numTimes = actualStripSize /* allStrips.at(i).m_faces.size() */
+ / threshold;
+ int numLeftover = actualStripSize /* allStrips.at(i).m_faces.size() */
+ % threshold;
+
+ int degenerateCount = 0;
+ for (j = 0; j < numTimes; j++) {
+ currentStrip = new StripInfo(startInfo, 0, -1);
+
+ int faceCtr = j * threshold + degenerateCount;
+ boolean bFirstTime = true;
+ while (faceCtr < threshold + (j * threshold)
+ + degenerateCount) {
+ if (isDegenerate(allStrips.at(i).m_faces.at(faceCtr))) {
+ degenerateCount++;
+
+ //last time or first time through, no need for a
+ // degenerate
+ if ((((faceCtr + 1) != threshold + (j * threshold)
+ + degenerateCount) || ((j == numTimes - 1)
+ && (numLeftover < 4) && (numLeftover > 0)))
+ && !bFirstTime) {
+ currentStrip.m_faces
+ .add(allStrips.at(i).m_faces
+ .at(faceCtr++));
+ } else
+ ++faceCtr;
+ } else {
+ currentStrip.m_faces.add(allStrips.at(i).m_faces
+ .at(faceCtr++));
+ bFirstTime = false;
+ }
+ }
+ /*
+ * threshold; faceCtr < threshold+(j*threshold); faceCtr++) {
+ * currentStrip.m_faces.add(allStrips.at(i).m_faces.at(faceCtr]); }
+ */
+ ///*
+ if (j == numTimes - 1) //last time through
+ {
+ if ((numLeftover < 4) && (numLeftover > 0)) //way too
+ // small
+ {
+ //just add to last strip
+ int ctr = 0;
+ while (ctr < numLeftover) {
+ if (!isDegenerate(allStrips.at(i).m_faces
+ .at(faceCtr))) {
+ currentStrip.m_faces
+ .add(allStrips.at(i).m_faces
+ .at(faceCtr++));
+ ++ctr;
+ } else {
+ currentStrip.m_faces
+ .add(allStrips.at(i).m_faces
+ .at(faceCtr++));
+ ++degenerateCount;
+ }
+ }
+ numLeftover = 0;
+ }
+ }
+ //*/
+ tempStrips.add(currentStrip);
+ }
+
+ int leftOff = j * threshold + degenerateCount;
+
+ if (numLeftover != 0) {
+ currentStrip = new StripInfo(startInfo, 0, -1);
+
+ int ctr = 0;
+ boolean bFirstTime = true;
+ while (ctr < numLeftover) {
+ if (!isDegenerate(allStrips.at(i).m_faces.at(leftOff))) {
+ ctr++;
+ bFirstTime = false;
+ currentStrip.m_faces.add(allStrips.at(i).m_faces
+ .at(leftOff++));
+ } else if (!bFirstTime)
+ currentStrip.m_faces.add(allStrips.at(i).m_faces
+ .at(leftOff++));
+ else
+ leftOff++;
+ }
+ /*
+ * for(int k = 0; k < numLeftover; k++) {
+ * currentStrip.m_faces.add(allStrips.at(i).m_faces[leftOff++]); }
+ */
+
+ tempStrips.add(currentStrip);
+ }
+ } else {
+ //we're not just doing a tempStrips.add(allBigStrips[i])
+ // because
+ // this way we can delete allBigStrips later to free the memory
+ currentStrip = new StripInfo(startInfo, 0, -1);
+
+ for (j = 0; j < allStrips.at(i).m_faces.size(); j++)
+ currentStrip.m_faces.add(allStrips.at(i).m_faces.at(j));
+
+ tempStrips.add(currentStrip);
+ }
+ }
+
+ //add small strips to face list
+ StripInfoVec tempStrips2 = new StripInfoVec();
+ removeSmallStrips(tempStrips, tempStrips2, outFaceList);
+
+ outStrips.clear();
+ //screw optimization for now
+ // for(i = 0; i < tempStrips.size(); ++i)
+ // outStrips.add(tempStrips[i]);
+
+ if (tempStrips2.size() != 0) {
+ //Optimize for the vertex cache
+ VertexCache vcache = new VertexCache(cacheSize);
+
+ float bestNumHits = -1.0f;
+ float numHits;
+ int bestIndex = -99999;
+
+ int firstIndex = 0;
+ float minCost = 10000.0f;
+
+ for (int i = 0; i < tempStrips2.size(); i++) {
+ int numNeighbors = 0;
+
+ //find strip with least number of neighbors per face
+ for (j = 0; j < tempStrips2.at(i).m_faces.size(); j++) {
+ numNeighbors += numNeighbors(tempStrips2.at(i).m_faces
+ .at(j), edgeInfos);
+ }
+
+ float currCost = (float) numNeighbors
+ / (float) tempStrips2.at(i).m_faces.size();
+ if (currCost < minCost) {
+ minCost = currCost;
+ firstIndex = i;
+ }
+ }
+
+ updateCacheStrip(vcache, tempStrips2.at(firstIndex));
+ outStrips.add(tempStrips2.at(firstIndex));
+
+ tempStrips2.at(firstIndex).visited = true;
+
+ boolean bWantsCW = (tempStrips2.at(firstIndex).m_faces.size() % 2) == 0;
+
+ //this n^2 algo is what slows down stripification so much....
+ // needs to be improved
+ while (true) {
+ bestNumHits = -1.0f;
+
+ //find best strip to add next, given the current cache
+ for (int i = 0; i < tempStrips2.size(); i++) {
+ if (tempStrips2.at(i).visited)
+ continue;
+
+ numHits = calcNumHitsStrip(vcache, tempStrips2.at(i));
+ if (numHits > bestNumHits) {
+ bestNumHits = numHits;
+ bestIndex = i;
+ } else if (numHits >= bestNumHits) {
+ //check previous strip to see if this one requires it
+ // to switch polarity
+ StripInfo strip = tempStrips2.at(i);
+ int nStripFaceCount = strip.m_faces.size();
+
+ FaceInfo tFirstFace = new FaceInfo(
+ strip.m_faces.at(0).m_v0,
+ strip.m_faces.at(0).m_v1,
+ strip.m_faces.at(0).m_v2);
+
+ // If there is a second face, reorder vertices such
+ // that the
+ // unique vertex is first
+ if (nStripFaceCount > 1) {
+ int nUnique = getUniqueVertexInB(strip.m_faces
+ .at(1), tFirstFace);
+ if (nUnique == tFirstFace.m_v1) {
+ int tmp = tFirstFace.m_v0;
+ tFirstFace.m_v0 = tFirstFace.m_v1;
+ tFirstFace.m_v1 = tmp;
+ } else if (nUnique == tFirstFace.m_v2) {
+ int tmp = tFirstFace.m_v0;
+ tFirstFace.m_v0 = tFirstFace.m_v2;
+ tFirstFace.m_v2 = tmp;
+ }
+
+ // If there is a third face, reorder vertices such
+ // that the
+ // shared vertex is last
+ if (nStripFaceCount > 2) {
+ int[] nShared = new int[2];
+ getSharedVertices(strip.m_faces.at(2),
+ tFirstFace, nShared);
+ if ((nShared[0] == tFirstFace.m_v1)
+ && (nShared[1] == -1)) {
+ int tmp = tFirstFace.m_v2;
+ tFirstFace.m_v2 = tFirstFace.m_v1;
+ tFirstFace.m_v1 = tmp;
+ }
+ }
+ }
+
+ // Check CW/CCW ordering
+ if (bWantsCW == isCW(strip.m_faces.at(0),
+ tFirstFace.m_v0, tFirstFace.m_v1)) {
+ //I like this one!
+ bestIndex = i;
+ }
+ }
+ }
+
+ if (bestNumHits == -1.0f)
+ break;
+ tempStrips2.at(bestIndex).visited = true;
+ updateCacheStrip(vcache, tempStrips2.at(bestIndex));
+ outStrips.add(tempStrips2.at(bestIndex));
+ bWantsCW = (tempStrips2.at(bestIndex).m_faces.size() % 2 == 0) ? bWantsCW
+ : !bWantsCW;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Stripify()
+ //
+ //
+ // in_indices are the input indices of the mesh to stripify
+ // in_cacheSize is the target cache size
+ //
+ void stripify(IntVec in_indices, int in_cacheSize, int in_minStripLength,
+ int maxIndex, StripInfoVec outStrips, FaceInfoVec outFaceList) {
+ meshJump = 0.0f;
+ bFirstTimeResetPoint = true; //used in FindGoodResetPoint()
+
+ //the number of times to run the experiments
+ int numSamples = 10;
+
+ //the cache size, clamped to one
+ cacheSize = Math.max(1, in_cacheSize - CACHE_INEFFICIENCY);
+
+ minStripLength = in_minStripLength;
+ //this is the strip size threshold below which we dump the strip into
+ // a list
+
+ indices = in_indices;
+
+ // build the stripification info
+ FaceInfoVec allFaceInfos = new FaceInfoVec();
+ EdgeInfoVec allEdgeInfos = new EdgeInfoVec();
+
+ buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex);
+
+ StripInfoVec allStrips = new StripInfoVec();
+
+ // stripify
+ findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
+
+ //split up the strips into cache friendly pieces, optimize them, then
+ // dump these into outStrips
+ splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos,
+ outFaceList);
+
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/tools/jme3tools/converters/model/strip/TriStrip.java b/engine/src/tools/jme3tools/converters/model/strip/TriStrip.java
new file mode 100644
index 0000000..aa84d5c
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/TriStrip.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+import java.util.Arrays;
+
+/**
+ * To use, call generateStrips method, passing your triangle index list and
+ * then construct geometry/render resulting PrimitiveGroup objects.
+ * Features:
+ * <ul>
+ * <li>generates strips from arbitrary geometry.
+ * <li>flexibly optimizes for post TnL vertex caches (16 on GeForce1/2, 24 on GeForce3).
+ * <li>can stitch together strips using degenerate triangles, or not.
+ * <li>can output lists instead of strips.
+ * <li>can optionally throw excessively small strips into a list instead.
+ * <li>can remap indices to improve spatial locality in your vertex buffers.
+ * </ul>
+ * On cache sizes: Note that it's better to UNDERESTIMATE the cache size
+ * instead of OVERESTIMATING. So, if you're targetting GeForce1, 2, and 3, be
+ * conservative and use the GeForce1_2 cache size, NOT the GeForce3 cache size.
+ * This will make sure you don't "blow" the cache of the GeForce1 and 2. Also
+ * note that the cache size you specify is the "actual" cache size, not the
+ * "effective" cache size you may have heard about. This is 16 for GeForce1 and 2,
+ * and 24 for GeForce3.
+ *
+ * Credit goes to Curtis Beeson and Joe Demers for the basis for this
+ * stripifier and to Jason Regier and Jon Stone at Blizzard for providing a
+ * much cleaner version of CreateStrips().
+ *
+ * Ported to java by Artur Biesiadowski <abies@pg.gda.pl>
+ */
+public class TriStrip {
+
+ public static final int CACHESIZE_GEFORCE1_2 = 16;
+ public static final int CACHESIZE_GEFORCE3 = 24;
+
+ int cacheSize = CACHESIZE_GEFORCE1_2;
+ boolean bStitchStrips = true;
+ int minStripSize = 0;
+ boolean bListsOnly = false;
+
+ /**
+ *
+ */
+ public TriStrip() {
+ super();
+ }
+
+ /**
+ * If set to true, will return an optimized list, with no strips at all.
+ * Default value: false
+ */
+ public void setListsOnly(boolean _bListsOnly) {
+ bListsOnly = _bListsOnly;
+ }
+
+ /**
+ * Sets the cache size which the stripfier uses to optimize the data.
+ * Controls the length of the generated individual strips. This is the
+ * "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2 You may
+ * want to play around with this number to tweak performance. Default
+ * value: 16
+ */
+ public void setCacheSize(int _cacheSize) {
+ cacheSize = _cacheSize;
+ }
+
+ /**
+ * bool to indicate whether to stitch together strips into one huge strip
+ * or not. If set to true, you'll get back one huge strip stitched together
+ * using degenerate triangles. If set to false, you'll get back a large
+ * number of separate strips. Default value: true
+ */
+ public void setStitchStrips(boolean _bStitchStrips) {
+ bStitchStrips = _bStitchStrips;
+ }
+
+ /**
+ * Sets the minimum acceptable size for a strip, in triangles. All strips
+ * generated which are shorter than this will be thrown into one big,
+ * separate list. Default value: 0
+ */
+ public void setMinStripSize(int _minStripSize) {
+ minStripSize = _minStripSize;
+ }
+
+ /**
+ * @param in_indices
+ * input index list, the indices you would use to render
+ * @return array of optimized/stripified PrimitiveGroups
+ */
+ public PrimitiveGroup[] generateStrips(int[] in_indices) {
+ int numGroups = 0;
+ PrimitiveGroup[] primGroups;
+ //put data in format that the stripifier likes
+ IntVec tempIndices = new IntVec();
+ int maxIndex = 0;
+
+ for (int i = 0; i < in_indices.length; i++) {
+ tempIndices.add(in_indices[i]);
+ if (in_indices[i] > maxIndex)
+ maxIndex = in_indices[i];
+ }
+
+ StripInfoVec tempStrips = new StripInfoVec();
+ FaceInfoVec tempFaces = new FaceInfoVec();
+
+ Stripifier stripifier = new Stripifier();
+
+ //do actual stripification
+ stripifier.stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces);
+
+ //stitch strips together
+ IntVec stripIndices = new IntVec();
+ int numSeparateStrips = 0;
+
+ if (bListsOnly) {
+ //if we're outputting only lists, we're done
+ numGroups = 1;
+ primGroups = new PrimitiveGroup[numGroups];
+ primGroups[0] = new PrimitiveGroup();
+ PrimitiveGroup[] primGroupArray = primGroups;
+
+ //count the total number of indices
+ int numIndices = 0;
+ for (int i = 0; i < tempStrips.size(); i++) {
+ numIndices += tempStrips.at(i).m_faces.size() * 3;
+ }
+
+ //add in the list
+ numIndices += tempFaces.size() * 3;
+
+ primGroupArray[0].type = PrimitiveGroup.PT_LIST;
+ primGroupArray[0].indices = new int[numIndices];
+ primGroupArray[0].numIndices = numIndices;
+
+ //do strips
+ int indexCtr = 0;
+ for (int i = 0; i < tempStrips.size(); i++) {
+ for (int j = 0; j < tempStrips.at(i).m_faces.size(); j++) {
+ //degenerates are of no use with lists
+ if (!Stripifier.isDegenerate(tempStrips.at(i).m_faces.at(j))) {
+ primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v0;
+ primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v1;
+ primGroupArray[0].indices[indexCtr++] = tempStrips.at(i).m_faces.at(j).m_v2;
+ } else {
+ //we've removed a tri, reduce the number of indices
+ primGroupArray[0].numIndices -= 3;
+ }
+ }
+ }
+
+ //do lists
+ for (int i = 0; i < tempFaces.size(); i++) {
+ primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v0;
+ primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v1;
+ primGroupArray[0].indices[indexCtr++] = tempFaces.at(i).m_v2;
+ }
+ } else {
+ numSeparateStrips = stripifier.createStrips(tempStrips, stripIndices, bStitchStrips);
+
+ //if we're stitching strips together, we better get back only one
+ // strip from CreateStrips()
+
+ //convert to output format
+ numGroups = numSeparateStrips; //for the strips
+ if (tempFaces.size() != 0)
+ numGroups++; //we've got a list as well, increment
+ primGroups = new PrimitiveGroup[numGroups];
+ for (int i = 0; i < primGroups.length; i++) {
+ primGroups[i] = new PrimitiveGroup();
+ }
+
+ PrimitiveGroup[] primGroupArray = primGroups;
+
+ //first, the strips
+ int startingLoc = 0;
+ for (int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++) {
+ int stripLength = 0;
+
+ if (!bStitchStrips) {
+ int i;
+ //if we've got multiple strips, we need to figure out the
+ // correct length
+ for (i = startingLoc; i < stripIndices.size(); i++) {
+ if (stripIndices.get(i) == -1)
+ break;
+ }
+
+ stripLength = i - startingLoc;
+ } else
+ stripLength = stripIndices.size();
+
+ primGroupArray[stripCtr].type = PrimitiveGroup.PT_STRIP;
+ primGroupArray[stripCtr].indices = new int[stripLength];
+ primGroupArray[stripCtr].numIndices = stripLength;
+
+ int indexCtr = 0;
+ for (int i = startingLoc; i < stripLength + startingLoc; i++)
+ primGroupArray[stripCtr].indices[indexCtr++] = stripIndices.get(i);
+
+ //we add 1 to account for the -1 separating strips
+ //this doesn't break the stitched case since we'll exit the
+ // loop
+ startingLoc += stripLength + 1;
+ }
+
+ //next, the list
+ if (tempFaces.size() != 0) {
+ int faceGroupLoc = numGroups - 1; //the face group is the last
+ // one
+ primGroupArray[faceGroupLoc].type = PrimitiveGroup.PT_LIST;
+ primGroupArray[faceGroupLoc].indices = new int[tempFaces.size() * 3];
+ primGroupArray[faceGroupLoc].numIndices = tempFaces.size() * 3;
+ int indexCtr = 0;
+ for (int i = 0; i < tempFaces.size(); i++) {
+ primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v0;
+ primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v1;
+ primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces.at(i).m_v2;
+ }
+ }
+ }
+ return primGroups;
+ }
+
+ /**
+ * Function to remap your indices to improve spatial locality in your
+ * vertex buffer.
+ *
+ * in_primGroups: array of PrimitiveGroups you want remapped numGroups:
+ * number of entries in in_primGroups numVerts: number of vertices in your
+ * vertex buffer, also can be thought of as the range of acceptable values
+ * for indices in your primitive groups. remappedGroups: array of remapped
+ * PrimitiveGroups
+ *
+ * Note that, according to the remapping handed back to you, you must
+ * reorder your vertex buffer.
+ *
+ */
+
+ public static int[] remapIndices(int[] indices, int numVerts) {
+ int[] indexCache = new int[numVerts];
+ Arrays.fill(indexCache, -1);
+
+ int numIndices = indices.length;
+ int[] remappedIndices = new int[numIndices];
+ int indexCtr = 0;
+ for (int j = 0; j < numIndices; j++) {
+ int cachedIndex = indexCache[indices[j]];
+ if (cachedIndex == -1) //we haven't seen this index before
+ {
+ //point to "last" vertex in VB
+ remappedIndices[j] = indexCtr;
+
+ //add to index cache, increment
+ indexCache[indices[j]] = indexCtr++;
+ } else {
+ //we've seen this index before
+ remappedIndices[j] = cachedIndex;
+ }
+ }
+
+ return remappedIndices;
+ }
+
+ public static void remapArrays(float[] vertexBuffer, int vertexSize, int[] indices) {
+ int[] remapped = remapIndices(indices, vertexBuffer.length / vertexSize);
+ float[] bufferCopy = vertexBuffer.clone();
+ for (int i = 0; i < remapped.length; i++) {
+ int from = indices[i] * vertexSize;
+ int to = remapped[i] * vertexSize;
+ for (int j = 0; j < vertexSize; j++) {
+ vertexBuffer[to + j] = bufferCopy[from + j];
+ }
+ }
+
+ System.arraycopy(remapped, 0, indices, 0, indices.length);
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/converters/model/strip/VertexCache.java b/engine/src/tools/jme3tools/converters/model/strip/VertexCache.java
new file mode 100644
index 0000000..b60e9e5
--- /dev/null
+++ b/engine/src/tools/jme3tools/converters/model/strip/VertexCache.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.converters.model.strip;
+
+import java.util.Arrays;
+
+
+class VertexCache {
+
+ int[] entries;
+ int numEntries;
+
+ public VertexCache() {
+ this(16);
+ }
+
+ public VertexCache(int size) {
+ numEntries = size;
+ entries = new int[numEntries];
+ clear();
+ }
+
+ public boolean inCache(int entry) {
+ for(int i = 0; i < numEntries; i++)
+ {
+ if(entries[i] == entry)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int addEntry(int entry) {
+ int removed;
+
+ removed = entries[numEntries - 1];
+
+ //push everything right one
+ for(int i = numEntries - 2; i >= 0; i--)
+ {
+ entries[i + 1] = entries[i];
+ }
+
+ entries[0] = entry;
+
+ return removed;
+ }
+
+ public void clear() {
+ Arrays.fill(entries,-1);
+ }
+
+ public int at(int index) {
+ return entries[index];
+ }
+
+ public void set(int index, int value) {
+ entries[index] = value;
+ }
+
+ public void copy(VertexCache inVcache)
+ {
+ for(int i = 0; i < numEntries; i++)
+ {
+ inVcache.set(i, entries[i]);
+ }
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/optimize/FastOctnode.java b/engine/src/tools/jme3tools/optimize/FastOctnode.java
new file mode 100644
index 0000000..d9e37da
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/FastOctnode.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.optimize;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import java.util.Set;
+
+public class FastOctnode {
+
+ int offset;
+ int length;
+ FastOctnode child;
+ FastOctnode next;
+
+ private static final BoundingBox tempBox = new BoundingBox();
+
+ public int getSide(){
+ return ((offset & 0xE0000000) >> 29) & 0x7;
+ }
+
+ public void setSide(int side){
+ offset &= 0x1FFFFFFF;
+ offset |= (side << 29);
+ }
+
+ public void setOffset(int offset){
+ if (offset < 0 || offset > 20000000){
+ throw new IllegalArgumentException();
+ }
+
+ this.offset &= 0xE0000000;
+ this.offset |= offset;
+ }
+
+ public int getOffset(){
+ return this.offset & 0x1FFFFFFF;
+ }
+
+ private void generateRenderSetNoCheck(Geometry[] globalGeomList, Set<Geometry> renderSet, Camera cam){
+ if (length != 0){
+ int start = getOffset();
+ int end = start + length;
+ for (int i = start; i < end; i++){
+ renderSet.add(globalGeomList[i]);
+ }
+ }
+
+ if (child == null)
+ return;
+
+ FastOctnode node = child;
+ while (node != null){
+ node.generateRenderSetNoCheck(globalGeomList, renderSet, cam);
+ node = node.next;
+ }
+ }
+
+ private static void findChildBound(BoundingBox bbox, int side){
+ float extent = bbox.getXExtent() * 0.5f;
+ bbox.getCenter().set(bbox.getCenter().x + extent * Octnode.extentMult[side].x,
+ bbox.getCenter().y + extent * Octnode.extentMult[side].y,
+ bbox.getCenter().z + extent * Octnode.extentMult[side].z);
+ bbox.setXExtent(extent);
+ bbox.setYExtent(extent);
+ bbox.setZExtent(extent);
+ }
+
+ public void generateRenderSet(Geometry[] globalGeomList, Set<Geometry> renderSet, Camera cam, BoundingBox parentBox, boolean isRoot){
+ tempBox.setCenter(parentBox.getCenter());
+ tempBox.setXExtent(parentBox.getXExtent());
+ tempBox.setYExtent(parentBox.getYExtent());
+ tempBox.setZExtent(parentBox.getZExtent());
+
+ if (!isRoot){
+ findChildBound(tempBox, getSide());
+ }
+
+ tempBox.setCheckPlane(0);
+ cam.setPlaneState(0);
+ Camera.FrustumIntersect result = cam.contains(tempBox);
+ if (result != Camera.FrustumIntersect.Outside){
+ if (length != 0){
+ int start = getOffset();
+ int end = start + length;
+ for (int i = start; i < end; i++){
+ renderSet.add(globalGeomList[i]);
+ }
+ }
+
+ if (child == null)
+ return;
+
+ FastOctnode node = child;
+
+ float x = tempBox.getCenter().x;
+ float y = tempBox.getCenter().y;
+ float z = tempBox.getCenter().z;
+ float ext = tempBox.getXExtent();
+
+ while (node != null){
+ if (result == Camera.FrustumIntersect.Inside){
+ node.generateRenderSetNoCheck(globalGeomList, renderSet, cam);
+ }else{
+ node.generateRenderSet(globalGeomList, renderSet, cam, tempBox, false);
+ }
+
+ tempBox.getCenter().set(x,y,z);
+ tempBox.setXExtent(ext);
+ tempBox.setYExtent(ext);
+ tempBox.setZExtent(ext);
+
+ node = node.next;
+ }
+ }
+ }
+
+ @Override
+ public String toString(){
+ return "OCTNode[O=" + getOffset() + ", L=" + length +
+ ", S=" + getSide() + "]";
+ }
+
+ public String toStringVerbose(int indent){
+ String str = "------------------".substring(0,indent) + toString() + "\n";
+ if (child == null)
+ return str;
+
+ FastOctnode children = child;
+ while (children != null){
+ str += children.toStringVerbose(indent+1);
+ children = children.next;
+ }
+
+ return str;
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java
new file mode 100644
index 0000000..ae6ad8c
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java
@@ -0,0 +1,418 @@
+package jme3tools.optimize;
+
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap.Entry;
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.*;
+import java.util.logging.Logger;
+
+public class GeometryBatchFactory {
+
+ private static final Logger logger = Logger.getLogger(GeometryBatchFactory.class.getName());
+
+ private static void doTransformVerts(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
+ Vector3f pos = new Vector3f();
+
+ // offset is given in element units
+ // convert to be in component units
+ offset *= 3;
+
+ for (int i = 0; i < inBuf.capacity() / 3; i++) {
+ pos.x = inBuf.get(i * 3 + 0);
+ pos.y = inBuf.get(i * 3 + 1);
+ pos.z = inBuf.get(i * 3 + 2);
+
+ transform.mult(pos, pos);
+
+ outBuf.put(offset + i * 3 + 0, pos.x);
+ outBuf.put(offset + i * 3 + 1, pos.y);
+ outBuf.put(offset + i * 3 + 2, pos.z);
+ }
+ }
+
+ private static void doTransformNorms(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
+ Vector3f norm = new Vector3f();
+
+ // offset is given in element units
+ // convert to be in component units
+ offset *= 3;
+
+ for (int i = 0; i < inBuf.capacity() / 3; i++) {
+ norm.x = inBuf.get(i * 3 + 0);
+ norm.y = inBuf.get(i * 3 + 1);
+ norm.z = inBuf.get(i * 3 + 2);
+
+ transform.multNormal(norm, norm);
+
+ outBuf.put(offset + i * 3 + 0, norm.x);
+ outBuf.put(offset + i * 3 + 1, norm.y);
+ outBuf.put(offset + i * 3 + 2, norm.z);
+ }
+ }
+
+ private static void doTransformTangents(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
+ Vector3f tan = new Vector3f();
+ float handedness = 0;
+ // offset is given in element units
+ // convert to be in component units
+ offset *= 4;
+
+ for (int i = 0; i < inBuf.capacity() / 4; i++) {
+ tan.x = inBuf.get(i * 4 + 0);
+ tan.y = inBuf.get(i * 4 + 1);
+ tan.z = inBuf.get(i * 4 + 2);
+ handedness = inBuf.get(i * 4 + 3);
+
+ transform.multNormal(tan, tan);
+
+ outBuf.put(offset + i * 4 + 0, tan.x);
+ outBuf.put(offset + i * 4 + 1, tan.y);
+ outBuf.put(offset + i * 4 + 2, tan.z);
+ outBuf.put(offset + i * 4 + 3, handedness);
+
+ }
+ }
+
+ /**
+ * Merges all geometries in the collection into
+ * the output mesh. Creates a new material using the TextureAtlas.
+ *
+ * @param geometries
+ * @param outMesh
+ */
+ public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) {
+ int[] compsForBuf = new int[VertexBuffer.Type.values().length];
+ Format[] formatForBuf = new Format[compsForBuf.length];
+
+ int totalVerts = 0;
+ int totalTris = 0;
+ int totalLodLevels = 0;
+
+ Mode mode = null;
+ for (Geometry geom : geometries) {
+ totalVerts += geom.getVertexCount();
+ totalTris += geom.getTriangleCount();
+ totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
+
+ Mode listMode;
+ int components;
+ switch (geom.getMesh().getMode()) {
+ case Points:
+ listMode = Mode.Points;
+ components = 1;
+ break;
+ case LineLoop:
+ case LineStrip:
+ case Lines:
+ listMode = Mode.Lines;
+ components = 2;
+ break;
+ case TriangleFan:
+ case TriangleStrip:
+ case Triangles:
+ listMode = Mode.Triangles;
+ components = 3;
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+
+ for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) {
+ compsForBuf[entry.getKey()] = entry.getValue().getNumComponents();
+ formatForBuf[entry.getKey()] = entry.getValue().getFormat();
+ }
+
+ if (mode != null && mode != listMode) {
+ throw new UnsupportedOperationException("Cannot combine different"
+ + " primitive types: " + mode + " != " + listMode);
+ }
+ mode = listMode;
+ compsForBuf[Type.Index.ordinal()] = components;
+ }
+
+ outMesh.setMode(mode);
+ if (totalVerts >= 65536) {
+ // make sure we create an UnsignedInt buffer so
+ // we can fit all of the meshes
+ formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt;
+ } else {
+ formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort;
+ }
+
+ // generate output buffers based on retrieved info
+ for (int i = 0; i < compsForBuf.length; i++) {
+ if (compsForBuf[i] == 0) {
+ continue;
+ }
+
+ Buffer data;
+ if (i == Type.Index.ordinal()) {
+ data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
+ } else {
+ data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
+ }
+
+ VertexBuffer vb = new VertexBuffer(Type.values()[i]);
+ vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data);
+ outMesh.setBuffer(vb);
+ }
+
+ int globalVertIndex = 0;
+ int globalTriIndex = 0;
+
+ for (Geometry geom : geometries) {
+ Mesh inMesh = geom.getMesh();
+ geom.computeWorldMatrix();
+ Matrix4f worldMatrix = geom.getWorldMatrix();
+
+ int geomVertCount = inMesh.getVertexCount();
+ int geomTriCount = inMesh.getTriangleCount();
+
+ for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
+ VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]);
+ VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]);
+
+ if (inBuf == null || outBuf == null) {
+ continue;
+ }
+
+ if (Type.Index.ordinal() == bufType) {
+ int components = compsForBuf[bufType];
+
+ IndexBuffer inIdx = inMesh.getIndicesAsList();
+ IndexBuffer outIdx = outMesh.getIndexBuffer();
+
+ for (int tri = 0; tri < geomTriCount; tri++) {
+ for (int comp = 0; comp < components; comp++) {
+ int idx = inIdx.get(tri * components + comp) + globalVertIndex;
+ outIdx.put((globalTriIndex + tri) * components + comp, idx);
+ }
+ }
+ } else if (Type.Position.ordinal() == bufType) {
+ FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
+ FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+ doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix);
+ } else if (Type.Normal.ordinal() == bufType) {
+ FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
+ FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+ doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
+ }else if(Type.Tangent.ordinal() == bufType){
+ FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
+ FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+ doTransformTangents(inPos, globalVertIndex, outPos, worldMatrix);
+ } else {
+ inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
+ }
+ }
+
+ globalVertIndex += geomVertCount;
+ globalTriIndex += geomTriCount;
+ }
+ }
+
+ public static void makeLods(Collection<Geometry> geometries, Mesh outMesh) {
+ int lodLevels = 0;
+ int[] lodSize = null;
+ int index = 0;
+ for (Geometry g : geometries) {
+ if (lodLevels == 0) {
+ lodLevels = g.getMesh().getNumLodLevels();
+ }
+ if (lodSize == null) {
+ lodSize = new int[lodLevels];
+ }
+ for (int i = 0; i < lodLevels; i++) {
+ lodSize[i] += g.getMesh().getLodLevel(i).getData().capacity();
+ //if( i == 0) System.out.println(index + " " +lodSize[i]);
+ }
+ index++;
+ }
+ int[][] lodData = new int[lodLevels][];
+ for (int i = 0; i < lodLevels; i++) {
+ lodData[i] = new int[lodSize[i]];
+ }
+ VertexBuffer[] lods = new VertexBuffer[lodLevels];
+ int bufferPos[] = new int[lodLevels];
+ //int index = 0;
+ int numOfVertices = 0;
+ int curGeom = 0;
+ for (Geometry g : geometries) {
+ if (numOfVertices == 0) {
+ numOfVertices = g.getVertexCount();
+ }
+ for (int i = 0; i < lodLevels; i++) {
+ ShortBuffer buffer = (ShortBuffer) g.getMesh().getLodLevel(i).getDataReadOnly();
+ //System.out.println("buffer: " + buffer.capacity() + " limit: " + lodSize[i] + " " + index);
+ for (int j = 0; j < buffer.capacity(); j++) {
+ lodData[i][bufferPos[i] + j] = buffer.get() + numOfVertices * curGeom;
+ //bufferPos[i]++;
+ }
+ bufferPos[i] += buffer.capacity();
+ }
+ curGeom++;
+ }
+ for (int i = 0; i < lodLevels; i++) {
+ lods[i] = new VertexBuffer(Type.Index);
+ lods[i].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(lodData[i]));
+ }
+ System.out.println(lods.length);
+ outMesh.setLodLevels(lods);
+ }
+
+ public static List<Geometry> makeBatches(Collection<Geometry> geometries) {
+ return makeBatches(geometries, false);
+ }
+
+ /**
+ * Batches a collection of Geometries so that all with the same material get combined.
+ * @param geometries The Geometries to combine
+ * @return A List of newly created Geometries, each with a distinct material
+ */
+ public static List<Geometry> makeBatches(Collection<Geometry> geometries, boolean useLods) {
+ ArrayList<Geometry> retVal = new ArrayList<Geometry>();
+ HashMap<Material, List<Geometry>> matToGeom = new HashMap<Material, List<Geometry>>();
+
+ for (Geometry geom : geometries) {
+ List<Geometry> outList = matToGeom.get(geom.getMaterial());
+ if (outList == null) {
+ outList = new ArrayList<Geometry>();
+ matToGeom.put(geom.getMaterial(), outList);
+ }
+ outList.add(geom);
+ }
+
+ int batchNum = 0;
+ for (Map.Entry<Material, List<Geometry>> entry : matToGeom.entrySet()) {
+ Material mat = entry.getKey();
+ List<Geometry> geomsForMat = entry.getValue();
+ Mesh mesh = new Mesh();
+ mergeGeometries(geomsForMat, mesh);
+ // lods
+ if (useLods) {
+ makeLods(geomsForMat, mesh);
+ }
+ mesh.updateCounts();
+ mesh.updateBound();
+
+ Geometry out = new Geometry("batch[" + (batchNum++) + "]", mesh);
+ out.setMaterial(mat);
+ retVal.add(out);
+ }
+
+ return retVal;
+ }
+
+ public static void gatherGeoms(Spatial scene, List<Geometry> geoms) {
+ if (scene instanceof Node) {
+ Node node = (Node) scene;
+ for (Spatial child : node.getChildren()) {
+ gatherGeoms(child, geoms);
+ }
+ } else if (scene instanceof Geometry) {
+ geoms.add((Geometry) scene);
+ }
+ }
+
+ /**
+ * Optimizes a scene by combining Geometry with the same material.
+ * All Geometries found in the scene are detached from their parent and
+ * a new Node containing the optimized Geometries is attached.
+ * @param scene The scene to optimize
+ * @return The newly created optimized geometries attached to a node
+ */
+ public static Spatial optimize(Node scene) {
+ return optimize(scene, false);
+ }
+
+ /**
+ * Optimizes a scene by combining Geometry with the same material.
+ * All Geometries found in the scene are detached from their parent and
+ * a new Node containing the optimized Geometries is attached.
+ * @param scene The scene to optimize
+ * @param useLods true if you want the resulting geometry to keep lod information
+ * @return The newly created optimized geometries attached to a node
+ */
+ public static Node optimize(Node scene, boolean useLods) {
+ ArrayList<Geometry> geoms = new ArrayList<Geometry>();
+
+ gatherGeoms(scene, geoms);
+
+ List<Geometry> batchedGeoms = makeBatches(geoms, useLods);
+ for (Geometry geom : batchedGeoms) {
+ scene.attachChild(geom);
+ }
+
+ for (Iterator<Geometry> it = geoms.iterator(); it.hasNext();) {
+ Geometry geometry = it.next();
+ geometry.removeFromParent();
+ }
+
+ // Since the scene is returned unaltered the transform must be reset
+ scene.setLocalTransform(Transform.IDENTITY);
+
+ return scene;
+ }
+
+ public static void printMesh(Mesh mesh) {
+ for (int bufType = 0; bufType < Type.values().length; bufType++) {
+ VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]);
+ if (outBuf == null) {
+ continue;
+ }
+
+ System.out.println(outBuf.getBufferType() + ": ");
+ for (int vert = 0; vert < outBuf.getNumElements(); vert++) {
+ String str = "[";
+ for (int comp = 0; comp < outBuf.getNumComponents(); comp++) {
+ Object val = outBuf.getElementComponent(vert, comp);
+ outBuf.setElementComponent(vert, comp, val);
+ val = outBuf.getElementComponent(vert, comp);
+ str += val;
+ if (comp != outBuf.getNumComponents() - 1) {
+ str += ", ";
+ }
+ }
+ str += "]";
+ System.out.println(str);
+ }
+ System.out.println("------");
+ }
+ }
+
+ public static void main(String[] args) {
+ Mesh mesh = new Mesh();
+ mesh.setBuffer(Type.Position, 3, new float[]{
+ 0, 0, 0,
+ 1, 0, 0,
+ 1, 1, 0,
+ 0, 1, 0
+ });
+ mesh.setBuffer(Type.Index, 2, new short[]{
+ 0, 1,
+ 1, 2,
+ 2, 3,
+ 3, 0
+ });
+
+ Geometry g1 = new Geometry("g1", mesh);
+
+ ArrayList<Geometry> geoms = new ArrayList<Geometry>();
+ geoms.add(g1);
+
+ Mesh outMesh = new Mesh();
+ mergeGeometries(geoms, outMesh);
+ printMesh(outMesh);
+ }
+}
diff --git a/engine/src/tools/jme3tools/optimize/OCTTriangle.java b/engine/src/tools/jme3tools/optimize/OCTTriangle.java
new file mode 100644
index 0000000..b31a8a3
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/OCTTriangle.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.optimize;
+
+import com.jme3.math.Vector3f;
+
+public final class OCTTriangle {
+
+ private final Vector3f pointa = new Vector3f();
+ private final Vector3f pointb = new Vector3f();
+ private final Vector3f pointc = new Vector3f();
+ private final int index;
+ private final int geomIndex;
+
+ public OCTTriangle(Vector3f p1, Vector3f p2, Vector3f p3, int index, int geomIndex) {
+ pointa.set(p1);
+ pointb.set(p2);
+ pointc.set(p3);
+ this.index = index;
+ this.geomIndex = geomIndex;
+ }
+
+ public int getGeometryIndex() {
+ return geomIndex;
+ }
+
+ public int getTriangleIndex() {
+ return index;
+ }
+
+ public Vector3f get1(){
+ return pointa;
+ }
+
+ public Vector3f get2(){
+ return pointb;
+ }
+
+ public Vector3f get3(){
+ return pointc;
+ }
+
+ public Vector3f getNormal(){
+ Vector3f normal = new Vector3f(pointb);
+ normal.subtractLocal(pointa).crossLocal(pointc.x-pointa.x, pointc.y-pointa.y, pointc.z-pointa.z);
+ normal.normalizeLocal();
+ return normal;
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/optimize/Octnode.java b/engine/src/tools/jme3tools/optimize/Octnode.java
new file mode 100644
index 0000000..e9a0080
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/Octnode.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.optimize;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Ray;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.debug.WireBox;
+import java.util.*;
+
+public class Octnode {
+
+ static final Vector3f[] extentMult = new Vector3f[]
+ {
+ new Vector3f( 1, 1, 1), // right top forw
+ new Vector3f(-1, 1, 1), // left top forw
+ new Vector3f( 1,-1, 1), // right bot forw
+ new Vector3f(-1,-1, 1), // left bot forw
+ new Vector3f( 1, 1,-1), // right top back
+ new Vector3f(-1, 1,-1), // left top back
+ new Vector3f( 1,-1,-1), // right bot back
+ new Vector3f(-1,-1,-1) // left bot back
+ };
+
+ final BoundingBox bbox;
+ final ArrayList<OCTTriangle> tris;
+ Geometry[] geoms;
+ final Octnode[] children = new Octnode[8];
+ boolean leaf = false;
+ FastOctnode fastNode;
+
+ public Octnode(BoundingBox bbox, ArrayList<OCTTriangle> tris){
+ this.bbox = bbox;
+ this.tris = tris;
+ }
+
+ private BoundingBox getChildBound(int side){
+ float extent = bbox.getXExtent() * 0.5f;
+ Vector3f center = new Vector3f(bbox.getCenter().x + extent * extentMult[side].x,
+ bbox.getCenter().y + extent * extentMult[side].y,
+ bbox.getCenter().z + extent * extentMult[side].z);
+ return new BoundingBox(center, extent, extent, extent);
+ }
+
+ private float getAdditionCost(BoundingBox bbox, OCTTriangle t){
+ if (bbox.intersects(t.get1(), t.get2(), t.get3())){
+ float d1 = bbox.distanceToEdge(t.get1());
+ float d2 = bbox.distanceToEdge(t.get2());
+ float d3 = bbox.distanceToEdge(t.get3());
+ return d1 + d2 + d3;
+ }
+ return Float.POSITIVE_INFINITY;
+ }
+
+ private void expandBoxToContainTri(BoundingBox bbox, OCTTriangle t){
+ Vector3f min = bbox.getMin(null);
+ Vector3f max = bbox.getMax(null);
+ BoundingBox.checkMinMax(min, max, t.get1());
+ BoundingBox.checkMinMax(min, max, t.get2());
+ BoundingBox.checkMinMax(min, max, t.get3());
+ bbox.setMinMax(min, max);
+ }
+
+ private boolean contains(BoundingBox bbox, OCTTriangle t){
+ if (bbox.contains(t.get1()) &&
+ bbox.contains(t.get2()) &&
+ bbox.contains(t.get3())){
+ return true;
+ }
+ return false;
+ }
+
+ public void subdivide(int depth, int minTrisPerNode){
+ if (tris == null || depth > 50 || bbox.getVolume() < 0.01f || tris.size() < minTrisPerNode){
+ // no need to subdivide anymore
+ leaf = true;
+ return;
+ }
+
+ ArrayList<OCTTriangle> keepTris = new ArrayList<OCTTriangle>();
+ ArrayList[] trisForChild = new ArrayList[8];
+ BoundingBox[] boxForChild = new BoundingBox[8];
+ // create boxes for children
+ for (int i = 0; i < 8; i++){
+ boxForChild[i] = getChildBound(i);
+ trisForChild[i] = new ArrayList<Triangle>();
+ }
+
+ for (OCTTriangle t : tris){
+ float lowestCost = Float.POSITIVE_INFINITY;
+ int lowestIndex = -1;
+ int numIntersecting = 0;
+ for (int i = 0; i < 8; i++){
+ BoundingBox childBox = boxForChild[i];
+ float cost = getAdditionCost(childBox, t);
+ if (cost < lowestCost){
+ lowestCost = cost;
+ lowestIndex = i;
+ numIntersecting++;
+ }
+ }
+ if (numIntersecting < 8 && lowestIndex > -1){
+ trisForChild[lowestIndex].add(t);
+ expandBoxToContainTri(boxForChild[lowestIndex], t);
+ }else{
+ keepTris.add(t);
+ }
+// boolean wasAdded = false;
+// for (int i = 0; i < 8; i++){
+// BoundingBox childBox = boxForChild[i];
+// if (contains(childBox, t)){
+// trisForChild[i].add(t);
+// wasAdded = true;
+// break;
+// }
+// }
+// if (!wasAdded){
+// keepTris.add(t);
+// }
+ }
+ tris.retainAll(keepTris);
+ for (int i = 0; i < 8; i++){
+ if (trisForChild[i].size() > 0){
+ children[i] = new Octnode(boxForChild[i], trisForChild[i]);
+ children[i].subdivide(depth + 1, minTrisPerNode);
+ }
+ }
+ }
+
+ public void subdivide(int minTrisPerNode){
+ subdivide(0, minTrisPerNode);
+ }
+
+ public void createFastOctnode(List<Geometry> globalGeomList){
+ fastNode = new FastOctnode();
+
+ if (geoms != null){
+ Collection<Geometry> geomsColl = Arrays.asList(geoms);
+ List<Geometry> myOptimizedList = GeometryBatchFactory.makeBatches(geomsColl);
+
+ int startIndex = globalGeomList.size();
+ globalGeomList.addAll(myOptimizedList);
+
+ fastNode.setOffset(startIndex);
+ fastNode.length = myOptimizedList.size();
+ }else{
+ fastNode.setOffset(0);
+ fastNode.length = 0;
+ }
+
+ for (int i = 0; i < 8; i++){
+ if (children[i] != null){
+ children[i].createFastOctnode(globalGeomList);
+ }
+ }
+ }
+
+ public void generateFastOctnodeLinks(Octnode parent, Octnode nextSibling, int side){
+ fastNode.setSide(side);
+ fastNode.next = nextSibling != null ? nextSibling.fastNode : null;
+
+ // We set the next sibling property by going in reverse order
+ Octnode prev = null;
+ for (int i = 7; i >= 0; i--){
+ if (children[i] != null){
+ children[i].generateFastOctnodeLinks(this, prev, i);
+ prev = children[i];
+ }
+ }
+ fastNode.child = prev != null ? prev.fastNode : null;
+ }
+
+ private void generateRenderSetNoCheck(Set<Geometry> renderSet, Camera cam){
+ if (geoms != null){
+ renderSet.addAll(Arrays.asList(geoms));
+ }
+ for (int i = 0; i < 8; i++){
+ if (children[i] != null){
+ children[i].generateRenderSetNoCheck(renderSet, cam);
+ }
+ }
+ }
+
+ public void generateRenderSet(Set<Geometry> renderSet, Camera cam){
+// generateRenderSetNoCheck(renderSet, cam);
+
+ bbox.setCheckPlane(0);
+ cam.setPlaneState(0);
+ Camera.FrustumIntersect result = cam.contains(bbox);
+ if (result != Camera.FrustumIntersect.Outside){
+ if (geoms != null){
+ renderSet.addAll(Arrays.asList(geoms));
+ }
+ for (int i = 0; i < 8; i++){
+ if (children[i] != null){
+ if (result == Camera.FrustumIntersect.Inside){
+ children[i].generateRenderSetNoCheck(renderSet, cam);
+ }else{
+ children[i].generateRenderSet(renderSet, cam);
+ }
+ }
+ }
+ }
+ }
+
+ public void collectTriangles(Geometry[] inGeoms){
+ if (tris.size() > 0){
+ List<Geometry> geomsList = TriangleCollector.gatherTris(inGeoms, tris);
+ geoms = new Geometry[geomsList.size()];
+ geomsList.toArray(geoms);
+ }else{
+ geoms = null;
+ }
+ for (int i = 0; i < 8; i++){
+ if (children[i] != null){
+ children[i].collectTriangles(inGeoms);
+ }
+ }
+ }
+
+ public void renderBounds(RenderQueue rq, Matrix4f transform, WireBox box, Material mat){
+ int numChilds = 0;
+ for (int i = 0; i < 8; i++){
+ if (children[i] != null){
+ numChilds ++;
+ break;
+ }
+ }
+ if (geoms != null && numChilds == 0){
+ BoundingBox bbox2 = new BoundingBox(bbox);
+ bbox.transform(transform, bbox2);
+// WireBox box = new WireBox(bbox2.getXExtent(), bbox2.getYExtent(),
+// bbox2.getZExtent());
+// WireBox box = new WireBox(1,1,1);
+
+ Geometry geom = new Geometry("bound", box);
+ geom.setLocalTranslation(bbox2.getCenter());
+ geom.setLocalScale(bbox2.getXExtent(), bbox2.getYExtent(),
+ bbox2.getZExtent());
+ geom.updateGeometricState();
+ geom.setMaterial(mat);
+ rq.addToQueue(geom, Bucket.Opaque);
+ box = null;
+ geom = null;
+ }
+ for (int i = 0; i < 8; i++){
+ if (children[i] != null){
+ children[i].renderBounds(rq, transform, box, mat);
+ }
+ }
+ }
+
+ public final void intersectWhere(Ray r, Geometry[] geoms, float sceneMin, float sceneMax,
+ CollisionResults results){
+ for (OCTTriangle t : tris){
+ float d = r.intersects(t.get1(), t.get2(), t.get3());
+ if (Float.isInfinite(d))
+ continue;
+
+ Vector3f contactPoint = new Vector3f(r.getDirection()).multLocal(d).addLocal(r.getOrigin());
+ CollisionResult result = new CollisionResult(geoms[t.getGeometryIndex()],
+ contactPoint,
+ d,
+ t.getTriangleIndex());
+ results.addCollision(result);
+ }
+ for (int i = 0; i < 8; i++){
+ Octnode child = children[i];
+ if (child == null)
+ continue;
+
+ if (child.bbox.intersects(r)){
+ child.intersectWhere(r, geoms, sceneMin, sceneMax, results);
+ }
+ }
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/optimize/Octree.java b/engine/src/tools/jme3tools/optimize/Octree.java
new file mode 100644
index 0000000..880f984
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/Octree.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.optimize;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.CollisionResults;
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Ray;
+import com.jme3.math.Triangle;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireBox;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class Octree {
+
+ private final ArrayList<OCTTriangle> allTris = new ArrayList<OCTTriangle>();
+ private final Geometry[] geoms;
+ private final BoundingBox bbox;
+ private final int minTrisPerNode;
+ private Octnode root;
+
+ private CollisionResults boundResults = new CollisionResults();
+
+ private static List<Geometry> getGeometries(Spatial scene){
+ if (scene instanceof Geometry){
+ List<Geometry> geomList = new ArrayList<Geometry>(1);
+ geomList.add((Geometry) scene);
+ return geomList;
+ }else if (scene instanceof Node){
+ Node n = (Node) scene;
+ List<Geometry> geoms = new ArrayList<Geometry>();
+ for (Spatial child : n.getChildren()){
+ geoms.addAll(getGeometries(child));
+ }
+ return geoms;
+ }else{
+ throw new UnsupportedOperationException("Unsupported scene element class");
+ }
+ }
+
+ public Octree(Spatial scene, int minTrisPerNode){
+ scene.updateGeometricState();
+
+ List<Geometry> geomsList = getGeometries(scene);
+ geoms = new Geometry[geomsList.size()];
+ geomsList.toArray(geoms);
+ // generate bound box for all geom
+ bbox = new BoundingBox();
+ for (Geometry geom : geoms){
+ BoundingVolume bv = geom.getWorldBound();
+ bbox.mergeLocal(bv);
+ }
+
+ // set largest extent
+ float extent = Math.max(bbox.getXExtent(), Math.max(bbox.getYExtent(), bbox.getZExtent()));
+ bbox.setXExtent(extent);
+ bbox.setYExtent(extent);
+ bbox.setZExtent(extent);
+
+ this.minTrisPerNode = minTrisPerNode;
+
+ Triangle t = new Triangle();
+ for (int g = 0; g < geoms.length; g++){
+ Mesh m = geoms[g].getMesh();
+ for (int i = 0; i < m.getTriangleCount(); i++){
+ m.getTriangle(i, t);
+ OCTTriangle ot = new OCTTriangle(t.get1(), t.get2(), t.get3(), i, g);
+ allTris.add(ot);
+ // convert triangle to world space
+// geom.getWorldTransform().transformVector(t.get1(), t.get1());
+// geom.getWorldTransform().transformVector(t.get2(), t.get2());
+// geom.getWorldTransform().transformVector(t.get3(), t.get3());
+ }
+ }
+ }
+
+ public Octree(Spatial scene){
+ this(scene,11);
+ }
+
+ public void construct(){
+ root = new Octnode(bbox, allTris);
+ root.subdivide(minTrisPerNode);
+ root.collectTriangles(geoms);
+ }
+
+ public void createFastOctnodes(List<Geometry> globalGeomList){
+ root.createFastOctnode(globalGeomList);
+ }
+
+ public BoundingBox getBound(){
+ return bbox;
+ }
+
+ public FastOctnode getFastRoot(){
+ return root.fastNode;
+ }
+
+ public void generateFastOctnodeLinks(){
+ root.generateFastOctnodeLinks(null, null, 0);
+ }
+
+ public void generateRenderSet(Set<Geometry> renderSet, Camera cam){
+ root.generateRenderSet(renderSet, cam);
+ }
+
+ public void renderBounds(RenderQueue rq, Matrix4f transform, WireBox box, Material mat){
+ root.renderBounds(rq, transform, box, mat);
+ }
+
+ public void intersect(Ray r, float farPlane, Geometry[] geoms, CollisionResults results){
+ boundResults.clear();
+ bbox.collideWith(r, boundResults);
+ if (boundResults.size() > 0){
+ float tMin = boundResults.getClosestCollision().getDistance();
+ float tMax = boundResults.getFarthestCollision().getDistance();
+
+ tMin = Math.max(tMin, 0);
+ tMax = Math.min(tMax, farPlane);
+
+ root.intersectWhere(r, geoms, tMin, tMax, results);
+ }
+ }
+}
diff --git a/engine/src/tools/jme3tools/optimize/TestCollector.java b/engine/src/tools/jme3tools/optimize/TestCollector.java
new file mode 100644
index 0000000..cc451aa
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/TestCollector.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.optimize;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Quad;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestCollector {
+
+ public static void main(String[] args){
+ Vector3f z = Vector3f.ZERO;
+ Geometry g = new Geometry("quad", new Quad(2,2));
+ Geometry g2 = new Geometry("quad", new Quad(2,2));
+ List<OCTTriangle> tris = new ArrayList<OCTTriangle>();
+ tris.add(new OCTTriangle(z, z, z, 1, 0));
+ tris.add(new OCTTriangle(z, z, z, 0, 1));
+ List<Geometry> firstOne = TriangleCollector.gatherTris(new Geometry[]{ g, g2 }, tris);
+ System.out.println(firstOne.get(0).getMesh());
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/optimize/TextureAtlas.java b/engine/src/tools/jme3tools/optimize/TextureAtlas.java
new file mode 100644
index 0000000..13c8b69
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/TextureAtlas.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3tools.optimize;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.material.MatParamTexture;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.BufferUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <b><code>TextureAtlas</code></b> allows combining multiple textures to one texture atlas.
+ *
+ * <p>After the TextureAtlas has been created with a certain size, textures can be added for
+ * freely chosen "map names". The textures are automatically placed on the atlas map and the
+ * image data is stored in a byte array for each map name. Later each map can be retrieved as
+ * a Texture to be used further in materials.</p>
+ *
+ * <p>The first map name used is the "master map" that defines new locations on the atlas. Secondary
+ * textures (other map names) have to reference a texture of the master map to position the texture
+ * on the secondary map. This is necessary as the maps share texture coordinates and thus need to be
+ * placed at the same location on both maps.</p>
+ *
+ * <p>The helper methods that work with <code>Geometry</code> objects handle the <em>DiffuseMap</em> or <em>ColorMap</em> as the master map and
+ * additionally handle <em>NormalMap</em> and <em>SpecularMap</em> as secondary maps.</p>
+ *
+ * <p>The textures are referenced by their <b>asset key name</b> and for each texture the location
+ * inside the atlas is stored. A texture with an existing key name is never added more than once
+ * to the atlas. You can access the information for each texture or geometry texture via helper methods.</p>
+ *
+ * <p>The TextureAtlas also allows you to change the texture coordinates of a mesh or geometry
+ * to point at the new locations of its texture inside the atlas (if the texture exists inside the atlas).</p>
+ *
+ * <p>Note that models that use texture coordinates outside the 0-1 range (repeating/wrapping textures)
+ * will not work correctly as their new coordinates leak into other parts of the atlas and thus display
+ * other textures instead of repeating the texture.</p>
+ *
+ * <p>Also note that textures are not scaled and the atlas needs to be large enough to hold all textures.
+ * All methods that allow adding textures return false if the texture could not be added due to the
+ * atlas being full. Furthermore secondary textures (normal, spcular maps etc.) have to be the same size
+ * as the main (e.g. DiffuseMap) texture.</p>
+ *
+ * <p><b>Usage examples</b></p>
+ * Create one geometry out of several geometries that are loaded from a j3o file:
+ * <pre>
+ * Node scene = assetManager.loadModel("Scenes/MyScene.j3o");
+ * Geometry geom = TextureAtlas.makeAtlasBatch(scene);
+ * rootNode.attachChild(geom);
+ * </pre>
+ * Create a texture atlas and change the texture coordinates of one geometry:
+ * <pre>
+ * Node scene = assetManager.loadModel("Scenes/MyScene.j3o");
+ * //either auto-create from node:
+ * TextureAtlas atlas = TextureAtlas.createAtlas(scene);
+ * //or create manually by adding textures or geometries with textures
+ * TextureAtlas atlas = new TextureAtlas(1024,1024);
+ * atlas.addTexture(myTexture, "DiffuseMap");
+ * atlas.addGeometry(myGeometry);
+ * //create material and set texture
+ * Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
+ * mat.setTexture("DiffuseMap", atlas.getAtlasTexture("DiffuseMap"));
+ * //change one geometry to use atlas, apply texture coordinates and replace material.
+ * Geometry geom = scene.getChild("MyGeometry");
+ * atlas.applyCoords(geom);
+ * geom.setMaterial(mat);
+ * </pre>
+ *
+ * @author normenhansen, Lukasz Bruun - lukasz.dk
+ */
+public class TextureAtlas {
+
+ private static final Logger logger = Logger.getLogger(TextureAtlas.class.getName());
+ private Map<String, byte[]> images;
+ private int atlasWidth, atlasHeight;
+ private Format format = Format.ABGR8;
+ private Node root;
+ private Map<String, TextureAtlasTile> locationMap;
+ private Map<String, String> mapNameMap;
+ private String rootMapName;
+
+ public TextureAtlas(int width, int height) {
+ this.atlasWidth = width;
+ this.atlasHeight = height;
+ root = new Node(0, 0, width, height);
+ locationMap = new TreeMap<String, TextureAtlasTile>();
+ mapNameMap = new HashMap<String, String>();
+ }
+
+ /**
+ * Add a geometries DiffuseMap (or ColorMap), NormalMap and SpecularMap to the atlas.
+ * @param geometry
+ * @return false if the atlas is full.
+ */
+ public boolean addGeometry(Geometry geometry) {
+ Texture diffuse = getMaterialTexture(geometry, "DiffuseMap");
+ Texture normal = getMaterialTexture(geometry, "NormalMap");
+ Texture specular = getMaterialTexture(geometry, "SpecularMap");
+ if (diffuse == null) {
+ diffuse = getMaterialTexture(geometry, "ColorMap");
+
+ }
+ if (diffuse != null && diffuse.getKey() != null) {
+ String keyName = diffuse.getKey().toString();
+ if (!addTexture(diffuse, "DiffuseMap")) {
+ return false;
+ } else {
+ if (normal != null && normal.getKey() != null) {
+ addTexture(diffuse, "NormalMap", keyName);
+ }
+ if (specular != null && specular.getKey() != null) {
+ addTexture(specular, "SpecularMap", keyName);
+ }
+ }
+ return true;
+ }
+ return true;
+ }
+
+ /**
+ * Add a texture for a specific map name
+ * @param texture A texture to add to the atlas.
+ * @param mapName A freely chosen map name that can be later retrieved as a Texture. The first map name supplied will be the master map.
+ * @return false if the atlas is full.
+ */
+ public boolean addTexture(Texture texture, String mapName) {
+ if (texture == null) {
+ throw new IllegalStateException("Texture cannot be null!");
+ }
+ String name = textureName(texture);
+ if (texture.getImage() != null && name != null) {
+ return addImage(texture.getImage(), name, mapName, null);
+ } else {
+ throw new IllegalStateException("Texture has no asset key name!");
+ }
+ }
+
+ /**
+ * Add a texture for a specific map name at the location of another existing texture on the master map.
+ * @param texture A texture to add to the atlas.
+ * @param mapName A freely chosen map name that can be later retrieved as a Texture.
+ * @param masterTexture The master texture for determining the location, it has to exist in tha master map.
+ */
+ public void addTexture(Texture texture, String mapName, Texture masterTexture) {
+ String sourceTextureName = textureName(masterTexture);
+ if (sourceTextureName == null) {
+ throw new IllegalStateException("Supplied master map texture has no asset key name!");
+ } else {
+ addTexture(texture, mapName, sourceTextureName);
+ }
+ }
+
+ /**
+ * Add a texture for a specific map name at the location of another existing texture (on the master map).
+ * @param texture A texture to add to the atlas.
+ * @param mapName A freely chosen map name that can be later retrieved as a Texture.
+ * @param sourceTextureName Name of the master map used for the location.
+ */
+ public void addTexture(Texture texture, String mapName, String sourceTextureName) {
+ if (texture == null) {
+ throw new IllegalStateException("Texture cannot be null!");
+ }
+ String name = textureName(texture);
+ if (texture.getImage() != null && name != null) {
+ addImage(texture.getImage(), name, mapName, sourceTextureName);
+ } else {
+ throw new IllegalStateException("Texture has no asset key name!");
+ }
+ }
+
+ private String textureName(Texture texture) {
+ if (texture == null) {
+ return null;
+ }
+ AssetKey key = texture.getKey();
+ if (key != null) {
+ return key.toString();
+ } else {
+ return null;
+ }
+ }
+
+ private boolean addImage(Image image, String name, String mapName, String sourceTextureName) {
+ if (rootMapName == null) {
+ rootMapName = mapName;
+ }
+ if (sourceTextureName == null && !rootMapName.equals(mapName)) {
+ throw new IllegalStateException("Atlas already has a master map called " + rootMapName + "."
+ + " Textures for new maps have to use a texture from the master map for their location.");
+ }
+ TextureAtlasTile location = locationMap.get(name);
+ if (location != null) {
+ //have location for texture
+ if (!mapName.equals(mapNameMap.get(name))) {
+ logger.log(Level.WARNING, "Same texture " + name + " is used in different maps! (" + mapName + " and " + mapNameMap.get(name) + "). Location will be based on location in " + mapNameMap.get(name) + "!");
+ drawImage(image, location.getX(), location.getY(), mapName);
+ return true;
+ } else {
+ return true;
+ }
+ } else if (sourceTextureName == null) {
+ //need to make new tile
+ Node node = root.insert(image);
+ if (node == null) {
+ return false;
+ }
+ location = node.location;
+ } else {
+ //got old tile to align to
+ location = locationMap.get(sourceTextureName);
+ if (location == null) {
+ throw new IllegalStateException("Cannot find master map texture for " + name + ".");
+ } else if (location.width != image.getWidth() || location.height != image.getHeight()) {
+ throw new IllegalStateException(mapName + " " + name + " does not fit " + rootMapName + " tile size. Make sure all textures (diffuse, normal, specular) for one model are the same size.");
+ }
+ }
+ mapNameMap.put(name, mapName);
+ locationMap.put(name, location);
+ drawImage(image, location.getX(), location.getY(), mapName);
+ return true;
+ }
+
+ private void drawImage(Image source, int x, int y, String mapName) {
+ if (images == null) {
+ images = new HashMap<String, byte[]>();
+ }
+ byte[] image = images.get(mapName);
+ if (image == null) {
+ image = new byte[atlasWidth * atlasHeight * 4];
+ images.put(mapName, image);
+ }
+ //TODO: all buffers?
+ ByteBuffer sourceData = source.getData(0);
+ int height = source.getHeight();
+ int width = source.getWidth();
+ Image newImage = null;
+ for (int yPos = 0; yPos < height; yPos++) {
+ for (int xPos = 0; xPos < width; xPos++) {
+ int i = ((xPos + x) + (yPos + y) * atlasWidth) * 4;
+ if (source.getFormat() == Format.ABGR8) {
+ int j = (xPos + yPos * width) * 4;
+ image[i] = sourceData.get(j); //a
+ image[i + 1] = sourceData.get(j + 1); //b
+ image[i + 2] = sourceData.get(j + 2); //g
+ image[i + 3] = sourceData.get(j + 3); //r
+ } else if (source.getFormat() == Format.BGR8) {
+ int j = (xPos + yPos * width) * 3;
+ image[i] = 1; //a
+ image[i + 1] = sourceData.get(j); //b
+ image[i + 2] = sourceData.get(j + 1); //g
+ image[i + 3] = sourceData.get(j + 2); //r
+ } else if (source.getFormat() == Format.RGB8) {
+ int j = (xPos + yPos * width) * 3;
+ image[i] = 1; //a
+ image[i + 1] = sourceData.get(j + 2); //b
+ image[i + 2] = sourceData.get(j + 1); //g
+ image[i + 3] = sourceData.get(j); //r
+ } else if (source.getFormat() == Format.RGBA8) {
+ int j = (xPos + yPos * width) * 4;
+ image[i] = sourceData.get(j + 3); //a
+ image[i + 1] = sourceData.get(j + 2); //b
+ image[i + 2] = sourceData.get(j + 1); //g
+ image[i + 3] = sourceData.get(j); //r
+ } else if (source.getFormat() == Format.Luminance8) {
+ int j = (xPos + yPos * width) * 1;
+ image[i] = 1; //a
+ image[i + 1] = sourceData.get(j); //b
+ image[i + 2] = sourceData.get(j); //g
+ image[i + 3] = sourceData.get(j); //r
+ } else if (source.getFormat() == Format.Luminance8Alpha8) {
+ int j = (xPos + yPos * width) * 2;
+ image[i] = sourceData.get(j + 1); //a
+ image[i + 1] = sourceData.get(j); //b
+ image[i + 2] = sourceData.get(j); //g
+ image[i + 3] = sourceData.get(j); //r
+ } else {
+ //ImageToAwt conversion
+ if (newImage == null) {
+ newImage = convertImageToAwt(source);
+ if (newImage != null) {
+ source = newImage;
+ sourceData = source.getData(0);
+ int j = (xPos + yPos * width) * 4;
+ image[i] = sourceData.get(j); //a
+ image[i + 1] = sourceData.get(j + 1); //b
+ image[i + 2] = sourceData.get(j + 2); //g
+ image[i + 3] = sourceData.get(j + 3); //r
+ }else{
+ throw new UnsupportedOperationException("Cannot draw or convert textures with format " + source.getFormat());
+ }
+ } else {
+ throw new UnsupportedOperationException("Cannot draw textures with format " + source.getFormat());
+ }
+ }
+ }
+ }
+ }
+
+ private Image convertImageToAwt(Image source) {
+ //use awt dependent classes without actual dependency via reflection
+ try {
+ Class clazz = Class.forName("jme3tools.converters.ImageToAwt");
+ if (clazz == null) {
+ return null;
+ }
+ Image newImage = new Image(format, source.getWidth(), source.getHeight(), BufferUtils.createByteBuffer(source.getWidth() * source.getHeight() * 4));
+ clazz.getMethod("convert", Image.class, Image.class).invoke(clazz.newInstance(), source, newImage);
+ return newImage;
+ } catch (InstantiationException ex) {
+ } catch (IllegalAccessException ex) {
+ } catch (IllegalArgumentException ex) {
+ } catch (InvocationTargetException ex) {
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ } catch (ClassNotFoundException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Get the <code>TextureAtlasTile</code> for the given Texture
+ * @param texture The texture to retrieve the <code>TextureAtlasTile</code> for.
+ * @return
+ */
+ public TextureAtlasTile getAtlasTile(Texture texture) {
+ String sourceTextureName = textureName(texture);
+ if (sourceTextureName != null) {
+ return getAtlasTile(sourceTextureName);
+ }
+ return null;
+ }
+
+ /**
+ * Get the <code>TextureAtlasTile</code> for the given Texture
+ * @param assetName The texture to retrieve the <code>TextureAtlasTile</code> for.
+ * @return
+ */
+ private TextureAtlasTile getAtlasTile(String assetName) {
+ return locationMap.get(assetName);
+ }
+
+ /**
+ * Creates a new atlas texture for the given map name.
+ * @param mapName
+ * @return
+ */
+ public Texture getAtlasTexture(String mapName) {
+ if (images == null) {
+ return null;
+ }
+ byte[] image = images.get(mapName);
+ if (image != null) {
+ Texture2D tex = new Texture2D(new Image(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image)));
+ tex.setMagFilter(Texture.MagFilter.Bilinear);
+ tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap);
+ tex.setWrap(Texture.WrapMode.Clamp);
+ return tex;
+ }
+ return null;
+ }
+
+ /**
+ * Applies the texture coordinates to the given geometry
+ * if its DiffuseMap or ColorMap exists in the atlas.
+ * @param geom The geometry to change the texture coordinate buffer on.
+ * @return true if texture has been found and coords have been changed, false otherwise.
+ */
+ public boolean applyCoords(Geometry geom) {
+ return applyCoords(geom, 0, geom.getMesh());
+ }
+
+ /**
+ * Applies the texture coordinates to the given output mesh
+ * if the DiffuseMap or ColorMap of the input geometry exist in the atlas.
+ * @param geom The geometry to change the texture coordinate buffer on.
+ * @param offset Target buffer offset.
+ * @param outMesh The mesh to set the coords in (can be same as input).
+ * @return true if texture has been found and coords have been changed, false otherwise.
+ */
+ public boolean applyCoords(Geometry geom, int offset, Mesh outMesh) {
+ Mesh inMesh = geom.getMesh();
+ geom.computeWorldMatrix();
+
+ VertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord);
+ VertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord);
+
+ if (inBuf == null || outBuf == null) {
+ throw new IllegalStateException("Geometry mesh has no texture coordinate buffer.");
+ }
+
+ Texture tex = getMaterialTexture(geom, "DiffuseMap");
+ if (tex == null) {
+ tex = getMaterialTexture(geom, "ColorMap");
+
+ }
+ if (tex != null) {
+ TextureAtlasTile tile = getAtlasTile(tex);
+ if (tile != null) {
+ FloatBuffer inPos = (FloatBuffer) inBuf.getData();
+ FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+ tile.transformTextureCoords(inPos, offset, outPos);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new IllegalStateException("Geometry has no proper texture.");
+ }
+ }
+
+ /**
+ * Create a texture atlas for the given root node, containing DiffuseMap, NormalMap and SpecularMap.
+ * @param root The rootNode to create the atlas for.
+ * @param atlasSize The size of the atlas (width and height).
+ * @return Null if the atlas cannot be created because not all textures fit.
+ */
+ public static TextureAtlas createAtlas(Spatial root, int atlasSize) {
+ List<Geometry> geometries = new ArrayList<Geometry>();
+ GeometryBatchFactory.gatherGeoms(root, geometries);
+ TextureAtlas atlas = new TextureAtlas(atlasSize, atlasSize);
+ for (Geometry geometry : geometries) {
+ if (!atlas.addGeometry(geometry)) {
+ logger.log(Level.WARNING, "Texture atlas size too small, cannot add all textures");
+ return null;
+ }
+ }
+ return atlas;
+ }
+
+ /**
+ * Creates one geometry out of the given root spatial and merges all single
+ * textures into one texture of the given size.
+ * @param spat The root spatial of the scene to batch
+ * @param mgr An assetmanager that can be used to create the material.
+ * @param atlasSize A size for the atlas texture, it has to be large enough to hold all single textures.
+ * @return A new geometry that uses the generated texture atlas and merges all meshes of the root spatial, null if the atlas cannot be created because not all textures fit.
+ */
+ public static Geometry makeAtlasBatch(Spatial spat, AssetManager mgr, int atlasSize) {
+ List<Geometry> geometries = new ArrayList<Geometry>();
+ GeometryBatchFactory.gatherGeoms(spat, geometries);
+ TextureAtlas atlas = createAtlas(spat, atlasSize);
+ if (atlas == null) {
+ return null;
+ }
+ Geometry geom = new Geometry();
+ Mesh mesh = new Mesh();
+ GeometryBatchFactory.mergeGeometries(geometries, mesh);
+ applyAtlasCoords(geometries, mesh, atlas);
+ mesh.updateCounts();
+ mesh.updateBound();
+ geom.setMesh(mesh);
+
+ Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
+ mat.getAdditionalRenderState().setAlphaTest(true);
+ Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap");
+ Texture normalMap = atlas.getAtlasTexture("NormalMap");
+ Texture specularMap = atlas.getAtlasTexture("SpecularMap");
+ if (diffuseMap != null) {
+ mat.setTexture("DiffuseMap", diffuseMap);
+ }
+ if (normalMap != null) {
+ mat.setTexture("NormalMap", normalMap);
+ }
+ if (specularMap != null) {
+ mat.setTexture("SpecularMap", specularMap);
+ }
+ mat.setFloat("Shininess", 16.0f);
+
+ geom.setMaterial(mat);
+ return geom;
+ }
+
+ private static void applyAtlasCoords(List<Geometry> geometries, Mesh outMesh, TextureAtlas atlas) {
+ int globalVertIndex = 0;
+
+ for (Geometry geom : geometries) {
+ Mesh inMesh = geom.getMesh();
+ geom.computeWorldMatrix();
+
+ int geomVertCount = inMesh.getVertexCount();
+
+ VertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord);
+ VertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord);
+
+ if (inBuf == null || outBuf == null) {
+ continue;
+ }
+
+ atlas.applyCoords(geom, globalVertIndex, outMesh);
+
+ globalVertIndex += geomVertCount;
+ }
+ }
+
+ private static Texture getMaterialTexture(Geometry geometry, String mapName) {
+ Material mat = geometry.getMaterial();
+ if (mat == null || mat.getParam(mapName) == null || !(mat.getParam(mapName) instanceof MatParamTexture)) {
+ return null;
+ }
+ MatParamTexture param = (MatParamTexture) mat.getParam(mapName);
+ Texture texture = param.getTextureValue();
+ if (texture == null) {
+ return null;
+ }
+ return texture;
+
+
+ }
+
+ private class Node {
+
+ public TextureAtlasTile location;
+ public Node child[];
+ public boolean occupied;
+
+ public Node(int x, int y, int width, int height) {
+ location = new TextureAtlasTile(x, y, width, height);
+ child = new Node[2];
+ child[0] = null;
+ child[1] = null;
+ occupied = false;
+ }
+
+ public boolean isLeaf() {
+ return child[0] == null && child[1] == null;
+ }
+
+ // Algorithm from http://www.blackpawn.com/texts/lightmaps/
+ public Node insert(Image image) {
+ if (!isLeaf()) {
+ Node newNode = child[0].insert(image);
+
+ if (newNode != null) {
+ return newNode;
+ }
+
+ return child[1].insert(image);
+ } else {
+ if (occupied) {
+ return null; // occupied
+ }
+
+ if (image.getWidth() > location.getWidth() || image.getHeight() > location.getHeight()) {
+ return null; // does not fit
+ }
+
+ if (image.getWidth() == location.getWidth() && image.getHeight() == location.getHeight()) {
+ occupied = true; // perfect fit
+ return this;
+ }
+
+ int dw = location.getWidth() - image.getWidth();
+ int dh = location.getHeight() - image.getHeight();
+
+ if (dw > dh) {
+ child[0] = new Node(location.getX(), location.getY(), image.getWidth(), location.getHeight());
+ child[1] = new Node(location.getX() + image.getWidth(), location.getY(), location.getWidth() - image.getWidth(), location.getHeight());
+ } else {
+ child[0] = new Node(location.getX(), location.getY(), location.getWidth(), image.getHeight());
+ child[1] = new Node(location.getX(), location.getY() + image.getHeight(), location.getWidth(), location.getHeight() - image.getHeight());
+ }
+
+ return child[0].insert(image);
+ }
+ }
+ }
+
+ public class TextureAtlasTile {
+
+ private int x;
+ private int y;
+ private int width;
+ private int height;
+
+ public TextureAtlasTile(int x, int y, int width, int height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Get the transformed texture coordinate for a given input location.
+ * @param previousLocation The old texture coordinate.
+ * @return The new texture coordinate inside the atlas.
+ */
+ public Vector2f getLocation(Vector2f previousLocation) {
+ float x = (float) getX() / (float) atlasWidth;
+ float y = (float) getY() / (float) atlasHeight;
+ float w = (float) getWidth() / (float) atlasWidth;
+ float h = (float) getHeight() / (float) atlasHeight;
+ Vector2f location = new Vector2f(x, y);
+ float prevX = previousLocation.x;
+ float prevY = previousLocation.y;
+ location.addLocal(prevX * w, prevY * h);
+ return location;
+ }
+
+ /**
+ * Transforms a whole texture coordinates buffer.
+ * @param inBuf The input texture buffer.
+ * @param offset The offset in the output buffer
+ * @param outBuf The output buffer.
+ */
+ public void transformTextureCoords(FloatBuffer inBuf, int offset, FloatBuffer outBuf) {
+ Vector2f tex = new Vector2f();
+
+ // offset is given in element units
+ // convert to be in component units
+ offset *= 2;
+
+ for (int i = 0; i < inBuf.capacity() / 2; i++) {
+ tex.x = inBuf.get(i * 2 + 0);
+ tex.y = inBuf.get(i * 2 + 1);
+ Vector2f location = getLocation(tex);
+ //TODO: add proper texture wrapping for atlases..
+ outBuf.put(offset + i * 2 + 0, location.x);
+ outBuf.put(offset + i * 2 + 1, location.y);
+ }
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+ }
+}
diff --git a/engine/src/tools/jme3tools/optimize/TriangleCollector.java b/engine/src/tools/jme3tools/optimize/TriangleCollector.java
new file mode 100644
index 0000000..9ce4ffa
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/TriangleCollector.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3tools.optimize;
+
+import com.jme3.light.Light;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.nio.Buffer;
+import java.nio.ShortBuffer;
+import java.util.*;
+
+public class TriangleCollector {
+
+ private static final GeomTriComparator comparator = new GeomTriComparator();
+
+ private static class GeomTriComparator implements Comparator<OCTTriangle> {
+ public int compare(OCTTriangle a, OCTTriangle b) {
+ if (a.getGeometryIndex() < b.getGeometryIndex()){
+ return -1;
+ }else if (a.getGeometryIndex() > b.getGeometryIndex()){
+ return 1;
+ }else{
+ return 0;
+ }
+ }
+ }
+
+ private static class Range {
+
+ private int start, length;
+
+ public Range(int start, int length) {
+ this.start = start;
+ this.length = length;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public int getStart() {
+ return start;
+ }
+
+ public void setStart(int start) {
+ this.start = start;
+ }
+
+ }
+
+ /**
+ * Grabs all the triangles specified in <code>tris</code> from the input array
+ * (using the indices OCTTriangle.getGeometryIndex() & OCTTriangle.getTriangleIndex())
+ * then organizes them into output geometry.
+ *
+ * @param inGeoms
+ * @param tris
+ * @return
+ */
+ public static final List<Geometry> gatherTris(Geometry[] inGeoms, List<OCTTriangle> tris){
+ Collections.sort(tris, comparator);
+ HashMap<Integer, Range> ranges = new HashMap<Integer, Range>();
+
+ for (int i = 0; i < tris.size(); i++){
+ Range r = ranges.get(tris.get(i).getGeometryIndex());
+ if (r != null){
+ // incremenet length
+ r.setLength(r.getLength()+1);
+ }else{
+ // set offset, length is 1
+ ranges.put(tris.get(i).getGeometryIndex(), new Range(i, 1));
+ }
+ }
+
+ List<Geometry> newGeoms = new ArrayList<Geometry>();
+ int[] vertIndicies = new int[3];
+ int[] newIndices = new int[3];
+ boolean[] vertexCreated = new boolean[3];
+ HashMap<Integer, Integer> indexCache = new HashMap<Integer, Integer>();
+ for (Map.Entry<Integer, Range> entry : ranges.entrySet()){
+ int inGeomIndex = entry.getKey().intValue();
+ int outOffset = entry.getValue().start;
+ int outLength = entry.getValue().length;
+
+ Geometry inGeom = inGeoms[inGeomIndex];
+ Mesh in = inGeom.getMesh();
+ Mesh out = new Mesh();
+
+ int outElementCount = outLength * 3;
+ ShortBuffer ib = BufferUtils.createShortBuffer(outElementCount);
+ out.setBuffer(Type.Index, 3, ib);
+
+ // generate output buffers based on input buffers
+ IntMap<VertexBuffer> bufs = in.getBuffers();
+ for (Entry<VertexBuffer> ent : bufs){
+ VertexBuffer vb = ent.getValue();
+ if (vb.getBufferType() == Type.Index)
+ continue;
+
+ // NOTE: we are not actually sure
+ // how many elements will be in this buffer.
+ // It will be compacted later.
+ Buffer b = VertexBuffer.createBuffer(vb.getFormat(),
+ vb.getNumComponents(),
+ outElementCount);
+
+ VertexBuffer outVb = new VertexBuffer(vb.getBufferType());
+ outVb.setNormalized(vb.isNormalized());
+ outVb.setupData(vb.getUsage(), vb.getNumComponents(), vb.getFormat(), b);
+ out.setBuffer(outVb);
+ }
+
+ int currentVertex = 0;
+ for (int i = outOffset; i < outOffset + outLength; i++){
+ OCTTriangle t = tris.get(i);
+
+ // find vertex indices for triangle t
+ in.getTriangle(t.getTriangleIndex(), vertIndicies);
+
+ // find indices in new buf
+ Integer i0 = indexCache.get(vertIndicies[0]);
+ Integer i1 = indexCache.get(vertIndicies[1]);
+ Integer i2 = indexCache.get(vertIndicies[2]);
+
+ // check which ones were not created
+ // if not created in new IB, create them
+ if (i0 == null){
+ vertexCreated[0] = true;
+ newIndices[0] = currentVertex++;
+ indexCache.put(vertIndicies[0], newIndices[0]);
+ }else{
+ newIndices[0] = i0.intValue();
+ vertexCreated[0] = false;
+ }
+ if (i1 == null){
+ vertexCreated[1] = true;
+ newIndices[1] = currentVertex++;
+ indexCache.put(vertIndicies[1], newIndices[1]);
+ }else{
+ newIndices[1] = i1.intValue();
+ vertexCreated[1] = false;
+ }
+ if (i2 == null){
+ vertexCreated[2] = true;
+ newIndices[2] = currentVertex++;
+ indexCache.put(vertIndicies[2], newIndices[2]);
+ }else{
+ newIndices[2] = i2.intValue();
+ vertexCreated[2] = false;
+ }
+
+ // if any verticies were created for this triangle
+ // copy them to the output mesh
+ IntMap<VertexBuffer> inbufs = in.getBuffers();
+ for (Entry<VertexBuffer> ent : inbufs){
+ VertexBuffer vb = ent.getValue();
+ if (vb.getBufferType() == Type.Index)
+ continue;
+
+ VertexBuffer outVb = out.getBuffer(vb.getBufferType());
+ // copy verticies that were created for this triangle
+ for (int v = 0; v < 3; v++){
+ if (!vertexCreated[v])
+ continue;
+
+ // copy triangle's attribute from one
+ // buffer to another
+ vb.copyElement(vertIndicies[v], outVb, newIndices[v]);
+ }
+ }
+
+ // write the indices onto the output index buffer
+ ib.put((short)newIndices[0])
+ .put((short)newIndices[1])
+ .put((short)newIndices[2]);
+ }
+ ib.clear();
+ indexCache.clear();
+
+ // since some verticies were cached, it means there's
+ // extra data in some buffers
+ IntMap<VertexBuffer> outbufs = out.getBuffers();
+ for (Entry<VertexBuffer> ent : outbufs){
+ VertexBuffer vb = ent.getValue();
+ if (vb.getBufferType() == Type.Index)
+ continue;
+
+ vb.compact(currentVertex);
+ }
+
+ out.updateBound();
+ out.updateCounts();
+ out.setStatic();
+ //out.setInterleaved();
+ Geometry outGeom = new Geometry("Geom"+entry.getKey(), out);
+ outGeom.setLocalTransform(inGeom.getWorldTransform());
+ outGeom.setMaterial(inGeom.getMaterial());
+ for (Light light : inGeom.getWorldLightList()){
+ outGeom.addLight(light);
+ }
+
+ outGeom.updateGeometricState();
+ newGeoms.add(outGeom);
+ }
+
+ return newGeoms;
+ }
+
+}
diff --git a/engine/src/tools/jme3tools/optimize/pvsnotes b/engine/src/tools/jme3tools/optimize/pvsnotes
new file mode 100644
index 0000000..61d83a5
--- /dev/null
+++ b/engine/src/tools/jme3tools/optimize/pvsnotes
@@ -0,0 +1,40 @@
+convert all leafs in octree to PvsNode, add to list pvsNodes
+
+for (every nodeX in pvsNodes):
+ for (every nodeY in pvsNodes):
+ if (nodeX == nodeY or nodeX adjecent or intersecting nodeY):
+ continue
+
+ setup camera for (nodeX, nodeY)
+ draw every node except nodeX & nodeY
+
+ turn on occlusion query
+ draw nodeY as bounding box
+ turn off occlusion query
+
+ if (numSamples > 0): // node is visible
+ add nodeY to nodeX's potentially visible set
+
+
+setup camera for node, sideI:
+
+ float width, height, near;
+
+ switch (sideI):
+ case X+
+ case X-
+ width = x extent
+ height = y extent
+ near = z extent / 2
+ case Y+
+ case Y-
+ width = x extent
+ height = z extent
+ near = y extent / 2
+ case Z+
+ case Z-
+ width = z extent
+ height = y extent
+ near = x extent / 2
+
+
diff --git a/engine/src/tools/jme3tools/savegame/SaveGame.java b/engine/src/tools/jme3tools/savegame/SaveGame.java
new file mode 100644
index 0000000..56a693e
--- /dev/null
+++ b/engine/src/tools/jme3tools/savegame/SaveGame.java
@@ -0,0 +1,118 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3tools.savegame;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.Savable;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.system.JmeSystem;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Tool for saving Savables as SaveGame entries in a system-dependent way.
+ * @author normenhansen
+ */
+public class SaveGame {
+
+ /**
+ * Saves a savable in a system-dependent way.
+ * @param gamePath A unique path for this game, e.g. com/mycompany/mygame
+ * @param dataName A unique name for this savegame, e.g. "save_001"
+ * @param data The Savable to save
+ */
+ public static void saveGame(String gamePath, String dataName, Savable data) {
+ BinaryExporter ex = BinaryExporter.getInstance();
+ OutputStream os = null;
+ try {
+ File daveFolder = new File(JmeSystem.getStorageFolder().getAbsolutePath() + File.separator + gamePath.replaceAll("/", File.separator));
+ if (!daveFolder.exists() && !daveFolder.mkdirs()) {
+ Logger.getLogger(SaveGame.class.getName()).log(Level.SEVERE, "Error creating save file!");
+ throw new IllegalStateException("SaveGame dataset cannot be created");
+ }
+ File saveFile = new File(daveFolder.getAbsolutePath() + File.separator + dataName);
+ if (!saveFile.exists()) {
+ if (!saveFile.createNewFile()) {
+ Logger.getLogger(SaveGame.class.getName()).log(Level.SEVERE, "Error creating save file!");
+ throw new IllegalStateException("SaveGame dataset cannot be created");
+ }
+ }
+ os = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(saveFile)));
+ ex.save(data, os);
+ } catch (IOException ex1) {
+ Logger.getLogger(SaveGame.class.getName()).log(Level.SEVERE, "Error saving data: {0}", ex1);
+ ex1.printStackTrace();
+ throw new IllegalStateException("SaveGame dataset cannot be saved");
+ } finally {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException ex1) {
+ Logger.getLogger(SaveGame.class.getName()).log(Level.SEVERE, "Error saving data: {0}", ex1);
+ ex1.printStackTrace();
+ throw new IllegalStateException("SaveGame dataset cannot be saved");
+ }
+ }
+ }
+
+ /**
+ * Loads a savable that has been saved on this system with saveGame() before.
+ * @param gamePath A unique path for this game, e.g. com/mycompany/mygame
+ * @param dataName A unique name for this savegame, e.g. "save_001"
+ * @return The savable that was saved
+ */
+ public static Savable loadGame(String gamePath, String dataName) {
+ return loadGame(gamePath, dataName, null);
+ }
+
+ /**
+ * Loads a savable that has been saved on this system with saveGame() before.
+ * @param gamePath A unique path for this game, e.g. com/mycompany/mygame
+ * @param dataName A unique name for this savegame, e.g. "save_001"
+ * @param manager Link to an AssetManager if required for loading the data (e.g. models with textures)
+ * @return The savable that was saved or null if none was found
+ */
+ public static Savable loadGame(String gamePath, String dataName, AssetManager manager) {
+ InputStream is = null;
+ Savable sav = null;
+ try {
+ File file = new File(JmeSystem.getStorageFolder().getAbsolutePath() + File.separator + gamePath.replaceAll("/", File.separator) + File.separator + dataName);
+ if(!file.exists()){
+ return null;
+ }
+ is = new GZIPInputStream(new BufferedInputStream(new FileInputStream(file)));
+ BinaryImporter imp = BinaryImporter.getInstance();
+ if (manager != null) {
+ imp.setAssetManager(manager);
+ }
+ sav = imp.load(is);
+ } catch (IOException ex) {
+ Logger.getLogger(SaveGame.class.getName()).log(Level.SEVERE, "Error loading data: {0}", ex);
+ ex.printStackTrace();
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ex) {
+ Logger.getLogger(SaveGame.class.getName()).log(Level.SEVERE, "Error loading data: {0}", ex);
+ ex.printStackTrace();
+ }
+ }
+ }
+ return sav;
+ }
+}
diff --git a/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java b/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java
new file mode 100644
index 0000000..c3be20a
--- /dev/null
+++ b/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java
@@ -0,0 +1,1511 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.xml;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.export.SavableClassUtil;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.*;
+import java.util.logging.Logger;
+import org.w3c.dom.*;
+
+/**
+ * Part of the jME XML IO system as introduced in the google code jmexml project.
+ *
+ * @author Kai Rabien (hevee) - original author of the code.google.com jmexml project
+ * @author Doug Daniels (dougnukem) - adjustments for jME 2.0 and Java 1.5
+ * @author blaine
+ */
+public class DOMInputCapsule implements InputCapsule {
+ private static final Logger logger =
+ Logger.getLogger(DOMInputCapsule.class .getName());
+
+ private Document doc;
+ private Element currentElem;
+ private XMLImporter importer;
+ private boolean isAtRoot = true;
+ private Map<String, Savable> referencedSavables = new HashMap<String, Savable>();
+
+ private int[] classHierarchyVersions;
+ private Savable savable;
+
+ public DOMInputCapsule(Document doc, XMLImporter importer) {
+ this.doc = doc;
+ this.importer = importer;
+ currentElem = doc.getDocumentElement();
+
+ String version = currentElem.getAttribute("format_version");
+ importer.formatVersion = version.equals("") ? 0 : Integer.parseInt(version);
+ }
+
+ public int getSavableVersion(Class<? extends Savable> desiredClass) {
+ if (classHierarchyVersions != null){
+ return SavableClassUtil.getSavedSavableVersion(savable, desiredClass,
+ classHierarchyVersions, importer.getFormatVersion());
+ }else{
+ return 0;
+ }
+ }
+
+ private static String decodeString(String s) {
+ if (s == null) {
+ return null;
+ }
+ s = s.replaceAll("\\&quot;", "\"").replaceAll("\\&lt;", "<").replaceAll("\\&amp;", "&");
+ return s;
+ }
+
+ private Element findFirstChildElement(Element parent) {
+ Node ret = parent.getFirstChild();
+ while (ret != null && (!(ret instanceof Element))) {
+ ret = ret.getNextSibling();
+ }
+ return (Element) ret;
+ }
+
+ private Element findChildElement(Element parent, String name) {
+ if (parent == null) {
+ return null;
+ }
+ Node ret = parent.getFirstChild();
+ while (ret != null && (!(ret instanceof Element) || !ret.getNodeName().equals(name))) {
+ ret = ret.getNextSibling();
+ }
+ return (Element) ret;
+ }
+
+ private Element findNextSiblingElement(Element current) {
+ Node ret = current.getNextSibling();
+ while (ret != null) {
+ if (ret instanceof Element) {
+ return (Element) ret;
+ }
+ ret = ret.getNextSibling();
+ }
+ return null;
+ }
+
+ public byte readByte(String name, byte defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Byte.parseByte(tmpString);
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public byte[] readByteArray(String name, byte[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of bytes for '" + name
+ + "'. size says " + requiredSize
+ + ", data contains "
+ + strings.length);
+ }
+ byte[] tmp = new byte[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ tmp[i] = Byte.parseByte(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public byte[][] readByteArray2D(String name, byte[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = currentElem.getChildNodes();
+ List<byte[]> byteArrays = new ArrayList<byte[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ byteArrays.add(readByteArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (byteArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + byteArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return byteArrays.toArray(new byte[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public int readInt(String name, int defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Integer.parseInt(tmpString);
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public int[] readIntArray(String name, int[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of ints for '" + name
+ + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ int[] tmp = new int[strings.length];
+ for (int i = 0; i < tmp.length; i++) {
+ tmp[i] = Integer.parseInt(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public int[][] readIntArray2D(String name, int[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+
+
+
+
+ NodeList nodes = currentElem.getChildNodes();
+ List<int[]> intArrays = new ArrayList<int[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ intArrays.add(readIntArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (intArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + intArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return intArrays.toArray(new int[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public float readFloat(String name, float defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Float.parseFloat(tmpString);
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public float[] readFloatArray(String name, float[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of floats for '" + name
+ + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ float[] tmp = new float[strings.length];
+ for (int i = 0; i < tmp.length; i++) {
+ tmp[i] = Float.parseFloat(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public float[][] readFloatArray2D(String name, float[][] defVal) throws IOException {
+ /* Why does this one method ignore the 'size attr.? */
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ int size_outer = Integer.parseInt(tmpEl.getAttribute("size_outer"));
+ int size_inner = Integer.parseInt(tmpEl.getAttribute("size_outer"));
+
+ float[][] tmp = new float[size_outer][size_inner];
+
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ for (int i = 0; i < size_outer; i++) {
+ tmp[i] = new float[size_inner];
+ for (int k = 0; k < size_inner; k++) {
+ tmp[i][k] = Float.parseFloat(strings[i]);
+ }
+ }
+ return tmp;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public double readDouble(String name, double defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Double.parseDouble(tmpString);
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public double[] readDoubleArray(String name, double[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of doubles for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ double[] tmp = new double[strings.length];
+ for (int i = 0; i < tmp.length; i++) {
+ tmp[i] = Double.parseDouble(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public double[][] readDoubleArray2D(String name, double[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = currentElem.getChildNodes();
+ List<double[]> doubleArrays = new ArrayList<double[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ doubleArrays.add(readDoubleArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (doubleArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + doubleArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return doubleArrays.toArray(new double[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public long readLong(String name, long defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Long.parseLong(tmpString);
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public long[] readLongArray(String name, long[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of longs for '" + name
+ + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ long[] tmp = new long[strings.length];
+ for (int i = 0; i < tmp.length; i++) {
+ tmp[i] = Long.parseLong(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public long[][] readLongArray2D(String name, long[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = currentElem.getChildNodes();
+ List<long[]> longArrays = new ArrayList<long[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ longArrays.add(readLongArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (longArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + longArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return longArrays.toArray(new long[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public short readShort(String name, short defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Short.parseShort(tmpString);
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public short[] readShortArray(String name, short[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of shorts for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ short[] tmp = new short[strings.length];
+ for (int i = 0; i < tmp.length; i++) {
+ tmp[i] = Short.parseShort(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public short[][] readShortArray2D(String name, short[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = currentElem.getChildNodes();
+ List<short[]> shortArrays = new ArrayList<short[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ shortArrays.add(readShortArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (shortArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + shortArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return shortArrays.toArray(new short[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public boolean readBoolean(String name, boolean defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return Boolean.parseBoolean(tmpString);
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public boolean[] readBooleanArray(String name, boolean[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of bools for '" + name
+ + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ boolean[] tmp = new boolean[strings.length];
+ for (int i = 0; i < tmp.length; i++) {
+ tmp[i] = Boolean.parseBoolean(strings[i]);
+ }
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public boolean[][] readBooleanArray2D(String name, boolean[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = currentElem.getChildNodes();
+ List<boolean[]> booleanArrays = new ArrayList<boolean[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ booleanArrays.add(readBooleanArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (booleanArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + booleanArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return booleanArrays.toArray(new boolean[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public String readString(String name, String defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ return decodeString(tmpString);
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public String[] readStringArray(String name, String[] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = tmpEl.getChildNodes();
+ List<String> strings = new ArrayList<String>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("String")) {
+ // Very unsafe assumption
+ strings.add(((Element) n).getAttributeNode("value").getValue());
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + strings.size());
+ }
+ return strings.toArray(new String[0]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public String[][] readStringArray2D(String name, String[][] defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ NodeList nodes = currentElem.getChildNodes();
+ List<String[]> stringArrays = new ArrayList<String[]>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().contains("array")) {
+ // Very unsafe assumption
+ stringArrays.add(readStringArray(n.getNodeName(), null));
+ }
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (stringArrays.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + stringArrays.size());
+ }
+ currentElem = (Element) currentElem.getParentNode();
+ return stringArrays.toArray(new String[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public BitSet readBitSet(String name, BitSet defVal) throws IOException {
+ String tmpString = currentElem.getAttribute(name);
+ if (tmpString == null || tmpString.length() < 1) return defVal;
+ try {
+ BitSet set = new BitSet();
+ String[] strings = parseTokens(tmpString);
+ for (int i = 0; i < strings.length; i++) {
+ int isSet = Integer.parseInt(strings[i]);
+ if (isSet == 1) {
+ set.set(i);
+ }
+ }
+ return set;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public Savable readSavable(String name, Savable defVal) throws IOException {
+ Savable ret = defVal;
+ if (name != null && name.equals(""))
+ logger.warning("Reading Savable String with name \"\"?");
+ try {
+ Element tmpEl = null;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+ } else if (isAtRoot) {
+ tmpEl = doc.getDocumentElement();
+ isAtRoot = false;
+ } else {
+ tmpEl = findFirstChildElement(currentElem);
+ }
+ currentElem = tmpEl;
+ ret = readSavableFromCurrentElem(defVal);
+ if (currentElem.getParentNode() instanceof Element) {
+ currentElem = (Element) currentElem.getParentNode();
+ } else {
+ currentElem = null;
+ }
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (Exception e) {
+ IOException io = new IOException(e.toString());
+ io.initCause(e);
+ throw io;
+ }
+ return ret;
+ }
+
+ private Savable readSavableFromCurrentElem(Savable defVal) throws
+ InstantiationException, ClassNotFoundException,
+ IOException, IllegalAccessException {
+ Savable ret = defVal;
+ Savable tmp = null;
+
+ if (currentElem == null || currentElem.getNodeName().equals("null")) {
+ return null;
+ }
+ String reference = currentElem.getAttribute("ref");
+ if (reference.length() > 0) {
+ ret = referencedSavables.get(reference);
+ } else {
+ String className = currentElem.getNodeName();
+ if (defVal != null) {
+ className = defVal.getClass().getName();
+ } else if (currentElem.hasAttribute("class")) {
+ className = currentElem.getAttribute("class");
+ }
+ tmp = SavableClassUtil.fromName(className, null);
+
+
+ String versionsStr = currentElem.getAttribute("savable_versions");
+ if (versionsStr != null && !versionsStr.equals("")){
+ String[] versionStr = versionsStr.split(",");
+ classHierarchyVersions = new int[versionStr.length];
+ for (int i = 0; i < classHierarchyVersions.length; i++){
+ classHierarchyVersions[i] = Integer.parseInt(versionStr[i].trim());
+ }
+ }else{
+ classHierarchyVersions = null;
+ }
+
+ String refID = currentElem.getAttribute("reference_ID");
+ if (refID.length() < 1) refID = currentElem.getAttribute("id");
+ if (refID.length() > 0) referencedSavables.put(refID, tmp);
+ if (tmp != null) {
+ // Allows reading versions from this savable
+ savable = tmp;
+ tmp.read(importer);
+ ret = tmp;
+ }
+ }
+ return ret;
+ }
+
+ public Savable[] readSavableArray(String name, Savable[] defVal) throws IOException {
+ Savable[] ret = defVal;
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ List<Savable> savables = new ArrayList<Savable>();
+ for (currentElem = findFirstChildElement(tmpEl);
+ currentElem != null;
+ currentElem = findNextSiblingElement(currentElem)) {
+ savables.add(readSavableFromCurrentElem(null));
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (savables.size() != requiredSize)
+ throw new IOException("Wrong number of Savables for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + savables.size());
+ }
+ ret = savables.toArray(new Savable[0]);
+ currentElem = (Element) tmpEl.getParentNode();
+ return ret;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (Exception e) {
+ IOException io = new IOException(e.toString());
+ io.initCause(e);
+ throw io;
+ }
+ }
+
+ public Savable[][] readSavableArray2D(String name, Savable[][] defVal) throws IOException {
+ Savable[][] ret = defVal;
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ int size_outer = Integer.parseInt(tmpEl.getAttribute("size_outer"));
+ int size_inner = Integer.parseInt(tmpEl.getAttribute("size_outer"));
+
+ Savable[][] tmp = new Savable[size_outer][size_inner];
+ currentElem = findFirstChildElement(tmpEl);
+ for (int i = 0; i < size_outer; i++) {
+ for (int j = 0; j < size_inner; j++) {
+ tmp[i][j] = (readSavableFromCurrentElem(null));
+ if (i == size_outer - 1 && j == size_inner - 1) {
+ break;
+ }
+ currentElem = findNextSiblingElement(currentElem);
+ }
+ }
+ ret = tmp;
+ currentElem = (Element) tmpEl.getParentNode();
+ return ret;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (Exception e) {
+ IOException io = new IOException(e.toString());
+ io.initCause(e);
+ throw io;
+ }
+ }
+
+ public ArrayList<Savable> readSavableArrayList(String name, ArrayList defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ ArrayList<Savable> savables = new ArrayList<Savable>();
+ for (currentElem = findFirstChildElement(tmpEl);
+ currentElem != null;
+ currentElem = findNextSiblingElement(currentElem)) {
+ savables.add(readSavableFromCurrentElem(null));
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (savables.size() != requiredSize)
+ throw new IOException(
+ "Wrong number of Savable arrays for '" + name
+ + "'. size says " + requiredSize
+ + ", data contains " + savables.size());
+ }
+ currentElem = (Element) tmpEl.getParentNode();
+ return savables;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (Exception e) {
+ IOException io = new IOException(e.toString());
+ io.initCause(e);
+ throw io;
+ }
+ }
+
+ public ArrayList<Savable>[] readSavableArrayListArray(
+ String name, ArrayList[] defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+ currentElem = tmpEl;
+
+ String sizeString = tmpEl.getAttribute("size");
+ int requiredSize = (sizeString.length() > 0)
+ ? Integer.parseInt(sizeString)
+ : -1;
+
+ ArrayList<Savable> sal;
+ List<ArrayList<Savable>> savableArrayLists =
+ new ArrayList<ArrayList<Savable>>();
+ int i = -1;
+ while (true) {
+ sal = readSavableArrayList("SavableArrayList_" + ++i, null);
+ if (sal == null && savableArrayLists.size() >= requiredSize)
+ break;
+ savableArrayLists.add(sal);
+ }
+
+ if (requiredSize > -1 && savableArrayLists.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + savableArrayLists.size());
+ currentElem = (Element) tmpEl.getParentNode();
+ return savableArrayLists.toArray(new ArrayList[0]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public ArrayList<Savable>[][] readSavableArrayListArray2D(String name, ArrayList[][] defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+ currentElem = tmpEl;
+ String sizeString = tmpEl.getAttribute("size");
+
+ ArrayList<Savable>[] arr;
+ List<ArrayList<Savable>[]> sall = new ArrayList<ArrayList<Savable>[]>();
+ int i = -1;
+ while ((arr = readSavableArrayListArray(
+ "SavableArrayListArray_" + ++i, null)) != null) sall.add(arr);
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (sall.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + sall.size());
+ }
+ currentElem = (Element) tmpEl.getParentNode();
+ return sall.toArray(new ArrayList[0][]);
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (Exception e) {
+ IOException io = new IOException(e.toString());
+ io.initCause(e);
+ throw io;
+ }
+ }
+
+ public ArrayList<FloatBuffer> readFloatBufferArrayList(
+ String name, ArrayList<FloatBuffer> defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ ArrayList<FloatBuffer> tmp = new ArrayList<FloatBuffer>();
+ for (currentElem = findFirstChildElement(tmpEl);
+ currentElem != null;
+ currentElem = findNextSiblingElement(currentElem)) {
+ tmp.add(readFloatBuffer(null, null));
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (tmp.size() != requiredSize)
+ throw new IOException(
+ "String array contains wrong element count. "
+ + "Specified size " + requiredSize
+ + ", data contains " + tmp.size());
+ }
+ currentElem = (Element) tmpEl.getParentNode();
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public Map<? extends Savable, ? extends Savable> readSavableMap(String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException {
+ Map<Savable, Savable> ret;
+ Element tempEl;
+
+ if (name != null) {
+ tempEl = findChildElement(currentElem, name);
+ } else {
+ tempEl = currentElem;
+ }
+ ret = new HashMap<Savable, Savable>();
+
+ NodeList nodes = tempEl.getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().equals("MapEntry")) {
+ Element elem = (Element) n;
+ currentElem = elem;
+ Savable key = readSavable(XMLExporter.ELEMENT_KEY, null);
+ Savable val = readSavable(XMLExporter.ELEMENT_VALUE, null);
+ ret.put(key, val);
+ }
+ }
+ currentElem = (Element) tempEl.getParentNode();
+ return ret;
+ }
+
+ public Map<String, ? extends Savable> readStringSavableMap(String name, Map<String, ? extends Savable> defVal) throws IOException {
+ Map<String, Savable> ret = null;
+ Element tempEl;
+
+ if (name != null) {
+ tempEl = findChildElement(currentElem, name);
+ } else {
+ tempEl = currentElem;
+ }
+ if (tempEl != null) {
+ ret = new HashMap<String, Savable>();
+
+ NodeList nodes = tempEl.getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().equals("MapEntry")) {
+ Element elem = (Element) n;
+ currentElem = elem;
+ String key = currentElem.getAttribute("key");
+ Savable val = readSavable("Savable", null);
+ ret.put(key, val);
+ }
+ }
+ } else {
+ return defVal;
+ }
+ currentElem = (Element) tempEl.getParentNode();
+ return ret;
+ }
+
+ public IntMap<? extends Savable> readIntSavableMap(String name, IntMap<? extends Savable> defVal) throws IOException {
+ IntMap<Savable> ret = null;
+ Element tempEl;
+
+ if (name != null) {
+ tempEl = findChildElement(currentElem, name);
+ } else {
+ tempEl = currentElem;
+ }
+ if (tempEl != null) {
+ ret = new IntMap<Savable>();
+
+ NodeList nodes = tempEl.getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node n = nodes.item(i);
+ if (n instanceof Element && n.getNodeName().equals("MapEntry")) {
+ Element elem = (Element) n;
+ currentElem = elem;
+ int key = Integer.parseInt(currentElem.getAttribute("key"));
+ Savable val = readSavable("Savable", null);
+ ret.put(key, val);
+ }
+ }
+ } else {
+ return defVal;
+ }
+ currentElem = (Element) tempEl.getParentNode();
+ return ret;
+ }
+
+ /**
+ * reads from currentElem if name is null
+ */
+ public FloatBuffer readFloatBuffer(String name, FloatBuffer defVal) throws IOException {
+ try {
+ Element tmpEl;
+ if (name != null) {
+ tmpEl = findChildElement(currentElem, name);
+ } else {
+ tmpEl = currentElem;
+ }
+ if (tmpEl == null) {
+ return defVal;
+ }
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of float buffers for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ FloatBuffer tmp = BufferUtils.createFloatBuffer(strings.length);
+ for (String s : strings) tmp.put(Float.parseFloat(s));
+ tmp.flip();
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public IntBuffer readIntBuffer(String name, IntBuffer defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of int buffers for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ IntBuffer tmp = BufferUtils.createIntBuffer(strings.length);
+ for (String s : strings) tmp.put(Integer.parseInt(s));
+ tmp.flip();
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public ByteBuffer readByteBuffer(String name, ByteBuffer defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of byte buffers for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ ByteBuffer tmp = BufferUtils.createByteBuffer(strings.length);
+ for (String s : strings) tmp.put(Byte.valueOf(s));
+ tmp.flip();
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public ShortBuffer readShortBuffer(String name, ShortBuffer defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ String[] strings = parseTokens(tmpEl.getAttribute("data"));
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (strings.length != requiredSize)
+ throw new IOException("Wrong number of short buffers for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + strings.length);
+ }
+ ShortBuffer tmp = BufferUtils.createShortBuffer(strings.length);
+ for (String s : strings) tmp.put(Short.valueOf(s));
+ tmp.flip();
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public ArrayList<ByteBuffer> readByteBufferArrayList(String name, ArrayList<ByteBuffer> defVal) throws IOException {
+ try {
+ Element tmpEl = findChildElement(currentElem, name);
+ if (tmpEl == null) {
+ return defVal;
+ }
+
+ String sizeString = tmpEl.getAttribute("size");
+ ArrayList<ByteBuffer> tmp = new ArrayList<ByteBuffer>();
+ for (currentElem = findFirstChildElement(tmpEl);
+ currentElem != null;
+ currentElem = findNextSiblingElement(currentElem)) {
+ tmp.add(readByteBuffer(null, null));
+ }
+ if (sizeString.length() > 0) {
+ int requiredSize = Integer.parseInt(sizeString);
+ if (tmp.size() != requiredSize)
+ throw new IOException("Wrong number of short buffers for '"
+ + name + "'. size says " + requiredSize
+ + ", data contains " + tmp.size());
+ }
+ currentElem = (Element) tmpEl.getParentNode();
+ return tmp;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (NumberFormatException nfe) {
+ IOException io = new IOException(nfe.toString());
+ io.initCause(nfe);
+ throw io;
+ } catch (DOMException de) {
+ IOException io = new IOException(de.toString());
+ io.initCause(de);
+ throw io;
+ }
+ }
+
+ public <T extends Enum<T>> T readEnum(String name, Class<T> enumType,
+ T defVal) throws IOException {
+ T ret = defVal;
+ try {
+ String eVal = currentElem.getAttribute(name);
+ if (eVal != null && eVal.length() > 0) {
+ ret = Enum.valueOf(enumType, eVal);
+ }
+ } catch (Exception e) {
+ IOException io = new IOException(e.toString());
+ io.initCause(e);
+ throw io;
+ }
+ return ret;
+ }
+
+ private static final String[] zeroStrings = new String[0];
+
+ protected String[] parseTokens(String inString) {
+ String[] outStrings = inString.split("\\s+");
+ return (outStrings.length == 1 && outStrings[0].length() == 0)
+ ? zeroStrings
+ : outStrings;
+ }
+} \ No newline at end of file
diff --git a/engine/src/xml/com/jme3/export/xml/DOMOutputCapsule.java b/engine/src/xml/com/jme3/export/xml/DOMOutputCapsule.java
new file mode 100644
index 0000000..2baef1f
--- /dev/null
+++ b/engine/src/xml/com/jme3/export/xml/DOMOutputCapsule.java
@@ -0,0 +1,825 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.xml;
+
+import com.jme3.export.FormatVersion;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.export.SavableClassUtil;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.*;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Part of the jME XML IO system as introduced in the google code jmexml project.
+ *
+ * @author Kai Rabien (hevee) - original author of the code.google.com jmexml project
+ * @author Doug Daniels (dougnukem) - adjustments for jME 2.0 and Java 1.5
+ */
+public class DOMOutputCapsule implements OutputCapsule {
+
+ private static final String dataAttributeName = "data";
+ private Document doc;
+ private Element currentElement;
+ private JmeExporter exporter;
+ private Map<Savable, Element> writtenSavables = new IdentityHashMap<Savable, Element>();
+
+ public DOMOutputCapsule(Document doc, JmeExporter exporter) {
+ this.doc = doc;
+ this.exporter = exporter;
+ currentElement = null;
+ }
+
+ public Document getDoc() {
+ return doc;
+ }
+
+ /**
+ * appends a new Element with the given name to currentElement, sets
+ * currentElement to be new Element, and returns the new Element as well
+ */
+ private Element appendElement(String name) {
+ Element ret = doc.createElement(name);
+ if (currentElement == null) {
+ ret.setAttribute("format_version", Integer.toString(FormatVersion.VERSION));
+ doc.appendChild(ret);
+ } else {
+ currentElement.appendChild(ret);
+ }
+ currentElement = ret;
+ return ret;
+ }
+
+ private static String encodeString(String s) {
+ if (s == null) {
+ return null;
+ }
+ s = s.replaceAll("\\&", "&amp;")
+ .replaceAll("\\\"", "&quot;")
+ .replaceAll("\\<", "&lt;");
+ return s;
+ }
+
+ public void write(byte value, String name, byte defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(byte[] value, String name, byte[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ for (byte b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(buf.length() - 1);
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(byte[][] value, String name, byte[][] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ for (byte[] bs : value) {
+ for (byte b : bs) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ buf.append(" ");
+ }
+ //remove last spaces
+ buf.setLength(buf.length() - 2);
+
+ Element el = appendElement(name);
+ el.setAttribute("size_outer", String.valueOf(value.length));
+ el.setAttribute("size_inner", String.valueOf(value[0].length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(int value, String name, int defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(int[] value, String name, int[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) { return; }
+ if (Arrays.equals(value, defVal)) { return; }
+
+ for (int b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(Math.max(0, buf.length() - 1));
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(int[][] value, String name, int[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ int[] array = value[i];
+ write(array, "array_"+i, defVal==null?null:defVal[i]);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(float value, String name, float defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(float[] value, String name, float[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ if (value != null) {
+ for (float b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(buf.length() - 1);
+ }
+
+ Element el = appendElement(name);
+ el.setAttribute("size", value == null ? "0" : String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(float[][] value, String name, float[][] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ for (float[] bs : value) {
+ for(float b : bs){
+ buf.append(b);
+ buf.append(" ");
+ }
+ }
+ //remove last space
+ buf.setLength(buf.length() - 1);
+
+ Element el = appendElement(name);
+ el.setAttribute("size_outer", String.valueOf(value.length));
+ el.setAttribute("size_inner", String.valueOf(value[0].length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(double value, String name, double defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(double[] value, String name, double[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ for (double b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(buf.length() - 1);
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(double[][] value, String name, double[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ double[] array = value[i];
+ write(array, "array_"+i, defVal==null?null:defVal[i]);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(long value, String name, long defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(long[] value, String name, long[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ for (long b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(buf.length() - 1);
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(long[][] value, String name, long[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ long[] array = value[i];
+ write(array, "array_"+i, defVal==null?null:defVal[i]);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(short value, String name, short defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(short[] value, String name, short[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ for (short b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(buf.length() - 1);
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(short[][] value, String name, short[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ short[] array = value[i];
+ write(array, "array_"+i, defVal==null?null:defVal[i]);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(boolean value, String name, boolean defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+ }
+
+ public void write(boolean[] value, String name, boolean[] defVal) throws IOException {
+ StringBuilder buf = new StringBuilder();
+ if (value == null) {
+ value = defVal;
+ }
+ for (boolean b : value) {
+ buf.append(b);
+ buf.append(" ");
+ }
+ //remove last space
+ buf.setLength(Math.max(0, buf.length() - 1));
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(boolean[][] value, String name, boolean[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ boolean[] array = value[i];
+ write(array, "array_"+i, defVal==null?null:defVal[i]);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(String value, String name, String defVal) throws IOException {
+ if (value == null || value.equals(defVal)) {
+ return;
+ }
+ currentElement.setAttribute(name, encodeString(value));
+ }
+
+ public void write(String[] value, String name, String[] defVal) throws IOException {
+ Element el = appendElement(name);
+
+ if (value == null) {
+ value = defVal;
+ }
+
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ String b = value[i];
+ appendElement("String_"+i);
+ String val = encodeString(b);
+ currentElement.setAttribute("value", val);
+ currentElement = el;
+ }
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void write(String[][] value, String name, String[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.length));
+
+ for (int i=0; i<value.length; i++) {
+ String[] array = value[i];
+ write(array, "array_"+i, defVal==null?null:defVal[i]);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(BitSet value, String name, BitSet defVal) throws IOException {
+ if (value == null || value.equals(defVal)) {
+ return;
+ }
+ StringBuilder buf = new StringBuilder();
+ for (int i = value.nextSetBit(0); i >= 0; i = value.nextSetBit(i + 1)) {
+ buf.append(i);
+ buf.append(" ");
+ }
+ buf.setLength(Math.max(0, buf.length() - 1));
+ currentElement.setAttribute(name, buf.toString());
+
+ }
+
+ public void write(Savable object, String name, Savable defVal) throws IOException {
+ if (object == null) {
+ return;
+ }
+ if (object.equals(defVal)) {
+ return;
+ }
+
+ Element old = currentElement;
+ Element el = writtenSavables.get(object);
+
+ String className = null;
+ if(!object.getClass().getName().equals(name)){
+ className = object.getClass().getName();
+ }
+ try {
+ doc.createElement(name);
+ } catch (DOMException e) {
+ // Ridiculous fallback behavior.
+ // Would be far better to throw than to totally disregard the
+ // specified "name" and write a class name instead!
+ // (Besides the fact we are clobbering the managed .getClassTag()).
+ name = "Object";
+ className = object.getClass().getName();
+ }
+
+ if (el != null) {
+ String refID = el.getAttribute("reference_ID");
+ if (refID.length() == 0) {
+ refID = object.getClass().getName() + "@" + object.hashCode();
+ el.setAttribute("reference_ID", refID);
+ }
+ el = appendElement(name);
+ el.setAttribute("ref", refID);
+ } else {
+ el = appendElement(name);
+
+ // jME3 NEW: Append version number(s)
+ int[] versions = SavableClassUtil.getSavableVersions(object.getClass());
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < versions.length; i++){
+ sb.append(versions[i]);
+ if (i != versions.length - 1){
+ sb.append(", ");
+ }
+ }
+ el.setAttribute("savable_versions", sb.toString());
+
+ writtenSavables.put(object, el);
+ object.write(exporter);
+ }
+ if(className != null){
+ el.setAttribute("class", className);
+ }
+
+ currentElement = old;
+ }
+
+ public void write(Savable[] objects, String name, Savable[] defVal) throws IOException {
+ if (objects == null) {
+ return;
+ }
+ if (Arrays.equals(objects, defVal)) {
+ return;
+ }
+
+ Element old = currentElement;
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(objects.length));
+ for (int i = 0; i < objects.length; i++) {
+ Savable o = objects[i];
+ if(o == null){
+ //renderStateList has special loading code, so we can leave out the null values
+ if(!name.equals("renderStateList")){
+ Element before = currentElement;
+ appendElement("null");
+ currentElement = before;
+ }
+ }else{
+ write(o, o.getClass().getName(), null);
+ }
+ }
+ currentElement = old;
+ }
+
+ public void write(Savable[][] value, String name, Savable[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size_outer", String.valueOf(value.length));
+ el.setAttribute("size_inner", String.valueOf(value[0].length));
+ for (Savable[] bs : value) {
+ for(Savable b : bs){
+ write(b, b.getClass().getSimpleName(), null);
+ }
+ }
+ currentElement = (Element) currentElement.getParentNode();
+ }
+
+ public void writeSavableArrayList(ArrayList array, String name, ArrayList defVal) throws IOException {
+ if (array == null) {
+ return;
+ }
+ if (array.equals(defVal)) {
+ return;
+ }
+ Element old = currentElement;
+ Element el = appendElement(name);
+ currentElement = el;
+ el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(array.size()));
+ for (Object o : array) {
+ if(o == null) {
+ continue;
+ }
+ else if (o instanceof Savable) {
+ Savable s = (Savable) o;
+ write(s, s.getClass().getName(), null);
+ } else {
+ throw new ClassCastException("Not a Savable instance: " + o);
+ }
+ }
+ currentElement = old;
+ }
+
+ public void writeSavableArrayListArray(ArrayList[] objects, String name, ArrayList[] defVal) throws IOException {
+ if (objects == null) {return;}
+ if (Arrays.equals(objects, defVal)) {return;}
+
+ Element old = currentElement;
+ Element el = appendElement(name);
+ el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(objects.length));
+ for (int i = 0; i < objects.length; i++) {
+ ArrayList o = objects[i];
+ if(o == null){
+ Element before = currentElement;
+ appendElement("null");
+ currentElement = before;
+ }else{
+ StringBuilder buf = new StringBuilder("SavableArrayList_");
+ buf.append(i);
+ writeSavableArrayList(o, buf.toString(), null);
+ }
+ }
+ currentElement = old;
+ }
+
+ public void writeSavableArrayListArray2D(ArrayList[][] value, String name, ArrayList[][] defVal) throws IOException {
+ if (value == null) return;
+ if(Arrays.deepEquals(value, defVal)) return;
+
+ Element el = appendElement(name);
+ int size = value.length;
+ el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(size));
+
+ for (int i=0; i< size; i++) {
+ ArrayList[] vi = value[i];
+ writeSavableArrayListArray(vi, "SavableArrayListArray_"+i, null);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array, String name, ArrayList<FloatBuffer> defVal) throws IOException {
+ if (array == null) {
+ return;
+ }
+ if (array.equals(defVal)) {
+ return;
+ }
+ Element el = appendElement(name);
+ el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(array.size()));
+ for (FloatBuffer o : array) {
+ write(o, XMLExporter.ELEMENT_FLOATBUFFER, null);
+ }
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void writeSavableMap(Map<? extends Savable, ? extends Savable> map, String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException {
+ if (map == null) {
+ return;
+ }
+ if (map.equals(defVal)) {
+ return;
+ }
+ Element stringMap = appendElement(name);
+
+ Iterator<? extends Savable> keyIterator = map.keySet().iterator();
+ while(keyIterator.hasNext()) {
+ Savable key = keyIterator.next();
+ Element mapEntry = appendElement(XMLExporter.ELEMENT_MAPENTRY);
+ write(key, XMLExporter.ELEMENT_KEY, null);
+ Savable value = map.get(key);
+ write(value, XMLExporter.ELEMENT_VALUE, null);
+ currentElement = stringMap;
+ }
+
+ currentElement = (Element) stringMap.getParentNode();
+ }
+
+ public void writeStringSavableMap(Map<String, ? extends Savable> map, String name, Map<String, ? extends Savable> defVal) throws IOException {
+ if (map == null) {
+ return;
+ }
+ if (map.equals(defVal)) {
+ return;
+ }
+ Element stringMap = appendElement(name);
+
+ Iterator<String> keyIterator = map.keySet().iterator();
+ while(keyIterator.hasNext()) {
+ String key = keyIterator.next();
+ Element mapEntry = appendElement("MapEntry");
+ mapEntry.setAttribute("key", key);
+ Savable s = map.get(key);
+ write(s, "Savable", null);
+ currentElement = stringMap;
+ }
+
+ currentElement = (Element) stringMap.getParentNode();
+ }
+
+ public void writeIntSavableMap(IntMap<? extends Savable> map, String name, IntMap<? extends Savable> defVal) throws IOException {
+ if (map == null) {
+ return;
+ }
+ if (map.equals(defVal)) {
+ return;
+ }
+ Element stringMap = appendElement(name);
+
+ for(Entry<? extends Savable> entry : map) {
+ int key = entry.getKey();
+ Element mapEntry = appendElement("MapEntry");
+ mapEntry.setAttribute("key", Integer.toString(key));
+ Savable s = entry.getValue();
+ write(s, "Savable", null);
+ currentElement = stringMap;
+ }
+
+ currentElement = (Element) stringMap.getParentNode();
+ }
+
+ public void write(FloatBuffer value, String name, FloatBuffer defVal) throws IOException {
+ if (value == null) {
+ return;
+ }
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.limit()));
+ StringBuilder buf = new StringBuilder();
+ int pos = value.position();
+ value.rewind();
+ int ctr = 0;
+ while (value.hasRemaining()) {
+ ctr++;
+ buf.append(value.get());
+ buf.append(" ");
+ }
+ if (ctr != value.limit())
+ throw new IOException("'" + name
+ + "' buffer contention resulted in write data consistency. "
+ + ctr + " values written when should have written "
+ + value.limit());
+ buf.setLength(Math.max(0, buf.length() - 1));
+ value.position(pos);
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(IntBuffer value, String name, IntBuffer defVal) throws IOException {
+ if (value == null) {
+ return;
+ }
+ if (value.equals(defVal)) {
+ return;
+ }
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.limit()));
+ StringBuilder buf = new StringBuilder();
+ int pos = value.position();
+ value.rewind();
+ int ctr = 0;
+ while (value.hasRemaining()) {
+ ctr++;
+ buf.append(value.get());
+ buf.append(" ");
+ }
+ if (ctr != value.limit())
+ throw new IOException("'" + name
+ + "' buffer contention resulted in write data consistency. "
+ + ctr + " values written when should have written "
+ + value.limit());
+ buf.setLength(buf.length() - 1);
+ value.position(pos);
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(ByteBuffer value, String name, ByteBuffer defVal) throws IOException {
+ if (value == null) return;
+ if (value.equals(defVal)) return;
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.limit()));
+ StringBuilder buf = new StringBuilder();
+ int pos = value.position();
+ value.rewind();
+ int ctr = 0;
+ while (value.hasRemaining()) {
+ ctr++;
+ buf.append(value.get());
+ buf.append(" ");
+ }
+ if (ctr != value.limit())
+ throw new IOException("'" + name
+ + "' buffer contention resulted in write data consistency. "
+ + ctr + " values written when should have written "
+ + value.limit());
+ buf.setLength(buf.length() - 1);
+ value.position(pos);
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(ShortBuffer value, String name, ShortBuffer defVal) throws IOException {
+ if (value == null) {
+ return;
+ }
+ if (value.equals(defVal)) {
+ return;
+ }
+
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(value.limit()));
+ StringBuilder buf = new StringBuilder();
+ int pos = value.position();
+ value.rewind();
+ int ctr = 0;
+ while (value.hasRemaining()) {
+ ctr++;
+ buf.append(value.get());
+ buf.append(" ");
+ }
+ if (ctr != value.limit())
+ throw new IOException("'" + name
+ + "' buffer contention resulted in write data consistency. "
+ + ctr + " values written when should have written "
+ + value.limit());
+ buf.setLength(buf.length() - 1);
+ value.position(pos);
+ el.setAttribute(dataAttributeName, buf.toString());
+ currentElement = (Element) el.getParentNode();
+ }
+
+ public void write(Enum value, String name, Enum defVal) throws IOException {
+ if (value == defVal) {
+ return;
+ }
+ currentElement.setAttribute(name, String.valueOf(value));
+
+ }
+
+ public void writeByteBufferArrayList(ArrayList<ByteBuffer> array,
+ String name, ArrayList<ByteBuffer> defVal) throws IOException {
+ if (array == null) {
+ return;
+ }
+ if (array.equals(defVal)) {
+ return;
+ }
+ Element el = appendElement(name);
+ el.setAttribute("size", String.valueOf(array.size()));
+ for (ByteBuffer o : array) {
+ write(o, "ByteBuffer", null);
+ }
+ currentElement = (Element) el.getParentNode();
+
+ }
+} \ No newline at end of file
diff --git a/engine/src/xml/com/jme3/export/xml/DOMSerializer.java b/engine/src/xml/com/jme3/export/xml/DOMSerializer.java
new file mode 100644
index 0000000..b30ba19
--- /dev/null
+++ b/engine/src/xml/com/jme3/export/xml/DOMSerializer.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.xml;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import org.w3c.dom.*;
+
+/**
+ * The DOMSerializer was based primarily off the DOMSerializer.java class from the
+ * "Java and XML" 3rd Edition book by Brett McLaughlin, and Justin Edelson. Some
+ * modifications were made to support formatting of elements and attributes.
+ *
+ * @author Brett McLaughlin, Justin Edelson - Original creation for "Java and XML" book.
+ * @author Doug Daniels (dougnukem) - adjustments for XML formatting
+ * @version $Revision: 4207 $, $Date: 2009-03-29 11:19:16 -0400 (Sun, 29 Mar 2009) $
+ */
+public class DOMSerializer {
+
+ /** The encoding to use for output (default is UTF-8) */
+ private Charset encoding = Charset.forName("utf-8");
+
+ /** The amount of indentation to use (default is 4 spaces). */
+ private int indent = 4;
+
+ /** The line separator to use (default is the based on the current system settings). */
+ private String lineSeparator = System.getProperty("line.separator", "\n");
+
+ private void escape(Writer writer, String s) throws IOException {
+ if (s == null) { return; }
+ for (int i = 0, len = s.length(); i < len; i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '<':
+ writer.write("&lt;");
+ break;
+ case '>':
+ writer.write("&gt;");
+ break;
+ case '&':
+ writer.write("&amp;");
+ break;
+ case '\r':
+ writer.write("&#xD;");
+ break;
+ default:
+ writer.write(c);
+ }
+ }
+ }
+
+ /**
+ * Serialize {@code doc} to {@code out}
+ *
+ * @param doc the document to serialize.
+ * @param file the file to serialize to.
+ * @throws IOException
+ */
+ public void serialize(Document doc, File file) throws IOException {
+ serialize(doc, new FileOutputStream(file));
+ }
+
+ /**
+ * Serialize {@code doc} to {@code out}
+ *
+ * @param doc the document to serialize.
+ * @param out the stream to serialize to.
+ * @throws IOException
+ */
+ public void serialize(Document doc, OutputStream out) throws IOException {
+ Writer writer = new OutputStreamWriter(out, encoding);
+ write(doc, writer, 0);
+ writer.flush();
+ }
+
+ /**
+ * Set the encoding used by this serializer.
+ *
+ * @param encoding the encoding to use, passing in {@code null} results in the
+ * default encoding (UTF-8) being set.
+ * @throws IllegalCharsetNameException if the given charset name is illegal.
+ * @throws UnsupportedCharsetException if the given charset is not supported by the
+ * current JVM.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = Charset.forName(encoding);
+ }
+
+ /**
+ * Set the number of spaces to use for indentation.
+ * <p>
+ * The default is to use 4 spaces.
+ *
+ * @param indent the number of spaces to use for indentation, values less than or
+ * equal to zero result in no indentation being used.
+ */
+ public void setIndent(int indent) {
+ this.indent = indent >= 0 ? indent : 0;
+ }
+
+ /**
+ * Set the line separator that will be used when serializing documents.
+ * <p>
+ * If this is not called then the serializer uses a default based on the
+ * {@code line.separator} system property.
+ *
+ * @param lineSeparator the line separator to set.
+ */
+ public void setLineSeparator(String lineSeparator) {
+ this.lineSeparator = lineSeparator;
+ }
+
+ private void write(Node node, Writer writer, int depth) throws IOException {
+ switch (node.getNodeType()) {
+ case Node.DOCUMENT_NODE:
+ writeDocument((Document) node, writer);
+ break;
+ case Node.ELEMENT_NODE:
+ writeElement((Element) node, writer, depth);
+ break;
+ case Node.TEXT_NODE:
+ escape(writer, node.getNodeValue());
+ break;
+ case Node.CDATA_SECTION_NODE:
+ writer.write("<![CDATA[");
+ escape(writer, node.getNodeValue());
+ writer.write("]]>");
+ break;
+ case Node.COMMENT_NODE:
+ for (int i = 0; i < depth; ++i) { writer.append(' '); }
+ writer.append("<!-- ").append(node.getNodeValue()).append(" -->").append(lineSeparator);
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ String n = node.getNodeName();
+ String v = node.getNodeValue();
+ for (int i = 0; i < depth; ++i) { writer.append(' '); }
+ writer.append("<?").append(n).append(' ').append(v).append("?>").append(lineSeparator);
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ writer.append('&').append(node.getNodeName()).append(';');
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ writeDocumentType((DocumentType) node, writer, depth);
+ break;
+ }
+ }
+
+ private void writeDocument(Document document, Writer writer) throws IOException {
+ String v = document.getXmlVersion();
+
+ writer.append("<?xml ");
+ writer.append(" version='").append(v == null ? "1.0" : v).append("'");
+ writer.append(" encoding='").append(encoding.name()).append("'");
+ if (document.getXmlStandalone()) {
+ writer.append(" standalone='yes'");
+ }
+ writer.append("?>").append(lineSeparator);
+
+ NodeList nodes = document.getChildNodes();
+ for (int i = 0, imax = nodes.getLength(); i < imax; ++i) {
+ write(nodes.item(i), writer, 0);
+ }
+ }
+
+ private void writeDocumentType(DocumentType docType, Writer writer, int depth) throws IOException {
+ String publicId = docType.getPublicId();
+ String internalSubset = docType.getInternalSubset();
+
+ for (int i = 0; i < depth; ++i) { writer.append(' '); }
+ writer.append("<!DOCTYPE ").append(docType.getName());
+ if (publicId != null) {
+ writer.append(" PUBLIC '").append(publicId).append("' ");
+ } else {
+ writer.write(" SYSTEM ");
+ }
+ writer.append("'").append(docType.getSystemId()).append("'");
+ if (internalSubset != null) {
+ writer.append(" [").append(internalSubset).append("]");
+ }
+ writer.append('>').append(lineSeparator);
+ }
+
+ private void writeElement(Element element, Writer writer, int depth) throws IOException {
+ for (int i = 0; i < depth; ++i) { writer.append(' '); }
+ writer.append('<').append(element.getTagName());
+ NamedNodeMap attrs = element.getAttributes();
+ for (int i = 0, imax = attrs.getLength(); i < imax; ++i) {
+ Attr attr = (Attr) attrs.item(i);
+ writer.append(' ').append(attr.getName()).append("='").append(attr.getValue()).append("'");
+ }
+ NodeList nodes = element.getChildNodes();
+ if (nodes.getLength() == 0) {
+ // no children, so just close off the element and return
+ writer.append("/>").append(lineSeparator);
+ return;
+ }
+ writer.append('>').append(lineSeparator);
+ for (int i = 0, imax = nodes.getLength(); i < imax; ++i) {
+ Node n = nodes.item(i);
+ if (n.getNodeType() == Node.ATTRIBUTE_NODE) { continue; }
+ write(n, writer, depth + indent);
+ }
+ for (int i = 0; i < depth; ++i) { writer.append(' '); }
+ writer.append("</").append(element.getTagName()).append('>').append(lineSeparator);
+ }
+
+}
diff --git a/engine/src/xml/com/jme3/export/xml/XMLExporter.java b/engine/src/xml/com/jme3/export/xml/XMLExporter.java
new file mode 100644
index 0000000..6e7c395
--- /dev/null
+++ b/engine/src/xml/com/jme3/export/xml/XMLExporter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.xml;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Part of the jME XML IO system as introduced in the google code jmexml project.
+ *
+ * @author Kai Rabien (hevee) - original author of the code.google.com jmexml project
+ * @author Doug Daniels (dougnukem) - adjustments for jME 2.0 and Java 1.5
+ */
+public class XMLExporter implements JmeExporter {
+
+ public static final String ELEMENT_MAPENTRY = "MapEntry";
+ public static final String ELEMENT_KEY = "Key";
+ public static final String ELEMENT_VALUE = "Value";
+ public static final String ELEMENT_FLOATBUFFER = "FloatBuffer";
+ public static final String ATTRIBUTE_SIZE = "size";
+
+ private DOMOutputCapsule domOut;
+
+ public XMLExporter() {
+
+ }
+
+ public boolean save(Savable object, OutputStream f) throws IOException {
+ try {
+ //Initialize Document when saving so we don't retain state of previous exports
+ this.domOut = new DOMOutputCapsule(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(), this);
+ domOut.write(object, object.getClass().getName(), null);
+ DOMSerializer serializer = new DOMSerializer();
+ serializer.serialize(domOut.getDoc(), f);
+ f.flush();
+ return true;
+ } catch (Exception ex) {
+ IOException e = new IOException();
+ e.initCause(ex);
+ throw e;
+ }
+ }
+
+ public boolean save(Savable object, File f) throws IOException {
+ return save(object, new FileOutputStream(f));
+ }
+
+ public OutputCapsule getCapsule(Savable object) {
+ return domOut;
+ }
+
+ public static XMLExporter getInstance() {
+ return new XMLExporter();
+ }
+
+}
diff --git a/engine/src/xml/com/jme3/export/xml/XMLImporter.java b/engine/src/xml/com/jme3/export/xml/XMLImporter.java
new file mode 100644
index 0000000..7a46b17
--- /dev/null
+++ b/engine/src/xml/com/jme3/export/xml/XMLImporter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.xml;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.xml.sax.SAXException;
+
+/**
+ * Part of the jME XML IO system as introduced in the google code jmexml project.
+ * @author Kai Rabien (hevee) - original author of the code.google.com jmexml project
+ * @author Doug Daniels (dougnukem) - adjustments for jME 2.0 and Java 1.5
+ */
+public class XMLImporter implements JmeImporter {
+
+ private AssetManager assetManager;
+ private DOMInputCapsule domIn;
+ int formatVersion = 0;
+
+ public XMLImporter() {
+ }
+
+ public int getFormatVersion() {
+ return formatVersion;
+ }
+
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ public void setAssetManager(AssetManager assetManager){
+ this.assetManager = assetManager;
+ }
+
+ public Object load(AssetInfo info) throws IOException{
+ assetManager = info.getManager();
+ InputStream in = info.openStream();
+ Savable obj = load(in);
+ in.close();
+ return obj;
+ }
+
+ public Savable load(File f) throws IOException {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(f);
+ Savable sav = load(fis);
+ return sav;
+ } finally {
+ if (fis != null) fis.close();
+ }
+ }
+
+ public Savable load(InputStream f) throws IOException {
+ try {
+ domIn = new DOMInputCapsule(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f), this);
+ return domIn.readSavable(null, null);
+ } catch (SAXException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (ParserConfigurationException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public InputCapsule getCapsule(Savable id) {
+ return domIn;
+ }
+
+ public static XMLImporter getInstance() {
+ return new XMLImporter();
+ }
+
+}